Introducing the Template Library Connector for SharePoint 2010

Are you annoyed that you have to create a new content type for every Office document template you want to publish in SharePoint 2010? Do you think it’s stupid that the SharePoint administrator has to re-publish that content type just because someone updated the document template? Are you disappointed that you cannot create a tree structure under the “New Document” button?

If so, you might want to consider the Template Library Connector for SharePoint 2010.

With the Template Library Connector you can choose any existing document library to be the source of document templates for your document library. You can choose as many sources as you want for each library, and all your libraries can have different sources.

Template Library Connector for SharePoint 2010

The new button "New from Template" is part of the Template Library Connector for SharePoint 2010

Depending on the size of your SharePoint 2010 environment, you can choose to designate a site collection to be your template hub, and put all template libraries there, or you can use your local site collection as its own source.

With the template library connector, you can keep the content types down to a sensible number. Say you have a contract template translated to five languages. Normally you would need five content types to publish these templates. If you were smart, you created one master with the site columns, but you would still need one dummy content type to attach the template. Now you can instead create one content type called Contract, and then assign this content type to all five templates.

When there is a change to a template, the template owner or a super user can simply go to the template library and edit the template directly. No re-publishing is needed, the updated template is immediately available in all subscribing libraries.

The template owners can organize their templates in a folder structure, and this structure is mirrored in the template selector modal window. This way you can have as many templates as you want available in a document library.

I created a couple of screen casts for those who prefer that: config and use. I also added some documentation on the codeplex site.

Go ahead and try it out, and please post comments and suggestions below this post.

Big thanks to Einar Otto Stangvik who created the Template Library Connector for MOSS2007, and to Henrik Næss who contributed to the connector for SharePoint 2010.

Posted in SharePoint | Tagged | 29 Comments

Sorting SharePoint 2010 SPFolders and SPFiles

Sometimes you want to iterate through all SPFolders in a list or library, or all items in a list or folder. You may have noticed that the folders or items seem to come back in no particular order. You probably want to sort them in some order…

The best I got from Google was this post, but I think it can be done even sleeker. First create a temporary generic list, then sort it using a neat Lambda expression, and finally iterate over the temporary list:

// folder is some SPFolder, e.g. SPList.RootFolder
List<SPFolder> spFolders = folder.SubFolders.Cast<SPFolder>().ToList();
spFolders.Sort((f1, f2) => f1.Name.CompareTo(f2.Name));

foreach (SPFolder subFolder in spFolders)
{
	// Your code here
}

Same procedure for SPFiles

// folder is some SPFolder, e.g. SPList.RootFolder
List<SPFile> spFiles = folder.Files.Cast<SPFile>().ToList();
spFiles.Sort((f1, f2) => f1.Name.CompareTo(f2.Name));

foreach (SPFile file in spFiles)
{
	// Your code here
}

I assume this work because the SPFolder.Files and SPFolder.SubFolders properties both are of a type that inherit the SPBaseCollection class. Thus, you should be able to use this method on any type that inherits one of the classes listed in SPBaseCollection’s inheritance hierarchy. For instance, SPSite.AllWebs, SPList.Items and SPList.Fields should all be sortable as shown above

Of course, while I sort alphabetically using the Name property in this example, you can sort on any property you want, like the TimeLastModified.

As always: Comments, questions, likes, dislikes, etc. are welcome!

Posted in SharePoint | Tagged , | Leave a comment

SharePoint 2010 SPListCollection.GetList by Id without permissions

If you want to get hold of a SharePoint List by its ID, but only when the current user has access to it, there are many pitfalls. If you try to use SPListCollection.GetList, and plan to check the permissions after you get it, it’s too late. Your app will blow up and the user will get the “Access Denied” window.

One solution is to make use of the ListsForCurrentUser property, and use a Lambda query to check if our ID is among the lists the current user has access to:

web = OpenWebById(webId);
if (web == null)
	return null;

var listId = new Guid(myListId);

// Return null if the current user does not have access
// (and if list does not exist)

web.Lists.ListsForCurrentUser = true;
List<SPList> lists = web.Lists.Cast<SPList>().ToList();
if ((lists.Where(t => t.ID == listId)).Count() == 0)
	return null;

// If we get to here, the user HAS access, so we can safely
// use web.Lists.GetList() or alternatively web.Lists[listId]

var list = web.Lists.GetList(listId, false);

Sometimes it’s easiest to solve SharePoint programming problems by going around them.

