Links List with Favicons and Under the QuickLaunch

Applies To: SharePoint

SharePoint has a handy list called Links that makes putting together a list of links with a display name pretty simple. Since it’s a normal list you can use views or even XSLT to make it look nice wherever you display it on the page. By default, here’s what a small links list looks like using the Summary View:

It’s not too bad, especially for a simple team site. But with just a little extra work you can have that same list of links display with their favicons and you can move them to some relatively unused real estate – under the QuickLaunch, and on every page in your site.

I’m combining these techniques because that was what I did. Fortunately, you can use the bulk of my tips to get nearly any web part to show up below the QuickLaunch. You can also just use the Favicon information to make your link display snazzy. Also, although I’m demonstrating all of this in SharePoint 2010, you should be able to do everything in SharePoint 2007 as well.

Displaying a Web Part Beneath the QuickLaunch

In order to place a Web Part below the QuickLaunch, you’re going to have to edit the Master Page. There are a couple of options. You can add a Web Part Zone and then customize this area on a page by page basis, or you can do what I’m going to demonstrate: add a specific web part to every page on your site.

Open your site in SharePoint Designer (Site Actions -> Edit in SharePoint Designer). Choose Master Pages in the Navigation pane and right-click on v4.master and choose Copy then right-click and choose Paste. Right-click on the new Master Page, v4_copy(1).master, and choose Rename. Once you’ve renamed it, right-click on it and select Edit File in Advanced Mode:

Depending on your site’s settings, you might have to check it out. If so, make sure you check it back in when done and verify you’ve published a major version so that those without full control can see your changes.

We’re going to place our web part right below the quicklaunch. So scroll down to approximately line 594 (in Code view) where you should see two closing divs shortly below the PlaceHolderQuickLaunchBottomV4 UIVersionedContent control. If you want your web part to be included in the leftpanel then press enter after the closing div in line 592, if you want it placed below the box press enter after the closing div in line 594:

Type <br /> and press enter again. Press Save. You’ll get a warning about customizing the page, go ahead and click Yes.

Now switch to the Insert ribbon and select Web Part > Content Query:

Switch to the Design view and right-click on your new web part and choose Web Part Properties. In the dialog window expand the Query section. Choose Show items from the following list under Source and click Browse… and choose your Links list.

Expand the Presentation section. Set Sort items by to <None> (This is to ensure the custom ordering allowed by Links lists is used). Uncheck the Limit the number of items to display checkbox.

In the Fields to display section enter Url [Custom Columns]; for the Link and remove the Title entry:

Choose any other display options you want (I expanded Apperance and chose Chrome Type: None). Press OK to close the dialog. Save the master page. In the navigation pane on the left, right-click on your master page and choose Set as Default Master Page:

Now when you refresh your site you should see the changes (Be sure to publish a major version and/or check in the file if required to ensure everyone can see it):

Adding Favicons to the Links

The above screenshot is pretty cool. Unfortunately, instead of using the display text, it just uses the link. It also doesn’t open the links in a new window. We’ll fix these issues and add a favicon using some simple XSL.

I found the basic XSL to fix the Links display on Marc D Anderson’s blog who apparently got it from this Microsoft forum thread. We’re going to straight up copy that XSL and tweak it just a little to add our favicons. Here’s our customized XSL:

<xsl:template name="LinkList" match="Row[@Style='LinkList']" mode="itemstyle">
	<xsl:variable name="SafeLinkUrl">
		<xsl:call-template name="OuterTemplate.GetSafeLink">
			<xsl:with-param name="UrlColumnName" select="@URL"/>
		</xsl:call-template>
	</xsl:variable>
	<xsl:variable name="DisplayTitle">
		<xsl:call-template name="OuterTemplate.GetTitle">
			<xsl:with-param name="Title" select="@URL"/>
			<xsl:with-param name="UrlColumnName" select="'LinkUrl'"/>
		</xsl:call-template>
	</xsl:variable>
	<xsl:variable name="TheLink">
		<xsl:value-of select="substring-before($DisplayTitle,',')"/>
	</xsl:variable>
	<div id="linkitem" class="item link-item" style="padding-left:10px;">
		<xsl:call-template name="OuterTemplate.CallPresenceStatusIconTemplate"/>
		<img src="http://www.google.com/s2/favicons?domain_url={$TheLink}" align="middle" style="padding-right:2px;" />
		<a href="{$TheLink}" target="_blank" title="This link opens in a new window">
			<xsl:value-of select="substring-after($DisplayTitle,',')"/>
		</a>
	</div>
