Overcoming the “Content type is still in use” message when deleting unused workflow task content types

When you’re working on a workflow that contains task items or task processes from SharePoint Designer, SharePoint will create content types for these tasks. If you’ve been experimenting with several variations, you could end up with some left over content types for tasks that you’re no longer using. Of course, we’d want to keep this list clean. However, when you try to delete these unused content types, you’ll receive a “Content type is still in use” message.

This message indicates that a list or library is still using the content type. This will be the task list assigned to your workflow (usually “Workflow Tasks”). To remove the content type from the SharePoint site, you should first remove the content type (assignment) from all task-based lists. Go to the list settings and remove the content type.

You can now remove the content type from the content types gallery.

Get file extention in XSLT

When working with data view web parts or data form web parts in SharePoint, you might want to use some conditional formatting or branching logic, based on the file extention of your SharePoint item.

This xsl template returns the file extention from an URL:

<!-- Determine File Extention template -->
<xsl:template name="get-file-extension">
    <xsl:param name="path"/>
    <xsl:choose>
        <xsl:when test="contains($path, '/')">
            <xsl:call-template name="get-file-extension">
                <xsl:with-param name="path" select="substring-after($path, '/')"/>
            </xsl:call-template>
        </xsl:when>
        <xsl:when test="contains($path, '.')">
            <xsl:call-template name="get-file-extension">
                <xsl:with-param name="path" select="substring-after($path, '.')"/>
            </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="$path"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

It runs recursively through the parameter “path” and returns the extention.

You can call and use it like this:

<xsl:variable name="extension">
	<xsl:call-template name="get-file-extension">
		<xsl:with-param name="path" select="@yourcolumnname" />
	</xsl:call-template>
</xsl:variable>
<!-- example use in branching logic -->
<xsl:choose>
	<xsl:when test="$extension = 'pdf'">
		<!-- add your logic -->
	</xsl:when>
</xsl:choose>

 

Different ways of scanning a document into SharePoint

SharePoint offers excellent ways to manage scanned documents, i.e. in PDF or XPS formats. Most scanners nowadays include software that automatically scans multiple page-documents and creates searchable PDF docs using OCR (text recognition).

When you’re scanning documents often, you’d want to minimize time and (manual) actions to get the documents into SharePoint. Luckily, SharePoint offers an array of possibilities to achive this. Furthermore, SharePoint is becoming more and more widespread in organizations, so many scanner manufacturers already support SharePoint connectivity out-of-the-box with their scanners and scanner-software. These are the most common methods:

SharePoint-ready scanners

The easiest way of scanning a document into SharePoint is achieved if the scanner-software manufacturer already prepared the scanner to work with SharePoint. This is already the case with many suppliers, such as Epson and Canon. Usually, this involves specifying the SharePoint address and port number and the user credentials to connect to this SharePoint site. These SharePoint-ready scanners usually work using the SharePoint Web Services and support WSS 3.0 / MOSS 2007, SharePoint 2010 and higher.

WebDAV

Web-based Distributed Authoring and Versioning (WebDAV) is an extension to the HTTP protocol, which makes it possible to manage files on a SharePoint document library from Windows Explorer. Some printer manufacturers support WebDAV out-of-the-box. When this is not the case, you could map a network drive letter to a WebDAV location, and treat the WebDAV location as a network share. There are different ways of doing this, depending on the Operation System. Use Google or Bing to find the one that works for you.

Email enabled libraries

If your scanner (software) does not support SharePoint or WebDAV directly, you could also specify an incoming email address for a library and send scanned documents to this address. Most scanners support scan-to-mail functionality. Be aware though, that this will impact your email infrastructure and keep email size limits in mind.

Alternate solutions

There are many more possible solutions, that may require (custom) development to some extend, depending on the flexibility of your scanner software. Examples include  using RESTful services within SharePoint 2010, PowerShell, the SharePoint Client Object Model, SharePoint Workspace, using File Classification Infrastructure (FCI) and more. Which one works best in your solution, depends primarily on the scanner and scanning software used, as well as the network infrastructure your users will be working in.

Debugging Feature EventReceivers… the easy way