As always: Comments are welcome!

Posted in SharePoint | Tagged , | Leave a comment

SharePoint Person or Group site column problem

Have you ever created a SharePoint site column of type “Person or Group“? Did you use this column in a content type used by Microsoft Word documents? Distributed from a content type hub? If so, you may be in trouble…

New site column - Person or Group

New site column: Person or Group

This type of site column was just what my client wanted. A smart lookup-field where they could select employees to tag their documents with. And it even comes with communicator presence information by default. Perfect for a HR department. One would even think that this field would work if an employee changes her name. In theory.

In practice however, the situation is rather different. In fact, this type of field can cause big trouble: The person added to this column may change to a different person!

Seriously? Yes, I’m afraid so. If you move a Word document from one site collection to another, you will probably see that another person shows up in that field/column. You may also see that the person is lost, or in rare cases, the correct person will still be there.

How can this happen? Well, in the document property promotion process values from the document metadata fields/columns are promoted to the SharePoint library (list). And similarly, in the document property demotion process, values from the columns in the library are demoted back to the document.

One should think that for a Person field, the DOMAIN\username would be written to the document, but this turns out not to be the case at all. This is what I found inside my test document:

<Company_Employee xmlns="264156cb-56b7-53fe-ad4d-c6818be5ec95">
   <UserInfo>
	<DisplayName>Garnes, Øystein</DisplayName>
	<AccountId>10</AccountId>
	<AccountType/>
   </UserInfo>
</Company_Employee>

It turns out that the display name and an account id is what is written to the document. This ID is the ID from the UserInfo table in the content database, which is local to each site collection! Hence, ID number 10 might be me in one site collection, but not in another. Use the following link to look at the users in your site collections:

http://<server>/sites/<sitename>/_layouts/userdisp.aspx?ID=<id&gt;

When you upload a document to a new site collection (which also has the content type with the same person-field), the promotion process will read that ID number, and try to match it to a user is it’s own UserInfo table. This gives three possible outcomes:

  1. Another person has that ID (e.g. ID no. 10 might be John Smith). If so, SharePoint will exchange the current display name with the new one.
  2. No person has that ID number in the target site. If only 8 persons were referenced so far in site 2, ID 10 would not exist, and SharePoint would delete the current person from that field
  3. If you are lucky (I was once in my test environment), the ID number corresponds to the same person in both source and target site, and the information is kept unchanged.

I asked a question on the SharePoint MSDN forum in this thread, but I have had no replies yet. If you can prove me wrong, or have a workaround, please add a comment below!

Posted in SharePoint | Tagged | 2 Comments

Insert SharePoint 2010 Document ID in a Word document

So the document id feature is a cool thing with SharePoint, but how can you add the document id inside the document text? My client wanted to use the document id as a reference value in their letters, in the classic “Our ref.” field.

Adding the document id for one document is easy: First save your document in a library where the document id feature is enabled, close and re-open the document, select insert – quick parts – document properties, and select the “Document Id Value”.

But end users don’t want to be bothered with inserting quick parts! They use office templates and want the value to pop up by default in the correct place.

Now this turned out to be a challenge, but here is the best way I have found so far: You will need:

  • A content type in your content type hub
    • the document if feature must be enabled in the CT hub
  • A site collection where the document ID feature is enabled.
  • A site collection where the document ID feature is NOT enabled.
  • Both site collections must be subscribing to the content type hub, so they inherit the content type.

Prepare your content type:

  • Make sure the DocId featue is enabled in the content type hub.
  • In the content type hub, go to site settings – site content types.
    • Create a content type if you haven’t already done so.
  • Edit your content type, select “Information management policy settings”.
  • Click “Enable Labels”.
  • In the Label format input field, write {Document ID Value} and click OK.
  • Republish your content type (under “Manage publishing for this content type”).
  • Run the following timer jobs:
    • “Content Type hub”
    • “Content Type Subscriber – <webapp name>”.

Prepare your Microsoft Word Document Template

  • Create a document library in the site collection where there is no document id.
    • Note: It is crucial that the DocId feature is disabled. If your document template gets a document id here, it will be stuck in the template, and all documents based on that template will get the same document id as the template, which is obviously not what you want!
  • Go to library settings, advanced settings, and enable content types.
  • Click “add from existing site content types” and add your content type.
  • Upload your document template to this library.
  • Make sure that it is given the correct content type.
  • Open the template in MS Word from the library.
  • Place the cursor in the correct location.
  • Click Insert – Quick Parts – Document Properties – Label.
  • Add a Word macro to the template: Open the template in Word, click the Developer tab – Macros – call it Reload and hit Create – use the following code (I got this from this MSDN forum thread)