</xsl:template>

The main changes I made were the additional padding added to the div in line 16 to get everything to line up with the QuickLaunch links and the img element in line 18.

The img element uses a special link from Google (found on the Coding Clues blog) concatenated with our link’s URL. This link allows us to dynamically retrieve the favicons without having to store them within SharePoint or maintain them as links get added or changed.

So where do we put the above XSL? In your site collection’s Style Library there is a folder called XSL Style Sheets. Open the ItemStyle.xsl file and scroll all the way to the bottom. Just before the final node, </xsl:stylesheet>, paste the above XSL. Since this is just a named template, this won’t affect anything else within your site collection. Upload the changed ItemStyle to the XSL Style Sheets folder and make sure to Publish a major version of the file so everyone can see it:

Now we need to tell our Links Content Query web part to use this item style. So, back in SharePoint Designer, right-click on your Content Query web part and choose Properties. Scroll down to ItemStyle and change it from Default to LinkList:

Save the master page and refresh your site and you should see something similar to this:

Isn’t that pretty!? Now everyone loves you!

Minimal.Master missing corev4.css

Applies To: SharePoint 2010

While testing our new master page that replaces the minimal.master I noticed that depending on your permission level, things were rendering differently. If you had full permissions then things looked great, but if you were a visitor with read-only permissions things were a little off.

Taking a look at the actual page source we found that the stylesheet links in the header section looked different. With Full permissions the link to corev4.css existed, but didn’t for anyone else. Obviously permission to the corev4.css file wasn’t the issue since this was working fine on all other sites regardless of user permissions.

Some quick searching and experimentation revealed that the corev4.css link was only added to the page when the Site Actions menu was set to render. For the standard minimal.master file this is fine, but if you are depending on any of the corev4.css styles, you’ve got a problem. Fortunately, the solution is simple.

In your custom minimal.master master page find this section in the head element:

 <SharePoint:CssLink runat="server" Alternate="true"/>
 <SharePoint:Theme runat="server"/>
 <SharePoint:CssRegistration Name="minimalv4.css" runat="server"/>
 <SharePoint:CssRegistration Name="layouts.css" runat="server"/>

And add a CssRegistration element to corev4.css above the minimalv4.css CssRegistration:

 <SharePoint:CssLink runat="server" Alternate="true"/>
 <SharePoint:Theme runat="server"/>
 <SharePoint:CssRegistration Name="corev4.css" runat="server"/>
 <SharePoint:CssRegistration Name="minimalv4.css" runat="server"/>
 <SharePoint:CssRegistration Name="layouts.css" runat="server"/>

That’ll do it. The corev4.css will load regardless of the presence of the Site Actions menu and there aren’t any conflicts since SharePoint ensures it isn’t registered twice.

Auto Publish and Approve Your Solution Files

Applies To: SharePoint 2010

By default, every file you deploy using a sandboxed solution is left checked out. This can lead to problems depending on the type of site you are deploying to and/or the permissions of your end users.

This post focuses on Branding solutions, but anytime you are deploying a sandboxed solution these techniques should help you. This is especially important for Master Pages since these often need an approved/published version in order to be visible to anyone but the site administrators.

I found an interesting approach by Waldek Mastykarz where he suggests using a “Stamp” (Feature ID property) on each file and using that to find and check in each file. This was very cool, but requires you to modify the Elements.xml entry for each file to ensure that property is added and he also never addressed Master Pages which can be a bit of a special case.

For my needs, I generally take a simpler approach of just deploying my resources to one root folder and creating sub folders as needed. This makes it easier to find stuff, but it also means I don’t need to track each file individually. Obviously if you are doing something a little more extensive then you may need to take a hybrid approach of using the featureid property and/or just tracking the various folders you are deploying too. But for a simple branding solution you really just need to:

  1. Apply your branding to each site
  2. Publish and Approve each resource file
  3. Publish and Approve each Master Page

