Build your own custom administration

Managing application specific configuration is mostly done within an application page (aka layouts page). Navigating to these pages is done via custom actions, which adds links to for example the Site Settings page. The challenge starts when the number of application pages grows. Do you keep adding them to the Site Settings page? Do you show them on all Site Settings pages in the web application of every web?

What you need is a centralized administration, like the Central Administration, controlling your application configuration. The Central Administration in SharePoint has two sorts of pages. The homepage (default.aspx) providing an overview and pages like Application Management showing a collection of links related to Application Management.

CentralAdministration_Homepage CentralAdministration_ApplicationManagement

It’s easy to build your own custom administration site. The image below shows my own administration site. Do you see the resemblance?

ApplicationAdministration_Homepage ApplicationAdministration_Security

Creating these pages is pretty straightforward when using the FeatureLinkSections class together with “LinkSectionLevel1.ascx” and “LinkSectionLevel2.ascx” controls. This web control handles the rendering of the links based on enabled features in the current context. The links are provisioned by custom actions as part of a (enabled) feature.

<SharePoint:FeatureLinkSections Runat="server" Id="SettingLinksV4" CellPadding="4" CellSpacing="4" Location="Contoso.SharePoint.AdminPage" LinkSectionControl="LinkSectionLevel1.ascx" />

The Location attribute is an unique identifier indicating the location where links are provisioned. The location is used in the custom action XML (shown below) to point to the location in the FeatureLinkSections control.

<!-- Admin Page - Security --> <CustomActionGroup Id="ContosoSecurity" Title="Contoso Security" Location="Contoso.SharePoint.AdminPage" Sequence="10" Description="" ImageUrl="/_layouts/images/ SiteSettings_UsersAndPermissions_48x48.png" /> <CustomAction Id="ContosoAdmins" GroupId="ContosoSecurity" Location="Contoso.SharePoint.AdminPage" Rights="EnumeratePermissions,BrowseUserInfo" Sequence="10" Title="Contoso Administrators" Description="..."> <UrlAction Url="_layouts/people.aspx" /> </CustomAction>

The FeatureLinkSections control uses two views controlled by the LinkSectionControl attribute.

LinkSectionLinks1.ascx

Shows a 48×48 icon for the group and links are placed below each other.

LinkSectionLinks1

LinkSectionLinks2.ascx

Shows a 32×32 icon for the group and links are delimited by the “|” character. LinkSectionLinks2

 

Visual Studio solution

I included the Visual Studio project containing the following components:

  • AdminPages_Web feature: Web scoped feature with event receiver updating the welcome page and quicklaunch. The feature contains the following components:
    • AdminPage module
    • AdminPageActions custom actions
    • PBWebTemplate element
    • Event receiver
  • AdminSite_Site feature: Site collection scoped feature containing the web template for creating the web. The feature contains the following components:
    • AdminWebTemplate web template
  • AdminPageActions custom actions: All custom actions containing all links displayed in the central adminstration.
  • AdminPages module: Provisions the adminpage.aspx and security.aspx pages in the web root folder.
  • AdminWebTemplate web template: Contains the web template for creating the web.
  • PBWebTemplate element: Property bag element with unique web template id

DownloadYou can download the WSP package and source code at http://sp2010admin.codeplex.com/.

Word text generator for Lorem Ipsum

Regularly I need Word documents with example text. Word has a example text generator feature which creates text like “Lorem ipsum dolor sit amet, consectetuer …” or “On the Insert tab, the galleries include items that are designed …” or “The quick brown fox jumps over the lazy dog. The quick brown fox …”.

Simply start Word and type =lorem(3,10) and press Enter

Word 2010, lorem ipsum function

This results in:

Word 2010, lorem ipsum result

There are some options you can use:

  • =lorem(p, l) where p is the number of paragraphs and l the number of lines per paragraph.
  • =rand(p,l) provides the same functionality but using different text “On the Insert tab, the galleries include items that are designed …
  • =rand.old(p,l) also provides the same functionality using different text “The quick brown fox jumps over the lazy dog. The quick brown fox …

Maybe you already knew, maybe not. I was looking for the “The quick brown fox jumps over the lazy dog. The quick brown fox …” text and thought lets share!

Out of the box Field and ContentType IDs

SharePoint keeps amazing me, even after working more than 6 years with the product. The product is this big that’s it is almost impossible to know all. This is one of those things. I simply did see it before.

When working on a “Yet Another SharePoint Gem” as Robin Meure called it. I stumbled on the SPBuiltInFieldId class and SPBuiltInContentTypeId class. When retrieving information like the CreatedBy field (also known as Author) it is very useful. Also when checking whether a item is based on a out-of-the-box (OOTB) content type. See exaples below.