Sub Reload()
   ActiveDocument.Reload
End Sub
  • Add another macro to call Reload directly after save. This will make the Document ID show in the document, and the user can print etc.
Sub FileSave()
   Dialogs(wdDialogFileSaveAs).Show
   Reload
End Sub
  • Save and close word.
  • Upload the template to the content type:
    • In the content type hub, go to site settings – site content types, edit your content type, click advanced settings, add it there.

Prepare the document library

  • Create a document library in the site collection where document ids are enabled.
  • Go to library settings, advanced settings, and enable content types.
  • Click add from existing site content types and add your content type.
  • Your content type (including the template) is now available for the end user.

The end user can now get the document id for free

  • In the document library ribbon, click “New Document” and select the newly created content type.
  • Fill in the desired fields etc. in the template and save it to the SP library.
    • SharePoint will now generate a document Id for the document.
  • Voilá, after save the reload macro is called, and the {Document ID Value} text is replaced with the actual document id value:

Background
The document template has the label containing the document id. When a user creates a document from the template and saves it to a SharePoint Library where the document id feature is enabled, SharePoint will assign a document id to the document, and demote this value from the list/library into the document itself.

Please add a comment below if you find this post useful.

Note: If you do not use a content type hub, please see Isaac Blum’s blog post on the same subject.

Note also that when a document that has a document ID is uploaded to SharePoint, the document ID will be replaced by a new document ID, as it is considered a copy. Because of this, you might want so send your letters that contain document IDs as PDF documents (or on paper of course).

Posted in SharePoint | Tagged , | 34 Comments

Activate On Default

When developing SharePoint features, you can set some properties on your features. One of them is called “Activate On Default”:

Now this sounds straight forward, right? Well, it both is and isn’t.

A few days ago, I created a SharePoint solution package with two features. One should create some site columns and content types, the other should manipulate the ribbon in document libraries in a specific site. So I set the content type feature to activate on default and the ribbon feature to not activate on default, as I was going to do this manually.

To my surprise, they were both activated on deployment!

I turns out that this setting only applies to features that are scoped to farm or web application level. If your feature is scoped to site (site collection) level (like my features) or web (site) level, then it doesn’t matter what you select here. Thanks to Christopher Kimbell for this knowledge. In fact, it has little to do with feature activation as part of deployment

So how to specify whether or not your feature should be activated on default when deploying? Well, go to your project properties, click SharePoint, and you’ll see this:

Here you can select “No activation” to stop visual studio from activating your features after deployment. Note that this is an “all or nothing” setting, so either all your features will be activated, or none. And this one does not discriminate based on feature scope, so features scoped to site and web will also be controlled here. To manually activate one of your features, use stsadm (this example is for a site scoped feature)

STSADM.EXE -o activatefeature -name my.feature.name 
-url http://my.domain.com/sites/somesite/

Also note that this last setting is a visual studio deployment setting, while the “activate on default” is a setting for the SharePoint feature. That is, if you create a web application scoped feature and leave the “activate on default” to true (its default setting) and deploy it, every web application you create after that deployment will get this feature turned on. That is probably not what you want, it is much better to activate the features you need explicitly using STSADM or a PowerShell cmdlet.

Also check out the official documentation.

Posted in SharePoint | Tagged , | 1 Comment

Error: Cannot add the specified assembly to the global assembly cache

Yesterday I was not able to deploy my solution to my sharepoint development server, with the error message

Error occurred in deployment step ‘Add Solution’: Error: Cannot add the specified assembly to the global assembly cache: MyLibrary.dll.

As the error message did not tell me why this happened or how I could fix it, I asked google. I found a lot of suggestions regarding permissions which were not relevant in my case. It turned out to be an easy fix however:

Manually uninstall the dll from the GAC!
In details: Start, run, %windir%\assembly\, find your dll, right click and choose uninstall.

In my case, I was not allowed to uninstall it because it was in use, so with some help from the process explorer from Sysinternals I found (click Ctrl+F and search) that owstimer, web analytics service and four instances of visual studio (devenv) were all using my dll. Restarting the services and closing my visual studio sessions allowed me to uninstall the dll from the GAC, and deployment worked fine again after that.

Posted in SharePoint | Tagged , | 13 Comments