All of this can be done in the FeatureActivating event with a simpler helper method:

    Public Overrides Sub FeatureActivated(ByVal properties As SPFeatureReceiverProperties)
        Dim siteCollection As SPSite = CType(properties.Feature.Parent, SPSite)
        If siteCollection IsNot Nothing Then
            Dim topSite As SPWeb = siteCollection.RootWeb

            'Calculate relative path to site from Web Application root
            Dim WebAppRelativePath As String = topSite.ServerRelativeUrl
            If Not WebAppRelativePath.EndsWith("/") Then WebAppRelativePath &= "/"

            'Enumerate through each site and apply branding
            For Each site As SPWeb In siteCollection.AllWebs
                If Not site.MasterUrl.EndsWith("minimal.master") Then
                    site.MasterUrl = WebAppRelativePath & "_catalogs/masterpage/BSmain.master"
                Else
                    site.MasterUrl = WebAppRelativePath & "_catalogs/masterpage/BSminimal.master"
                End If
                If Not site.CustomMasterUrl.EndsWith("minimal.master") Then
                    site.CustomMasterUrl = WebAppRelativePath & "_catalogs/masterpage/BSmain.master"
                Else
                    site.CustomMasterUrl = WebAppRelativePath & "_catalogs/masterpage/BSminimal.master"
                End If
                site.AlternateCssUrl = WebAppRelativePath & "Style%20Library/BSResources/BS.css"
                site.SiteLogoUrl = WebAppRelativePath & "Style%20Library/BSResources/Images/BSlogo.png"
                site.UIVersion = 4
                site.Update()
            Next

            'Publish and Approve each file
            Dim styleLibrary As SPList = topSite.Lists.TryGetList("Style Library")
            If styleLibrary IsNot Nothing Then
                Dim folders As SPListItemCollection = styleLibrary.Folders
                Dim item As SPListItem = DirectCast((From i In folders Where DirectCast(i, SPListItem).Url = "Style Library/BSResources" Select i).FirstOrDefault(), SPListItem)
                ApproveAndPublish(item.Folder, styleLibrary.EnableModeration)
            End If

            'Publish and Approve the Master Pages
            Dim mpGallery As SPList = siteCollection.GetCatalog(SPListTemplateType.MasterPageCatalog)
            If mpGallery IsNot Nothing Then
                Dim mpages As SPListItemCollection = mpGallery.GetItems(New SPQuery With {.Query = "<Where><Or><Eq><FieldRef Name='FileLeafRef' /><Value Type='Text'>RegalIC.master</Value></Eq><Eq><FieldRef Name='FileLeafRef' /><Value Type='Text'>RegalICminimal.master</Value></Eq></Or></Where>"})
                If mpages IsNot Nothing Then
                    For Each i As SPListItem In mpages
                        If Not i.File.CheckOutType = SPFile.SPCheckOutType.None Then
                            i.File.CheckIn("Feature Activation", SPCheckinType.MajorCheckIn)
                            If mpGallery.EnableModeration Then
                                i.File.Approve("Feature Activation")
                            End If
                        End If
                    Next
                End If
            End If

        End If
    End Sub

    Private Sub ApproveAndPublish(folder As SPFolder, Approve As Boolean)
        If folder Is Nothing Then Return
        For Each subfolder As SPFolder In folder.SubFolders
            ApproveAndPublish(subfolder, Approve)
        Next
        For Each file As SPFile In folder.Files
            If Not file.CheckOutType = SPFile.SPCheckOutType.None Then
                file.CheckIn("Feature Activation", SPCheckinType.MajorCheckIn)
                If Approve Then
                    file.Approve("Feature Activation")
                End If
            End If
        Next
    End Sub

1. Apply your branding to each site

After gathering basic information about where the feature is being deployed and getting the correct reference URLs, we begin looping through every site in the sitecollection and setting the master page, sitelogo, and CSS settings to use our custom branding beginning in line 11.

The only thing different than the approach described in the Microsoft article, Deploying Branding Solutions for SharePoint 2010 Sites Using Sandboxed Solutions is that I am checking if the current master page is the minimal.master and if so, using my BSminimal.master file instead. This allows me to have both master pages deployed correctly, but it also allows me to restore these settings more accurately in the deactivating event (See my previous post).

2. Publish and Approve each resource file