Update: in addition to both SharePoint 2010 Foundation classes SharePoint Server has ContentTypeId class and FieldId class. Both are part of the Microsoft.SharePoint.Publishing namespace. Thanks to Jeffrey Schmitz (@DJeffa).

Here an example for the extension of the SPListItem class to retrieve the Author:

public static class SPListItemExtentions { /// <summary> /// Gets the author of a list item. /// </summary> /// <param name="item">Extended class.</param> /// <returns>Returns the author of the list item.</returns> public static SPUser GetAuthor(this SPListItem item) { string user = item[SPBuiltInFieldId.Author].ToString(); int userId = Convert.ToInt16(user.Split(';')[0]); return item.Web.AllUsers.GetByID(userId); } }

Another example for checking the base content type:

SPListItem item = list.GetItemById(1); SPListItem topic = item; // If item isn't the dicussion topic, get topic! if (!item.ContentType.Id.IsChildOf(SPBuiltInContentTypeId.Discussion)) topic = GetTopicItem(item);

Upgrading MOSS2007 to SharePoint 2010 results in navigation error

Having issues with navigation after an upgrade from MOSS2007 to SharePoint 2010 using the publishing template? We did! After an upgrade the navigation returned errors instead of menu items. We encountered these issues when the Dutch language pack was installed, because the Pages library has been translated/renamed to “Paginas”.

SharePoint Upgrade navigation errors Dutch Language Pack

Microsoft has provided a solution described in KB 2484317. The root cause of the issue is described as:

With Microsoft Office SharePoint Server 2007 we stored new Publishing Pages in a library called Pages. In SharePoint 2010 it has been translated to Paginas as the default for Dutch. The navigation code assumes that any changes from the default are noted in a specific property, which is not the case for upgraded Dutch sites that have been created with the 2007 default name.

The solution to this problem is pretty easy, a simple PowerShell script will restore your navigation.

$site = Get-SPSite("http://WebApplicationURL") foreach ($web in $site.AllWebs) { Write-Output("web:" + $web.ServerRelativeUrl); $pageslistid = $web.AllProperties["__PagesListId"] if ([String]::IsNullOrEmpty($pageslistid)) { Write-Output($web.ServerRelativeUrl +" doesn't have a pages list id set - skipping"); } else { $pageslistguid = new-object System.Guid($pageslistid) $pageslist = $web.Lists[$pageslistguid] $pagesurl = $pageslist.RootFolder.Url $pb = $web.Properties; $pb.Add("__PagesListName", $pagesurl); $pb.Update(); Write-Output($web.ServerRelativeUrl +" set pages list to " + $pagesurl); } }

PowerShell script for retrieving AD user attributes based on display name

Populating more than 1.000 people picker values based on users display name can be quite challenging when doing it manually. PowerShell to the rescue!

This script reads a TXT-file with 1 users’ display name per line. Per user it searches the local directory and outputs the displayName and sAMAccountName in a semicolon separated line (easy for Excel extraction).

PowerShell script:

function ConvertUser($user) { $search = New-Object DirectoryServices.DirectorySearcher([ADSI]“”) $search.filter = “(&(objectClass=user)(displayName=$user))” $results = $search.Findall() foreach($result in $results){ $userEntry = $result.GetDirectoryEntry() Write-Output('' + $userEntry.displayName + ';' + $userEntry.sAMAccountName) } } function ConvertUsers { process{ foreach($user In $_){ ConvertUser($user) } } } Get-Content “.\Users.txt” | ConvertUsers

TXT-file with users (filename: “Users.txt”):

Aaron Painter Adam Barr Alan Brewer Alan Steiner

Make a TXT-file with users display name (see example below). Then copy, paste and run the script above should do the trick.

image

Removing Super User and Super Reader account

A colleague of mine had issues with the object cache user accounts (Super User and Super Reader accounts). He used the blog post addressing the access denied errors. The post describes setting the Super User and Super Reader, but it did not describing removing both properties.

My colleague needed to remove the properties, this PowerShell script removes both properties:

# Remove properties $wa = Get-SPWebApplication -Identity "<WebApplication>" $wa.Properties.Remove("portalsuperuseraccount") $wa.Properties.Remove("portalsuperreaderaccount") $wa.Update() Write-Host("Properties count for " + $wa.Url + ": " + $wa.Properties.Count)

I hope it helps those who want to undo their settings. To complete the script:

# Get current properties $wa = Get-SPWebApplication -Identity "<WebApplication>" $user = $wa.Properties["portalsuperuseraccount"] $reader = $wa.Properties["portalsuperreaderaccount"] # Write current properties Write-Host("Properties count for " + $wa.Url + ": " + $wa.Properties.Count) Write-Host("Super user (" + $wa.Url + "): '" + $user + "'") Write-Host("Super reader (" + $wa.Url + "): '" + $reader + "'")

SharePoint Features and Solution Upgrading slide deck DevDays

Last Thursday I presented at DevDays 2011 in The Hague (NL). All sessions are recorded and can be viewed on Channel 9. It was a great time!

SharePoint features and solution upgrading

This was a level 300 session about SharePoint features and solutions upgrading.

The feature and solution framework used for the deployment of custom SharePoint solutions was introduced in SharePoint 2007. With SharePoint 2010 some major improvements have been made to the framework to allow for feature versioning and upgradability. This provides new ways to deal with application lifecycle management.

This presentation covers the feature and solution framework, explains when to use the feature upgrade and talks about upgrading existing activated and non-activated features. During the session different demos will be build supporting the different scenarios.

You can download the slide deck here:


Download PowerPoint