One of the great improvements of SharePoint 2010 is the ease of debugging your SharePoint projects. However, this does not hold for Feature Event Receivers, at least so it’d seem.

There is, however a very fast and easy way to start debugging your Feature EventReceiver’s overridden methods, like FeatureActivated() and FeaureDeactivating(): Simply add the following line of code at the point you want to start debugging:

System.Diagnostics.Debugger.Launch();

Now simply deploy your solution to your local SharePoint environment. This will start the debugger of your liking.

 

How to get the Managed Path for a Site Collection

There’s no easy way of retrieving the Managed Path for a Site Collection (SPSite) through the SharePoint 2010 object model.

Therefore, I wrote this method, to retrieve it for you. It should work with both wildcard inclusion and explicit inclusion Managed Paths, including nested managed paths (i.e. /sites/, /sites/projects/, /sites/depts/, etc.).

private Microsoft.SharePoint.Administration.SPPrefix GetManagedPath(SPSite site)
{
    if (site == null)
        throw new ArgumentNullException("site");
    string relativeUrl = site.ServerRelativeUrl;
    if (relativeUrl.Length == 0)
        throw new Exception("Invalid ServerRelativeUrl for this SPSite.");

    foreach (Microsoft.SharePoint.Administration.SPPrefix prefix in site.WebApplication.Prefixes)
    {
        switch (prefix.PrefixType)
        {
            case Microsoft.SharePoint.Administration.SPPrefixType.ExplicitInclusion:
                if (relativeUrl == "/" + prefix.Name || relativeURL == prefix.Name)
                    return prefix;
                break;
            case Microsoft.SharePoint.Administration.SPPrefixType.WildcardInclusion:
                // WildCard Inclusion prefixes can be nested (i.e. /sites/ , /sites/projects/, /sites/depts/).
                // Thus, we remove the site collection's name and compare against the whole string, in stead of using .StartsWith()
                if (relativeUrl.Substring(0, relativeUrl.LastIndexOf("/")) == "/" + prefix.Name)
                    return prefix;
                break;
        }
    }
    // A prefix should've been returned by now.
    throw new Exception("No SPPrefix (Managed Path) found for this SPSite");
}

The method returns an SPPrefix class, which contains the Name and PrefixType properties of the Managed Path. If the Site Collection is located at the root of the Web Application, the SPPrefix.Name property will be empty and of the type Explicit Inclusion.

New tool: SharePoint 2010 Theme Generator

I’ve just released version 1.0 of my SharePoint 2010 Theme Generator. You can find it on my site, under the new tools section.

This is a Silverlight application that generates a high quality color scheme from any base color, which you can use as a SharePoint theme.

It currently only has basic styling and no function for generating the .thmx files yet.

Go check it out and please give me feedback.

*Update* I just saw Microsoft’s Theme Builder doesn’t support Hex color values… So I’ve added RGB output to the generator as well.

Updating the User Information List in SharePoint 2010 from AD

In some cases, there is no need for User Profiles and My Sites. However, when you’re using Active Directory for authentication, you might notice that changes made to the AD Domain users are not effective for SharePoint users, such as a different or new email address. When you click “My Settings” from the user menu, you’ll still see the old values for name, email address, etc.

A simple way of solving this is using the Set-SPUser cmdlet in PowerShell, using the -SyncFromAD parameter. However, when you have multiple site collections with multiple users, you’ll need to do this for each user, in each site collection’s top level site.

The following PowerShell script will save you the trouble of going through this manually, it fetches all site collections with their RootWebs and syncs all available SPUser objects with AD.

Make sure you run it under a user that has access to each site collection, preferably as a site collection administrator.

$sites = Get-SPSite -Limit All
foreach($site in $sites) {
    $web = $site.RootWeb
    if($web -ne $null) {
        Write-Host "Processing: $site"
        Get-SPUser -Limit All -web $web | foreach {
            if ($site.WebApplication.UseClaimsAuthentication) {
                $claim = New-SPClaimsPrincipal $_.LoginName -IdentityType WindowsSamAccountName
                $user  = $web | Get-SPUser -Identity $claim -ErrorAction SilentlyContinue
            }
            else
            {
                $user = $web | Get-SPUser -Identity $_.LoginName -ErrorAction SilentlyContinue
            }
            if ($user -ne $null)
            {
                Set-SPUser -Identity $user -web $web -SyncFromAD -ErrorAction SilentlyContinue
            }
        }
    }
    $web.Dispose()
    $site.Dispose()
}

 