To keep things simple, I keep all of my resource files in a single root folder within the Style Library. This makes looping through each subfolder and resource very simple to ensure that each one gets checked in and/or approved as necessary.

Lines 29-34 get a reference to the resource folder within the Style Library and pass that information over to a helper method called ApproveAndPublish. This method takes an SPFolder reference and a boolean indicating if approval is necessary or not. For the initial call, the folder is our resource folder and the approval setting comes directly from the Style Library and is found in the EnableModeration property of the SPList object.

The ApproveAndPublish method (Lines 55-68) is a recursive function that loops through every subfolder and checks in every file found. If Approval is required, it also marks them as approved.

This means you don’t have to track each file (either through stamping or keeping a list). This really cuts down on all the plumbing that is often necessary when working on a SharePoint solution.

3. Publish and Approve each Master Page

Unfortunately, Master Pages aren’t usually deployed to a sub folder and so the above technique for approval and check in has to be tweaked slightly. Lines 37-50 take care of this. Basically, we get a reference to the Master Page Catalog and use some basic CAML to isolate our master pages and then loop through them to check them in and/or activate them if required.

That’s all that’s required. You now have your files successfully deployed and ready to be used. Be sure to check out my previous post Branding Solution Cleanup. In that post I describe how to remove all of your solution files when your solution gets deactivated.

Branding Solution Cleanup

Applies To: SharePoint 2010

I followed the Microsoft article, Deploying Branding Solutions for SharePoint 2010 Sites Using Sandboxed Solutions and I was able to quickly get the bones of a Branding project put together. Unfortunately, I found that when the solution was deactivated all the files I deployed remained exactly where they were.

I found various solutions for removing your files ranging from individual file lists to marking every file with your feature ID, but for a simple Branding project all you really need to do is:

  1. Remove usage of your Master Pages from every site referencing them
  2. Remove your files from the Style Library
  3. Remove your Master Page files from the Master Page Catalog

The first two can be done in the FeatureDeactivating event handler and the third can be done in the FeatureUninstalling event handler. For those that just want the code, here it is:

    Public Overrides Sub FeatureDeactivating(ByVal properties As SPFeatureReceiverProperties)
        Dim siteCollection As SPSite = CType(properties.Feature.Parent, SPSite)
        If siteCollection IsNot Nothing Then
            Dim topSite As SPWeb = siteCollection.RootWeb

            'Calculate relative path to site from Web Application root
            Dim WebAppRelativePath As String = topSite.ServerRelativeUrl
            If Not WebAppRelativePath.EndsWith("/") Then WebAppRelativePath &= "/"

            'Enumerate through each site and remove branding
            For Each site As SPWeb In siteCollection.AllWebs
                If Not site.MasterUrl.EndsWith("minimal.master") Then
                    site.MasterUrl = WebAppRelativePath & "_catalogs/masterpage/v4.master"
                Else
                    site.MasterUrl = WebAppRelativePath & "_catalogs/masterpage/minimal.master"
                End If
                If Not site.CustomMasterUrl.EndsWith("minimal.master") Then
                    site.CustomMasterUrl = WebAppRelativePath & "_catalogs/masterpage/v4.master"
                Else
                    site.CustomMasterUrl = WebAppRelativePath & "_catalogs/masterpage/minimal.master"
                End If
                site.AlternateCssUrl = String.Empty
                site.SiteLogoUrl = String.Empty
                site.Update()
            Next

            'Kill Style Library Folder
            Dim styleLibrary As SPList = topSite.Lists.TryGetList("Style Library")
            If styleLibrary IsNot Nothing Then
                Dim folders As SPListItemCollection = styleLibrary.Folders
                Dim item As SPListItem = DirectCast((From i In folders Where DirectCast(i, SPListItem).Url = "Style Library/BSResources" Select i).FirstOrDefault(), SPListItem)
                item.Delete()
            End If

        End If
    End Sub

    Public Overrides Sub FeatureUninstalling(ByVal properties As SPFeatureReceiverProperties)
        Dim siteCollection As SPSite = properties.UserCodeSite
        If siteCollection IsNot Nothing Then

            'Kill Master Pages
            Dim mpGallery As SPList = siteCollection.GetCatalog(SPListTemplateType.MasterPageCatalog)
            If mpGallery IsNot Nothing Then
                Dim mpages As SPListItemCollection = mpGallery.GetItems(New SPQuery With {.Query = "<Where><Or><Eq><FieldRef Name='FileLeafRef' /><Value Type='Text'>BSmain.master</Value></Eq><Eq><FieldRef Name='FileLeafRef' /><Value Type='Text'>BSminimal.master</Value></Eq></Or></Where>"})
                If mpages IsNot Nothing Then
                    For i As Integer = mpages.Count - 1 To 0 Step -1
                        mpages(i).Delete()
                    Next
                End If
            End If

        End If
    End Sub