 Enjoy the video on my session on Channel 9!

Channel9 SharePoint Features en Solution Upgrading

How to recover a crashed VM

Last week it happened again, one of my virtual machines crashed. And I needed a way to repair the virtual machine or at least retrieve the files located on the virtual hard disk.

Important note: This is (probably) not supported and I can’t guarantee your virtual machine is recovered. Please only use this how-to as your last resort! It might help you :-)

What happened?: When the virtual machine (VM) crashed it needed a forced shutdown. When restarting the VM it came back with Recovery mode. I wasn’t able to start Windows normally!

Solution: Within VirtualBox (I’m currently using Oracle VM VirtualBox), or any other virtualization software, you are able to mount multiple hard disks. You need to mount the hard disk of the crashed VM to another healthy VM (which works normally). Pay attention to snapshots, mount the latest snapshot! Start the healthy VM.

What happens?: The healthy VM will pick up the crashed hard disk and starts scanning and repairing the hard disk. After repairing Windows will start normally and you are able to browse the hard disk and retrieve your files.

If you are having a lucky day? You could try to shutdown the healthy VM. Unmount the hard disk (which was repaired). And try starting your crashed VM. Now you know whether you are really lucky! It might start!? Good luck!

Adding script like jQuery to your pages

When working with the post of Andrew Connell I faced some challenges with implementing my solution. I wanted to add multiple scripts with dependencies to my pages. When creating one elements.xml containing all three script links the order of the <CustomAction Location=”X” ScriptScr=”Y” /> elements is important.

The following elements.xml is packaged in a feature.

<Elements xmlns="http://schemas.microsoft.com/sharepoint/"> <CustomAction Location="ScriptLink" ScriptSrc="~site/_layouts/project_name/js/custom-1.0.0.js?rev=20110413" /> <CustomAction Location="ScriptLink" ScriptSrc="~sitecollection/_layouts/project_name/js/ jquery_query-2_1_7.js?rev=20110413" /> <CustomAction Location="ScriptLink" ScriptSrc="~site/_layouts/project_name/js/jquery-1.5.1.min.js?rev=20110413" /> </Elements>

This results in the following HTML in your pages.

document.write('<script type="text/javascript" src="/_layouts/project_name/js/jquery-1.5.1.min.js?rev=20110413"></' + 'script>'); document.write('<script type="text/javascript" src="/_layouts/project_name/js/jquery_query-2_1_7.js?rev=20110413"></' + 'script>'); document.write('<script type="text/javascript" src="/_layouts/project_name/js/custom-1.0.0.js?rev=20110413"></' + 'script>');

Important to know is the order in the elements.xml is the opposite sequence in the HTML. Meaning the script “jquery_query-2_1_7.js” is depending on “jquery-1.5.1.min.js” and needs to be the last item in the elements.xml.

In the script link I’m using “~site” and “~sitecollection” tokens to change the link according to the site or site collection where the user is located. This is important when creating a generic feature used on multiple sites.

By using the “?rev=20110413” at the end of your link you can force the browser to refresh its local cache. A (script) file is downloaded the first time accessing the page. As long as the link does not change the browser does not download the file. By changing the query string you can force a download of the script when changes are made. The actual value “20110413” is todays date, but can be your own versioning logic.

The example above also has a version number in the file name, this is another way of forcing a download. I simply wanted to illustrate the possibilities, you probably will use either query string or version number.

Lessons learned / best practices:

  • Order in elements.xml for <CustomAction /> elements is of influence of the order of script links in the resulting HTML.
  • Use “~site” and “~sitecollection” tokens to control the script link.
  • Use “?rev=20110413” query string or version number in filename to force a download of the file.
Follow

Get every new post delivered to your Inbox.