“Edit in SharePoint Designer” starts SPD 2007 instead of 2010

If you’ve installed both SharePoint Designer 2010 and SharePoint Designer 2007, you might run into this problem:

You’re on a SharePoint 2010 page and want to edit it in SharePoint Designer 2010. You switch to the Page menu on the ribbon, expand the Edit Page button and click the Edit in SharePoint Designer menu item.

You’d expect SharePoint Designer 2010 to start up and open the current page for you. Instead, SharePoint Designer 2007 starts up. Which is no good to you, and quite annoying!

There can be multiple ways to fix this, but by far the easiest is to go to your Windows Control Panel, open the Programs and Features list, locate Microsoft SharePoint Designer 2010 in this list and click the Change button. This opens a Setup window with several choices. Close all MS Office programs and choose the Repair option. Wait for a few minutes and after it’s finished, reboot your machine.

The correct settings have now been set for SharePoint Designer 2010 and you should be able to start it directly from the SharePoint pages.

Using Powershell to Add Permission Levels in SharePoint 2010

Besides using PowerShell to modify or adding list permissions, you can also add new Permission Levels. As permission levels don’t persist within site templates, this can be handy when creating deployment scripts for new sites.

Here’s how you do it:

# Add Permission Level to a site
# (c) 2011 Morgan de Jonge

$spSite = Get-SPSite "http://portal.contoso.com"
# We'll assume the list is in the top-level site in the site collection
$spWeb = $spSite | Get-SPWeb

# In this example, we add a new Permission Level labelled "Add Only" to the site, which will allow users to only add new items (no editing or removing)
if($spWeb.RoleDefinitions["Add Only"] -eq $null)
{
    # Role Definition named "Add Only" does not yet exist
    $spRoleDefinition = New-Object Microsoft.SharePoint.SPRoleDefinition
    $spRoleDefinition.Name = "Add Only"
    $spRoleDefinition.Description = "Can only Add items. Use this Permission Level for List or Library Permissions."
    # .Type is a ReadOnly property, hence it'll remain on "None".

    # Use the command [System.Enum]::GetNames("Microsoft.SharePoint.SPBasePermissions") to get a list of possible BasePermission values
    # For this Permission Level, we'll add four base permissions:
    $spRoleDefinition.BasePermissions = "ViewListItems, AddListItems, Open, ViewPages"
    $spWeb.RoleDefinitions.Add($spRoleDefinition)
}

#Display the properties for our new Permission level
$spWeb.RoleDefinitions["Add Only"] | Out-Host

$spWeb.Dispose()
$spSite.Dispose()

Unfortunately, the RoleDefinition.Type property, which contains a RoleType Enum value,  is Read-Only. Hence, it’ll get the default value “None”.

The .BasePermissions Property is a flags attribute which contains the actual permissions granted to users and groups assigned with the permission level. See MSDN for a description of these Permissions, or use the following command to enumerate them in PowerShell:

# Enumerate through SPBasePermissions
PS > [System.Enum]::GetNames("Microsoft.SharePoint.SPBasePermissions")
EmptyMask
ViewListItems
AddListItems
EditListItems
DeleteListItems
ApproveItems
OpenItems
ViewVersions
DeleteVersions
CancelCheckout
ManagePersonalViews
ManageLists
ViewFormPages
Open
ViewPages
AddAndCustomizePages
ApplyThemeAndBorder
ApplyStyleSheets
ViewUsageData
CreateSSCSite
ManageSubwebs
CreateGroups
ManagePermissions
BrowseDirectories
BrowseUserInfo
AddDelPrivateWebParts
UpdatePersonalWebParts
ManageWeb
UseClientIntegration
UseRemoteAPIs
ManageAlerts
CreateAlerts
EditMyUserInfo
EnumeratePermissions
FullMask

See the modify or adding list permissions article for instructions on how to assign this new Permission Level to a user or group.