Wow code! Alright Scriptkitties, copy away! Everyone else, here’s what we’re doing and why:

1. Remove usage of your Master Pages from every site referencing them

After gathering basic information about where the feature was deployed and figuring out the correct reference URLs, we begin looping through every site in the sitecollection and resetting the master page to the defaults beginning in line 11.

We are just undoing what was done in the FeatureActivating event. The only thing of note is that I hate when a Branding solution replaces every MasterPage with theirs and then just blindly restores v4.master. Mostly this is fine, but if one of your subsites is an Enterprise Search site or anything else using the minimal.master you’ve just wrecked it. Obviously if you know you aren’t using minimal.master then you can simplify this section. Also, I always name my minimal.master replacement in the form [Something]minimal.master to ensure this works out.

2. Remove your files from the Style Library

To keep things simple, I keep all of my resource files in a single root folder within the Style Library. Obviously I have subfolders to organize images, fonts, etc. but all of those are within my one folder. This makes finding stuff much easier, but more than that it makes removing the files super easy – Just delete that folder.

Lines 27-33 do just that. After getting a reference to the Style Library (Every sitecollection in SharePoint 2010 has one of these), grab the folder (just replace the “Style Library/BSResources” string in line 31 with your folder path) and delete.

3. Remove your Master Page files from the Master Page Catalog

There are lots of guides for deleting deployed master pages and I didn’t find any that worked. Basically every time I tried to delete a master page from within the FeatureDeactivating event I got an error about them still being used. I’m sure there’s a good reason for this (feel free to let me know in the comments), but it doesn’t really matter because as long as you followed step 1 above, it’ll work in the FeatureUninstalling event.

We simply grab a reference to the sitecollection’s Master Page Gallery and use some simple CAML to grab references to our Master Pages. Then we walk through them and delete them.

That’s it, you now have a self-cleaning solution and you are a responsible member of the SharePoint community.

Relative Paths in SharePoint using SPUrlExpressionBuilder ($SPUrl)

Applies To: SharePoint Server (MOSS)

When you edit pages and/or master pages in SharePoint either for branding or some other project you will eventually come across the need to link to some resource file whether it’s an image, icon, CSS, etc. When this came up for me, I realized I had no idea how to do this properly.

Sure you can hardcode a link, but if this is part of a solution (especially a sandboxed solution) then this quickly becomes an unusable option. You can use the dots to do relative links, but again this can easily break on system pages or when you need to reference site collection level items from any sub site regardless of level.

Fortunately, some quick searching found the solution I needed. I came across Bugra Postaci’s blog post on the subject and that got me going. Be sure to check it out for some background, but the basic idea is to use the SPUrlExpressionBuilder class to generate relative links. Unfortunately, this class is part of the publishing namespace and so is only available in SharePoint Server (2010) and MOSS (2007).

He gives an example of linking to an image within the Style Library of the site collection using the following code directly within the master page:

<asp:Image ImageUrl="<% $SPUrl:~sitecollection/Style Library/CustomImages/BlogofBugra.jpg %>" runat="server" />

The key thing to note is the inline use of the $SPUrl command. The example shows this within an asp:Image control but this can be done in a standard tag as well (img, link, etc.).

After seeing that, I really wanted to know what other magic tokens you can use, but the Microsoft Documentaiton on the class doesn’t even show the inline $SPUrl syntax, let alone list any of the usable tokens! Doing a quick search will find you an assortment of undocumented tokens. However this can be misleading as the tokens listed in {} braces are generally only for custom actions and not for inline links (you can find examples of those lists here and here).

Here is what I’ve found by actually looking at the code using Reflector:

Token Replaced By Version
~site/ SPContext.Current.Web.ServerRelativeUrl 2007, 2010
~sitecollection/ SPContext.Current.Site.ServerRelativeUrl 2007, 2010
~language Thread.CurrentThread.CurrentUICulture.Name 2007, 2010
With the exception of the ~language token, the others are replaced using a call to Microsoft.SharePoint.Utilities.SPUtility.UrlFromPrefixedUrlCore.

SharePoint 2010 Default Theme Colors

Applies To: SharePoint

I’ve been messing with Branding quite a bit lately and have been trying to find a way to keep it as simple as possible. The easiest choice is to use themes. There are several out of the box and it’s easy enough to create your set of colors using PowerPoint or ThemeBuilder. On a publishing site you can even just specify them right on the site!

But every theme I tried just didn’t look right. I finally decided that I really liked the default theme except for a color or two. Unfortunately, I couldn’t just start with the default and change a couple of colors using the simple themes engine. So I cracked open the corev4.css to see if I could find the default colors. Sure enough, you can see where the Theme Replacement comments are right next to a specified color (default when no theme).

So I started just snooping through the CSS and found some really interesting things. First, the theming engine is really powerful. Not only does it replace element colors on the fly, it can tint images (nice png gradients), and for each color it will actually use shades for various elements allowing you to only have to specify a small number of colors to get a wide palette.

For much more detail about where and how theme colors are used in the CSS check out Eric Schrader’s blog post that includes a pretty awesome Excel file: http://eschrader.com/2010/09/23/sharepoint-2010-%E2%80%93-themes-and-corev4-css-comparison-technique-for-developers/

However, the most interesting thing I found was that when you don’t use a theme (Default) you get many more colors and so the page looks much better. What I mean is that in trying to find the theme color default, I found that each one had several variations which I’ve laid out in a chart below. I expected to find (except in the case of tinting and named variations (lighter, lightest, etc.)) a single color for each theme color. Instead, most had several variations – ALL of which get replaced by a single color. This results in the default theme having a much greater depth than any theme available.

For example, in the CSS file there are even single classes that specify different defaults for the same theme replacement (Lines 2302 & 2304 below):

.ms-siteactionsmenuinner{
/* [ReplaceColor(themeColor:"Dark2")] */ border-color:#21374C;
/* [ReplaceColor(themeColor:"Dark2")] */ border-top-color:#394f63;
/* [RecolorImage(themeColor:"Dark2-Lighter",method:"Filling",includeRectangle:{x:0,y:467,width:1,height:11})] */ background:url("/_layouts/images/bgximg.png") repeat-x -0px -467px;
/* [ReplaceColor(themeColor:"Dark2")] */ background-color:#21374c;
}

Notice the background-color and the border-top-color are defaulting to different colors but both are replaced with the Dark2 theme color.

So what does this mean? Well it means that the default theme (no theme) will always look better than any theme you choose. Looking through the chart below you may even be surprised to see that certain colors are reused in the default theme but are replaced by different theme colors! (The default Hyperlink color #0072BC for instance). If you’d like to achieve the same level of depth you’ll need to create your own CSS file based on corev4.css removing the theme comments and manually specifying your colors. If you’re very serious about Branding, you’re probably already doing this, but those of us looking for a quick solution, this is pretty frustrating.

The themeing engine is very powerful and is still a huge leg up from the themeing available in SharePoint 2007, but it surprises me that they didn’t extend it further to handle all the options they themselves obviously required.

Default Theme Colors

Theme Color Value Example of Color
Light1 #FFFFFF
Dark1 #000000
#003759
#676767
#525252
#6d6f72
#3b4f65
#0072bc
Light2 #f6f6f6
#efefef
#fcfcfc
#F5F6F7
#e7e7e8
#ffffff
#f9f9f9
Dark2 #204d89
#003759
#929fad
#65686b
#0072bc
#161d25
#3b4f65
#5d6878
#003759
#49617a
#21374C
#394f63
#23272c
#666666
#476382
#182738
Accent1 #44aff6
#529dcc
#0072bc
Accent2 #ff0000
#EC008C
#CA0078
Accent3 #003399
#0093CA
#00adee
Accent4 #fd9f08
#ffbb47
#FD9F08
Accent5 #058036
#36B000
Accent6 #FAE032
Hyperlink #0072BC
#0061a0
FollowedHyperlink #b10069