Ribbon Customization in SharePoint 2010

SharePoint 2010 Resources - Ribbon Development

MSDN Documentation


Customizing the ribbon - Chris O'Brien.

Customizing and Extending the SharePoint 2010 Server Ribbon
http://msdn.microsoft.com/en-us/library/gg552606.aspx - Andrew Connell

Creating a SharePoint 2010 Ribbon extension - part 1 - Wictor Wilén

SharePoint 2010 Ribbon Controls - Part 1 - Summary (multi-part series) - Wictor Wilén

Using RefreshCommandUI with the Server Ribbon (Dallas Tester, Fred Mameri) - SharePoint Developer Team Blog

Tokenization in the SharePoint 2010 Server Ribbon (Dallas Tester) - SharePoint Developer Team Blog

Enabling a Button on the Ribbon Based on Selection - Microsoft SharePoint Team Blog

Fixed width layouts, the scrollbar and the ribbon in SharePoint 2010. A better way? - web.o

A Better Enhanced SharePoint 2010 Floating Ribbon - Greg Galipeau

Code to Hide the Ribbon and Site Actions Menu for Anonymous Users - Elumenotion

Ribbon customization : Basics - Mano Mangaldas

How To Activate SharePoint Ribbon Tab by JavaScript Code - Ingo Karstein

Sample Visual Studio 2010 project for creating a custom SharePoint 2010 Ribbon tab on runtime (!) - Ingo Karstein

Additional Ribbon Sample: Custom Ribbon Tab generated by a delegate control that lists all associated list workflows and allow to start a workflow on a selected list item. (Part 3) - Ingo Karstein

SharePoint 2010 - Ribbon … becoming a SP2010 developer - Part 3 - Andreas Glasser


Getting Started SharePoint 2010: Developing Ribbon Controls

Introduction to SharePoint 2010 UI Features

The SharePoint Fluent User Interface

Module 10: Creating Dialogs and Ribbon Controls for SharePoint 2010

Codeplex projects

SharePoint 2010 Fluent Ribbon API

Show or Hide SharePoint 2010 ribbon based on SharePoint Groups

SharePoint 2010 Print List Ribbon Button

SharePoint 2010 Custom Ribbon Demo

SharePoint 2010 Ribbon Customization with Web Parts for SharePoint

Happy Coding!

SharePoint Workflows in Three Minutes

After every seminar I give, people come up and say: "you're the SharePoint Workflow guy – explain it to me in 3 minutes". Here's what you need to know:

SharePoint 2010 workflows can:

  • Automate common business steps
  • Help manage long-running processes
  • Route documents and forms
  • Collect feedback, approval, or notifications
  • Provide visual indicators of a process
  • Wash your car …

SharePoint 2010 Standard and Enterprise include a selection of 'out of the box' workflows. These tools help you automate the most common business tasks – sharing and collecting feedback on documents. I see the following workflows used most often:

  • Approval Workflow - Routes the content for approval. You can setup this workflow as serial or parallel plus preset the approvers.
  • Collect Feedback Workflow - Routes the content for feedback. When the workflow completes all the feedback is sent to the originator of the workflow.
  • Collect Signature Workflow - Rather than just sending content for approval, this workflow requires digital signatures on the content.

'Out of the Box' Workflows

'Out of the box' workflows can be added to any SharePoint document library. Simply select a template and pick a name. Your workflow can start whenever a document is added, whenever a document is changed, or both.

Built-in workflow templates make setup a snap

Once your workflow is set up, it will auto-magically run when triggered. Users may also start a workflow manually – for example, James can start an Approval workflow on his report.

Manually start a workflow in SharePoint

When the workflow starts, it will ask the user to set some rules. Who should approve the document? Is there a due date? And my favorite! Send a reminder e-mail if SharePoint doesn't hear from an approver.

Workflow setup screen

Workflows with Visio or SharePoint Designer

When your needs become more complex, SharePoint power users and developers can create custom workflows using drag-and-drop tools. Workflows are composed of simple building block "activities" and conditional logic. Workflows run based on events and conditions affecting a document or SharePoint list item.

If you are comfortable with Visio, you can use familiar tools to snap together a SharePoint workflow. Various workflow actions are available, such as Assign Task, Send E-mail, Create List Item, Collect Data from User, Wait for Timer, and more.

SharePoint Workflow in Visio 2010

Once the workflow is laid out, a tool named SharePoint Designer allows you to further define the workflow and deploy it to your SharePoint site. A simple publishing process helps you link the workflow with a SharePoint List or Document Library

Editing a workflow in SharePoint Designer

Your workflow is ready! It will automatically run whenever a document or list item is added, or users can start the workflow when they need it. Your workflow can even be started from within a Microsoft Office document.

Start a workflow from within an Office document

Were you timing me? In just a few minutes I've explained how simple – yet powerful – workflows can help you automate your business processes. Workflows can be designed to improve almost any aspect of your business. My team at Concurrency has built workflows to manage projects, track engineering change orders, manage customer service, route mortgage documents, automate human resource tasks – even starting a bidding war between vendors during an RFP process.

Let's talk and find out how SharePoint 2010 workflows can change your life!

How to Create Offline Surveys in SharePoint 2010

Using surveys out of the box in SharePoint 2010 give site collection administrators an easy way to gather information from end users, for reporting purposes. Unfortunately, if you need any special features for your surveys, the stock survey functionality quickly falls short. Custom development would then be needed to extend or enhance the functionality needed for surveys.

One such scenario requiring custom development is how to extract survey questions for offline use, along with importing survey responses from a Windows forms-based application back into SharePoint. This blog will detail the steps needed to provide this functionality. I am using SharePoint 2010, along with Visual Studio 2010 Professional Edition.

Extracting Survey Information

The first step in providing surveys offline is to extract questions from a survey. This can be done using event receivers in SharePoint 2010. Ideally you would create an event receiver that would fire when an item in a list is added, updated, or deleted, so you can capture any changes made to the survey questions. I am checking theBaseTypeof the list being added, as the event receiver will fire for other list types:

public override void FieldAdded(SPListEventProperties properties)
// Only process XML Definition file for surveys
if (properties.List.BaseType == SPBaseType.Survey)
ProcessSurveyQuestions(properties.List, properties.Web.Site.RootWeb, properties.Web);
catch (Exception ex)
// Handle exception

Once you've determined that the list is a survey, getting all the information from that survey is just a matter of iterating through the list. There is a field property called SchemaXML that holds all the information for each question in the survey (in XML format), so it is a simple matter to create an XML document that represents all the survey questions contained in a survey:

int x, y;

int fCount = localSurveyList.Fields.Count;

string strFinalXML = "<Survey>";

strFinalXML += "<Title>" + localSurveyList.Title + "</Title>";

strFinalXML += "<URL>" + localSurveyList.DefaultViewUrl + "</URL>";

strFinalXML += "<SurveyURL>" + localSurveyList.DefaultViewUrl + "</SurveyURL><SurveyQuestions>";

// All questions and info are contained in list; just need to iterate through it

for (x = 0; x < fCount; x++)


// Do not process if fields are "Order" or "MetaInfo"

if ((localSurveyList.Fields[x].StaticName != "Order") &&

(localSurveyList.Fields[x].StaticName != "MetaInfo"))


// Fields need to be one of the SPFieldTypes specified in order to process

if (!localSurveyList.Fields[x].ReadOnlyField &&

(localSurveyList.Fields[x].Type == SPFieldType.Text ||

localSurveyList.Fields[x].Type == SPFieldType.Note ||

localSurveyList.Fields[x].Type == SPFieldType.Choice ||

localSurveyList.Fields[x].Type == SPFieldType.MultiChoice ||

localSurveyList.Fields[x].Type == SPFieldType.Boolean ||

localSurveyList.Fields[x].Type == SPFieldType.Currency ||

localSurveyList.Fields[x].Type == SPFieldType.DateTime ||

localSurveyList.Fields[x].Type == SPFieldType.GridChoice ||

localSurveyList.Fields[x].Type == SPFieldType.Lookup ||

localSurveyList.Fields[x].Type == SPFieldType.Number))


// Use SchemaXml to determine characteristics of the question

XmlDocument xmlDoc = new XmlDocument();


switch (localSurveyList.Fields[x].Type)


case SPFieldType.Text:

// question with a single line of text answer

strFinalXML += localSurveyList.Fields[x].SchemaXml;


case SPFieldType.Lookup:

// Cannot use XMLSchema, so individual attributes are created

// and lookup list is retrieved manually

strFinalXML += "<Field Type=\"Lookup\" ";

strFinalXML += "DisplayName=\"" + xmlDoc.DocumentElement.Attributes["DisplayName"].Value + "\" ";

strFinalXML += "Required=\"" + xmlDoc.DocumentElement.Attributes["Required"].Value + "\" ";

strFinalXML += "EnforceUniqueValues=\"" + xmlDoc.DocumentElement.Attributes["EnforceUniqueValues"].Value + "\" ";

strFinalXML += "List=\"" + xmlDoc.DocumentElement.Attributes["List"].Value + "\" ";

strFinalXML += "ShowField=\"" + xmlDoc.DocumentElement.Attributes["ShowField"].Value + "\" ";

strFinalXML += "StaticName=\"" + xmlDoc.DocumentElement.Attributes["StaticName"].Value + "\" ";

strFinalXML += "Name=\"" + xmlDoc.DocumentElement.Attributes["Name"].Value + "\">";

// Load appropriate lookup list, using GUID

Guid listGUID = new Guid(xmlDoc.DocumentElement.Attributes["List"].Value);

SPList localList = localSurveySite.Lists[listGUID];

int fListCount = localList.ItemCount;

strFinalXML += "<CHOICES>";

for (y = 0; y < fListCount; y++)

strFinalXML += "<CHOICE>" + localList.Items[y].Title + "</CHOICE>";

strFinalXML += "</CHOICES></Field>";








strFinalXML += "</SurveyQuestions></Survey>";

// Replace any ampersands with proper syntax

strFinalXML = strFinalXML.Replace("&", "&amp;");

A few things to note are that I skip fields in the field collection with the StaticName of "Order" or "MetaInfo", as those fields do not hold information concerning the survey questions. Additionally, the code snippet is not extracting the information from every question type. This was done for brevity in the article, and it is up to the reader to extend the code to handle all question types.

Also note that for lookup question types, I cannot use the SchemaXML information directly, so I am forced to extract the individual attributes I need separately. This is necessary in order to extract all the possible choices that the survey question presents (as the lookup type is a dropdown, radio buttons or check boxes). The GUID of the lookup list is provided, and I am using that to reference the list in SharePoint, in order to iterate through the items in that list, to save them to my XML string.

Once the XML string is created for all the survey questions, we need to do something with that information. I am saving this XML as a document in a document library in SharePoint that I've created (named SurveyDefinitions), as seen below:



string strSurveyFileName = "FinalSurveyXML.xml";

string onlineSurveySiteRootURL = "http://yoursite.com/";

string surveySiteURL="";

string targetDocLibrary = "Survey Definitions";

using (SPSite site = new SPSite(onlineSurveySiteRootURL))


site.AllowUnsafeUpdates = true;

using (SPWeb web = site.OpenWeb(surveySiteURL))


web.AllowUnsafeUpdates = true;

SPDocumentLibrary lib = (SPDocumentLibrary)web.Lists[targetDocLibrary];

// Upload survey file to document library (for processing)

// Open and read XML data file into a byte array

System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();

byte[] content = new byte[strFinalXML.Length];

content = encoding.GetBytes(strFinalXML);

SPFileCollection col = web.Files;

SPFile file = col.Add(onlineSurveySiteRootURL + surveySiteURL + targetDocLibrary + "/" + strSurveyFileName, content,true);


web.AllowUnsafeUpdates = false;



localSurveySite.AllowUnsafeUpdates = false;


catch (Exception ex)




Displaying Surveys Offline

Now that we have our XML file for a survey, we need to use it outside of SharePoint. We can create a Windows Forms-based application to display the survey questions, and allow the user to enter responses for the survey.

The main challenge of displaying surveys offline is that we don't know beforehand what questions are in a survey, so any application will have to render the controls associated with the survey questions dynamically. Here is where the XML file generated earlier comes into play. We can iterate through the XML file to determine what type of survey questions are contained within, and render the proper controls dynamically in our application:

// Read XML Definition file and iterate through it, rendering controls

XPathNavigator nav;

XPathDocument docNav;

// Open the XML.

docNav = new XPathDocument(ConfigurationManager.AppSettings["XMLDefinitionFilesLocation"] + strSurveyFile);

// Create a navigator to query with XPath.

nav = docNav.CreateNavigator();

// Initial XPathNavigator to start at the root.


// Move to the first child node

nav.MoveToFollowing("SurveyQuestions", "");

// Find the first element (must be 'SurveyQuestions')

if ((nav.NodeType == XPathNodeType.Element) && (nav.Name == "SurveyQuestions"))



yPos = yCtlSpacerValue;

xCtlMaxWidthValue = (pnlContainer.Width – xLeftSpacerValue – xRightSpacerValue);

// Iterate through Fields listed



// Get control info from XML file

strControlType = nav.GetAttribute("Type", "");

strDisplayName = nav.GetAttribute("DisplayName", "");

// Render label control for question text

lblQuestion = new Label();

lblQuestion.Name = "lbl" + strDisplayName;

lblQuestion.Text = strDisplayName;

lblQuestion.AutoSize = false;

// Add the question text label to the form


lblQuestion.Location = new System.Drawing.Point(xLeftSpacerValue, yPos);

lblQuestion.Size = lblQuestion.GetPreferredSize(new Size(xCtlMaxWidthValue, yCtlHeightValue));

yPos += (lblQuestion.Height + yCtlSpacerValue);

switch (strControlType)


case "Text":

bRequired = Convert.ToBoolean(nav.GetAttribute("Required", ""));

if (nav.GetAttribute("MaxLength", "").Trim() != "")

nMaxLength = Convert.ToInt32(nav.GetAttribute("MaxLength", ""));


nMaxLength = -1;

txtField = new RegExTextBox();

txtField.Required = bRequired;

txtField.Name = nav.GetAttribute("DisplayName", "");

if (nMaxLength != -1)

txtField.MaxLength = nMaxLength;

txtField.Multiline = false;

txtField.Tag = strControlType; // Holds the control type

txtField.ThemeName = ConfigurationManager.AppSettings["TelerikControlThemeName"];

txtField.Validating += new CancelEventHandler(ControlFields_Validating);

// Add the new control to the form


txtField.Size = new System.Drawing.Size(xCtlMaxWidthValue, yCtlHeightValue);

txtField.Location = new System.Drawing.Point(xLeftSpacerValue, yPos);

// Set ErrorProvider Icon location to be left of the control

this.errorProvider1.SetIconAlignment(txtField, System.Windows.Forms.ErrorIconAlignment.MiddleLeft);

// Increment y position for next control

yPos += (yCtlHeightValue + yCtlSpacerValue);





} while (nav.MoveToNext());




// Throw exception stating XML Definition file is not in correct format

throw new Exception("Definition file is not in the correct format. File=" + strSurveyFile);


Our application also allows the user to save his/her responses for the survey they have selected, and this response information is saved to a different XML file. Please note that for lookup lists, radio buttons and multiple selection checkboxes, there is a specific format that you must follow when saving your responses to the XML file. This is necessary when importing the survey responses back into SharePoint, which I will describe in the next section.

// This method retrieves the survey question responses (from the controls on the form)

// and saves them to an XML Data file

public static void SaveSurveyResponses(Panel pnlContainer, string strSurveyFile, string strSurveyTitle, string strDestFile, string strCustomerSelected)


string strFinalXML = "<Survey>";

string strDateTimeIdentifier = DateTime.Now.ToString("MM-dd-yyyy-mm-ss");

string strLocalSurveyTitle;

// Strip out unique identifier (after the '_')

if (strSurveyTitle.IndexOf("_") > 0)


strLocalSurveyTitle = strSurveyTitle.Substring(0, strSurveyTitle.IndexOf("_"));

strDateTimeIdentifier = strSurveyTitle.Substring((strSurveyTitle.IndexOf("_") + 1));



strLocalSurveyTitle = strSurveyTitle;

// Retrieve list of survey questions, and calc max label width needed

List<string> strQuestions = SurveyInfo.GetSurveyQuestions(strSurveyFile);

XmlDocument xmlFinalDoc = new XmlDocument();

strFinalXML += "<DefinitionFile>" + strSurveyFile + "</DefinitionFile>";

strFinalXML += "<Title>" + strLocalSurveyTitle + "</Title>";

strFinalXML += "<UniqueName>" + strLocalSurveyTitle + "_" + strCustomerSelected + "</UniqueName><SurveyQuestions>";

#region Create XML for Controls

// All questions and info are contained in controls; just need to iterate through them

foreach (Control c in pnlContainer.Controls)


// Do not process labels

if (!(c is Label))


string strControlType = c.Tag.ToString();

// Single, multiple line textbox question; also number or currency textbox

if (c is RadTextBox)


RadTextBox localControl = (RadTextBox)c;

strFinalXML += "<Field Type=\"" + strControlType + "\" DisplayName=\"" + SurveyInfo.Encode(localControl.Name) + "\" ";

// Question is single line textbox

if (strControlType == "Text")

strFinalXML += "MaxLength=\"" + localControl.MaxLength.ToString() + "\">";


strFinalXML += ">";

// Add field value to XML string

strFinalXML += SurveyInfo.Encode(localControl.Text) + "</Field>";


// Choice – Listbox questions (multichoice lookup question)

if (c is RadListBox)


RadListBox localControl = (RadListBox)c;

// Question is Lookup

if (strControlType == "Lookup")


strFinalXML += "<Field Type=\"Lookup\" ";

strFinalXML += "DisplayName=\"" + SurveyInfo.Encode(localControl.Name) + "\">";

foreach (RadListBoxItem rlbi in localControl.SelectedItems)



//strFinalXML += localControl.SelectedIndex.ToString() + ";#" + SurveyInfo.Encode(localControl.Text) + "</Field>";

strFinalXML += rlbi.Value.ToString() + ";#" + SurveyInfo.Encode(rlbi.Text) + ";#";


// Remove final ";#' from string

strFinalXML = strFinalXML.Remove((strFinalXML.Length – 2), 2);

strFinalXML += "</Field>";





strFinalXML += "</SurveyQuestions></Survey>";

// Replace any ampersands with proper syntax, _x0020_ with a space

strFinalXML = strFinalXML.Replace("_x0020_", " ");

SharePoint 2010 Content Organizer

One of the most powerful features within SharePoint 2010 for document management is the Content Organizer. This feature allows for the automatic routing of documents to different libraries and folders within those libraries based on pre-defined rules.

The core strength of this tool is the management of document libraries and the flow of documents into these locations. Building upon the SharePoint 2007 Records Center, it expands routing to allow for the use of managed metadata as a key document property. Documents can be routed to document libraries within other Site Collections if the Content Organizer is activated in both the sending and receiving Site Collection. A threshold can also be set for the folders within a document library to limit the number of items it can hold. When the threshold is met, a new folder is automatically created and configured with a pre-formatted naming convention.

In order for routing to occur, routing rules must be in place. Creation of routing rules is based on content types and can contain multiple conditions. Using managed metadata in conditions allows for a centralized source to manage large amounts of descriptive document information. Routing rules are stored in the Content Organizer Rules list for ease of creation and editing.

Sample Content Organizer Rule Setting

SharePoint 2010 Content Organizer

Successful use of the Content Organizer depends on several key factors. A few suggestions are as follows:

Gain a keen understanding of how your users organize documents.

Understanding how users organize documents is critical to laying out the technical foundation for routing. Determine the folder structure and folder naming conventions currently used by your organization. Example: If users in the Sales department store documents by client name and then by document type, use this folder structure as baseline for determining if this structure is effective or if another structure is better suited for your centralized document libraries.

Discover the document types and descriptions for documents.

Document types can range from drafts and memos to finalized estimates and proposals. Users also refer to these documents by different descriptions. Example: A proposal could be referenced as "Microsoft Proposal", "Proposal 2-22-10", "Proposal V5", or "Proposal – Final Version". It is imperative to come to a consensus document type and description naming convention for documents. Doing so will make it easier for users to properly tag and describe documents. They will also be able to locate and access these documents quickly and effectively.

Use managed metadata to properly tag documents.

Once you have determined the document type and description naming convention, use these terms to populate the appropriate managed metadata term set. Use managed metadata to control the metadata placed on documents. This ensures that documents will have the correct description and be routed to the correct location. Example: Users open document templates which include metadata for client name and document type. Their choices for client name and document type are determined by the managed metadata values in SharePoint. Once they select the client name and document type, the document will be routed to the appropriate folder.

Create meaningful folder structures and routing rules.

Create a folder structure that reflects how your users should organize and access documents. Be specific as possible and avoid the use of general buckets for documents. Your routing rules should be based on predefined managed metadata. Example: If the client name is "Microsoft" and the document type is "Proposal" route the document to the "Proposals" folder within the "Microsoft" folder.


With the Content Organizer in place, and the logical aspect of where and how you want to route documents determined, great efficiencies come into play as documents are sent to the correct location and users no longer ask the question "Where is that document?".

Permissions and Security in SharePoint 2010

Microsoft SharePoint Server 2010 includes six permission levels by default. You can customize the permissions available in these permission levels (except for the Limited Access and Full Control permission levels), or you can create customized permission levels that contain only the specific permissions you need.

Although you cannot directly edit the Limited Access and Full Control permission levels, you can make individual permissions unavailable for the entire Web application, which removes those permissions from the Limited Access and Full Control permission levels.

The following tables from TechNet illustrate the various permissions for team and non-team sites:

Permission levels for team sites

Permission level Description Permissions included by default
Limited Access Allows access to shared resources in the Web site so that the users can access an item within the site. Designed to be combined with fine-grained permissions to give users access to a specific list, document library, item, or document, without giving them access to the entire site. Cannot be customized or deleted.
  • Browse User Information
  • Use Client Integration Features Open
Read Allows read-only access to the Web site.
  • View Items
  • Open Items
  • View Versions
  • Create Alerts
  • View Application Pages
  • Use Self-Service Site Creation
  • View Pages
  • Browse User Information
  • Use Remote Interfaces
  • Use Client Integration Features
  • Open
Contribute Create and edit items in the existing lists and document libraries.
  • Read permissions, plus:
  • Manage Unsafe Content
Design Create lists and document libraries and edit pages in the Web site.
  • Approve permissions, plus:
  • Manage Lists
  • Add and Customize Pages
  • Apply Themes and Borders
  • Apply Style Sheets
Full Control Allows full control of the scope. All permissions

If you use a site template other than the team site template, you will see a different list of default SharePoint groups. For example, the following table shows additional permission levels provided with the publishing template.

Permission levels for non-team sites

Permission level Description Permissions included by default
Restricted Read View pages and documents. For publishing sites only.
  • View Items
  • Open Items
  • View Pages
  • Open
View Only View pages, list items, and documents. If the document has a server-side file handler available, they can only view the document by using that file handler.
  • Limited Access permissions, plus:
  • View Items
  • View Versions
  • Create Alerts
  • Create mobile alerts
  • View Application Pages
Approve Edit and approve pages, list items, and documents. For publishing sites only.
  • Contribute permissions, plus:
  • Override Checkout
  • Approve Items
Manage Hierarchy Create sites; edit pages, list items, and documents. For Publishing sites only.
  • Design permissions minus the Approve Items permission), plus:
  • Manage permissions
  • View Usage Data
  • Create Subsites
  • Manage Web Site
  • Manage Alerts

Unmanageable security is one of the primary factors which leads to "SharePoint Creep", as users create new sites when they are unsure of the current sites security level. The following guidance is intended to assist with creating a security model that is effective, scalable, and manageable.

  1. Centrally create SharePoint security groups and add Active Directory (AD) groups to the SharePoint groups.

    By creating a SharePoint Security Group and then adding Active Directory (AD) groups to the SharePoint Group, you will essentially encapsulate any AD groups or individuals into one manageable container. This will help prevent "one off" security setting in which the administration time of trying to manage multiple groups or individuals is severely reduced as security is controlled at the container level. This is known as the "group within a group" approach for SharePoint security modeling. The groups should be created at the root of SharePoint Site Collection and then inherited down across sites. Break inheritance only when SharePoint groups should not have permissions to a site and need to be removed. If you break inheritance and create a new group, you have broken the centralization of your security model and lost a large portion of the capability to track which SharePoint groups have been created.

  2. Avoid adding individuals. Use Active Directory groups instead.

    By adding groups from Active Directory to SharePoint, individuals still receive permissions and if they leave a group (ex. an employee changes from Marketing to Sales) they are automatically removed from the sites the group they belonged to had access to. If an individual is added, they will need to be removed at one a time from any SharePoint sites or objects they were given access to. Even if a group has only one individual, try to create a group so that security is set with a role based methodology (ex. there is only one person who undertakes quality control in the organization. They should be added to a Quality Control group with Active Directory, and then the AD Quality Control group should be added to the SharePoint Quality Control Group that has "Contribute" access to the Sales site).

What is Delegate Controls in SharePoint 2010

SharePoint provides a mechanism that allows developers to customize out-of-the-box functionality in pages, without the need for modifying the pages that come with SharePoint. This mechanism is provided with delegate controls. I am going to show you how to create a delegate control that you can use to override the Global Navigation bar at the top of standard SharePoint pages.


First off, it's important to understand what a delegate control is, so we can properly create and apply it in the specific situation we are developing for. A delegate control defines a region in an aspx page that allows the content to be replaced with our custom content. This custom content is created via a SharePoint feature, and when deployed and activated, replaces the standard content at runtime.


The Global Navigation bar is located at the top of all the pages in a site, and the master page uses a delegate control in a placeholder to determine what to render at runtime. Here is the markup for the delegate control:

<SharePoint:DelegateControl runat="server" ControlId="GlobalNavigation"/>

Note that the ControlId value determines what delegate control is, so for our example the Global Navigation bar has a ControlId value of GlobalNavigation.

Once we know the name of the delegate control we are working with, we are ready to write code for our delegate control.

Open Visual Studio 2010 and create a new Empty SharePoint project. Map a folder to the CONTROLTEMPLATES directory in the SharePoint root (the 14 hive). Add a new SharePoint 2010 User Control by right-mouse clicking the CONTROLTEMPLATES mapped directory, from the Solution Explorer. Give the user control an appropriate name (I named it ucGlobNavDelegateControl). Once the control is created, open it in the Designer and add some markup:

Custom Delegate Control

You will also need to create an Elements.xml file, so add a module to the project, which will create the Elements.xml file as part of it (you can delete the Sample.txt file created).

Delete the markup between the Elements tags, and add the following markup:

<Control Id = "GlobalNavigation" Sequence="90"  ControlSrc="~/_ControlTemplates/ucGlobNavDelegateControl.ascx" />

This markup associates the delegate control we are creating with the delegate control placeholder defined in the master pages in a SharePoint site collection. Note that the ControlId value equals GlobalNavigation, which specifies that our custom delegate control is overriding the Global Navigation control. The Sequence value must be less than 100, since SharePoint will render the control with the lowest sequence (the default control is set at 100). The ControlSrc value indicates the location to our custom delegate control.


Deploying our custom delegate control is done using a feature, so create a feature in Visual Studio and name it MyCustomGlobalNavigationFeature. Add the module you created to it, and set the feature scope to Web. Compile and deploy the project to your SharePoint site.

Once your feature is deployed successfully, you should be able to verify that your feature has been deployed and activated in your site by managing the Site Features, under Site Settings. Look for your deployed feature, as shown here:

Activating Feature

If your feature is properly activated, you should now see the Global Navigation replaced by your custom delegate control:

SharePoint Page with Custom Delegate Control


And that's all there is to it! Of course, the example here is basic, so there is a lot more that can be done with delegate controls. I am also including a screenshot of my Solution Explorer in Visual Studio, so you can get an idea of how my example project was structured:

Solution Explorer

How to Add jQuery to SharePoint 2010

In this article I'll show you how to create a nice SharePoint 2010 wsp Feature that you can deploy to any SharePoint site you'd like, and when activated, it will "turn on" jQuery for all of your webs in the site collection (without editing the MasterPage)! Let's get started:

Creating the Project

  1. In Visual Studio, create a new SharePoint 2010 "Empty SharePoint Project"
  2. Name it "jQueryFrameworkFeature"
  3. Enter your local site address and choose "Deploy as a farm solution" in the SharePoint Customization Wizard.

Prepping the Project

You should be looking at a new Empty SharePoint Project. Now, let's prep the project:

  1. Right click on your jQueryFrameworkFeature project node in the solution explorer, choose "Add > SharePoint Mapped Folder…"
  2. Expand the "TEMPLATE" node, choose "CONTROLTEMPLATES" and click OK.
  3. In the Solution Explorer, right click the newly added mapped folder, "CONTROLTEMPLATES" and add a new folder named "jQueryFrameworkFeature"
  4. Add another SharePoint Mapped Folder again, this time chose TEMPLATE\IMAGES

We now have our basic folder structure that we'll need and so the overall project should now look like this:

The next thing we want to do is have a cool icon that will be used in our feature list in SharePoint to set it apart from the other generic looking features. I've created my own that will immediately scream jQuery! when you see it in the list:

Save the image above into your IMAGES/jQueryFrameworkFeature folder.

Adding the jQuery

The next thing that we'll need to do is add a user control to our jQueryFrameworkFeature folder in our CONTROLTEMPLATES folder. Follow these steps:

  1. Right click on jQueryFrameworkFeature folder in our CONTROLTEMPLATES folder, and choose "Add > New Item…"
  2. Under SharePoint > 2010, select "User Control"
  3. Name it jQueryRef.ascx, click the Add button.
  4. Delete the jQueryRef.ascx.cs file (this will also delete the .designer file too, which is OK).
  5. Open the Source code view for the jQueryRef.ascx file and remove every single line of code except for the "<%@ Control …" line (should be the last line of code). You should now have something like this:
  6. Remove the CodeBehind="jQueryRef.ascx.cs" property completely.
  7. Directly after the Control declaration line, add the following line of code:
    < script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.5.1.min.js "></script>
  8. Save, and then close this file.

Getting it on the MasterPage

So you might wonder how we're going to get jQuery onto the MasterPage without editing it. Well as you've seen already, we've got a User Control with our jQuery reference sitting in it. Because of this, we can put this user control into one of the MasterPage's content control areas, specifically the "AdditionalPageHead" area. Here's how:

  1. Right click on your project, choose "Add > New Item…"
  2. In SharePoint > 2010 choose "Module"
  3. Name it "jQueryFrameworkModule"
  4. Within the module we've just added, remove the "Sample.txt" file.
  5. Open the elements.xml file and edit to look like this:

Creating the Feature

In the previous section, when adding the module, you may have noticed that it created a feature for us, named "Feature1." We'll use this to complete the process:

  1. Rename "Feature1″ to "jQueryFrameworkFeature"
  2. Double click on the first node of this feature to edit its properties. You should see in the "Item in the Feature" list on the right our "jQueryFrameworkModule" module.
  3. Enter, for the title, "jQuery Framework"
  4. Enter, for the description, "Enables the user of jQuery framework across pages in the current site collection.
  5. Change the Scope to "Site"
  6. In the properties window in the lower right, find the "Image Url" property, and enter the following URL

If you right click on your project and deploy you will now have jQuery in your SharePoint!

How to Create Custom Content Types with Code for SharePoint 2010

Creating content types is one of the fundamental features of SharePoint, and although you can create your own content types through the user interface of SharePoint, I'd like to show you a couple options on how you can create your own content types using code. I am going to jump right in, so if you need a refresher on what content types are, please review Kelly Rusk's article on content types.

Option 1: Custom Content Types (via a Content Type project)

The first (and probably the easiest) way to create a custom content type is to use Visual Studio 2010 to create a special project type of type Content Type. I'm going to create a custom content type named MovieData, which will have a choice column that will allow selection of a movie genre, when creating/uploading a document.

Launch Visual Studio 2010 and create a new project. Click on the installed project templates to display the templates in the SharePoint 2010 section, and select the template named Content Type. Give the project the name MovieData and click the OK button.

The SharePoint Customization Wizard opens, which will display a series of steps you need to provide information for, so Visual Studio can configure your project properly. On the first screen, enter the URL of the SharePoint site you want this content type to be deployed to. You should also select the Deploy as a farm solution radio button. Press the Next button to continue.

The next screen requires that you choose which content type to inherit the content type from. We want to inherit from the Document content type, so select Document from the dropdown list, and press the Finish button. Visual Studio will now create the project, and we are ready to finish coding our custom content type.

You will notice that for the Content Type project template, Visual Studio creates an elements.xml file as part of the project. This is the only file we will need to modify to complete our custom content type. Open the elements.xml file, and under the ContentType node, set the Name attribute to 'MovieData'. You can optionally set the Description attribute value to whatever you like, and if you wish to have the content type be displayed in a specific content type group, you can modify the Group attribute value. Note that the ID attribute value represents the GUID value of the content type we inherited from (the Document content type).

The next step is to create the Genre column that will be part of our MovieData content type. To do this, add a Field element inside the Elements node, right before the ContentType node. Set the Name and DisplayName attribute values to 'Genre', and make the column a choice column by setting the Type attribute to 'Choice'. You also need to give the field a unique ID value (a GUID), which you can use the GUIDGEN tool to do this for you.

Since the column we are adding is a choice column, we now need to add entries that will be displayed when the user is selecting an entry. Add a CHOICES node inside the Field element, and then add one or more CHOICE nodes inside the CHOICES node.

Once you have this column defined, you must add a field reference so the column can be displayed as part of the content type. Add a FieldRef element inside of the FieldRefs node, as seen here:

Make sure you use the same GUID that you created when you created the Field element, and give the Name and DisplayName attributes a value of 'Genre'. Save the elements.xml file when you are finished.

The project is now ready to be compiled and deployed to our SharePoint site. Right mouse click the project in Solution Explorer, and select Deploy. Visual Studio will compile the project, deploy the content type (as a Feature), and automatically activate it at the site level.

At this point, our custom content type is ready to use, but first we need to enable it in the document library that we want to use it in. I have created a normal document library in my SharePoint site named Movies. Navigate to the Document Library Settings page, and click on Advanced Settings. Enable management of content types, and click OK. This allows us to add our content type for this document library, so we can classify documents with this content type.

When you return to the Document Library Settings page, you will notice that under the Content Types section, there is the Add from existing site content types link. Click on this link to add our MovieData custom content type to this library.

I have also specified that our MovieData content type is the default content type by making it the first in the list of content types (selecting '1' in the Position from Top selection), under the Change new button order and default content type link. Click OK when finished.

That's it! We are now ready to upload (or create) a document to our library, and our content type will be used when uploading/creating the document. You will notice that a dialog is displayed when I upload a document, allowing selection of a Genre metadata field when uploading the document.

Option 2: Custom Content Types (via a blank SharePoint project)

The second way to create a custom content type is to do it with SharePoint object model code. The code will be deployed as a feature also, but the content type will be created when the feature is activated. This has the advantage that if you need to do anything custom to your content type before it gets created, you can handle that with code.

I am going to create the same MovieData content type that I created in Option 1, so you can see the differences in how the content type is created.

Launch Visual Studio 2010 and create a new project. Click on the installed project templates to display the templates in the SharePoint 2010 section, and select the template named Empty SharePoint Project. Give the project the name MovieData and click the OK button. You will notice that Visual Studio only asks you to specify whether the project should be deployed in a farm or sandbox solution (select the farm solution). Visual Studio doesn't ask you for any additional information when creating the project, as it did in Option 1. An empty SharePoint project assumes you will handle all the configurations yourself.

The next step is to create a feature, as this is how we will deploy our custom content type. Right mouse click the Features node in Solution Explorer, and click on Add Feature. This will create a feature in the project and open the designer page for the Feature1.feature file. We want to add code to the events that fire when the feature is activated/deactivated, so in order to do that we need to add an event receiver to the feature. Right mouse click the Feature1.feature file in Solution Explorer, and click on Add Event Receiver:

Visual Studio creates a Feature1.EventReceiver.cs file, which contains the event handlers for the FeatureActivated, FeatureDeactivating, FeatureInstalled, FeatureUninstalling, and FeatureUpgrading events. We are only going to add code to the FeatureActivated and FeatureDeactivating events. Uncomment the FeatureActivated event, and add the following code snippet:

SPWeb thisWeb = (SPWeb)properties.Feature.Parent;
thisWeb.AllowUnsafeUpdates = true;

SPContentTypeCollection contentTypes = thisWeb.ContentTypes;
SPContentType parent = thisWeb.ContentTypes["Document"];
SPContentTypeId parentID = parent.Id;

SPContentType MovieDataCT = new SPContentType(contentTypes[parentID], contentTypes, "MovieData");

string GenreField = thisWeb.Fields.Add("Genre", SPFieldType.Choice, false);
SPFieldChoice genre = (SPFieldChoice)thisWeb.Fields.GetFieldByInternalName(GenreField);
genre.Choices.Add("Science Fiction");

SPFieldLink genreLink = new SPFieldLink(genre);

SPList MovieList = thisWeb.Lists["Movies"];
MovieList.ContentTypesEnabled = true;

SPContentTypeCollection listCTs = MovieList.ContentTypes;

System.Collections.Generic.IList newOrder = new System.Collections.Generic.List();

MovieList.RootFolder.UniqueContentTypeOrder = newOrder;

This code will execute when the feature is activated, and is responsible for all the steps of creating our custom content type. Let's go through each area of the code.

SPWeb thisWeb = (SPWeb)properties.Feature.Parent;
thisWeb.AllowUnsafeUpdates = true;

SPContentTypeCollection contentTypes = thisWeb.ContentTypes;
SPContentType parent = thisWeb.ContentTypes["Document"];
SPContentTypeId parentID = parent.Id;

SPContentType MovieDataCT = new SPContentType(contentTypes[parentID], contentTypes, "MovieData");

The first thing the code does is get a reference to the SharePoint site and set the AllowUnsafeUpdates property, so we can modify the site through code. We then need to reference the Document content type defined, so we can create our custom content type, inheriting from the Document content type. Once we have this, we then create our new content type called MovieData, and add it to the content type collection defined for our SharePoint site.

SPWeb thisWeb = (SPWeb)properties.Feature.Parent;
string GenreField = thisWeb.Fields.Add("Genre", SPFieldType.Choice, false);
SPFieldChoice genre = (SPFieldChoice)thisWeb.Fields.GetFieldByInternalName(GenreField);
genre.Choices.Add("Science Fiction");

The next step is to create the Genre site column that is part of our MovieData content type. This will be a Choice column, so we will instantiate a variable of type SPFieldChoice class to create the column of the proper type. We also need to add some choices to be available, and then finally calling Update() will create the site column on the SharePoint site.

Once the site column is created, we need to link it to our content type. This is done with a SPFieldLink class, specifying the site column to link. The code adds this to the FieldLinks collection of our content type, and then calling Update() on our content type actually creates the custom content type on the SharePoint site.

SPWeb thisWeb = (SPWeb)properties.Feature.Parent;
SPList MovieList = thisWeb.Lists["Movies"];
MovieList.ContentTypesEnabled = true;

Now that our content type has been created, we can now use it in our Movies document library. In order to do this, the code first makes sure that content type management is enabled for our document library, and then adds the content type to the document library. Note that we need the call to the Update() method after setting the ContentTypesEnabled flag to true. If we omit this, then adding the content type to our document library would throw an exception, as content type management has not been enabled.

SPWeb thisWeb = (SPWeb)properties.Feature.Parent;
SPContentTypeCollection listCTs = MovieList.ContentTypes;

System.Collections.Generic.IList newOrder = new System.Collections.Generic.List();

MovieList.RootFolder.UniqueContentTypeOrder = newOrder;

The final step that the code takes is to set our MovieData content type as the default content type for the Movies document library. This is done by creating a generic list variable of type SPContentType, and adding the existing content types for our document library to it. What is important to remember here is the order in which the content types are listed in the generic list variable, as SharePoint considers the first content type in the list to be the default. The code builds this list and replaces the existing list in our document library by replacing the UniqueContentTypeOrder variable. Calling Update() on the RootFolder commits the changes back to SharePoint.

Since we are creating a content type when a feature is being activated, it makes good sense that we clean up after ourselves when the feature is deactivated. Uncomment the FeatureDeactivating event handler, and add the following code snippet:

SPWeb thisWeb = (SPWeb)properties.Feature.Parent;
thisWeb.AllowUnsafeUpdates = true;

SPContentTypeCollection contentTypes = thisWeb.ContentTypes;
SPContentType MovieDataCT = thisWeb.ContentTypes["MovieData"];
SPContentTypeId MovieDataCTID = MovieDataCT.Id;

SPList MovieList = thisWeb.Lists["Movies"];
SPContentTypeId listCTID = MovieList.ContentTypes["MovieData"].Id;

// Delete any list items of our content type first
foreach (SPListItem item in MovieList.Items)
if (item.ContentTypeId == listCTID)


SPContentTypeCollection listCTs = MovieList.ContentTypes;



This code snippet handles all the steps needed to remove the MovieData custom content type, as well as the Genre site column from our SharePoint site. Note that before the content type can be removed, the code goes through the Movies document library and deletes any documents that have been classified with our custom content type.

In conclusion, I've shown you two different options on how to create custom content types with code. The first option is the quickest way to accomplish this, and the second option gives you more control and flexibility, if you need to add any custom functionality or logic when implementing your custom content types.

How to Allow anonymous access to SharePoint application pages in the _layouts directory

Often times, you need to add your own custom application pages (aspx) to SharePoint, which are deployed to the '_layouts' directory. These types of pages could consist of a custom login page, maintenance pages, or even complete web applications that you'll have live in SharePoint. But what if your web application has an authentication provider set up, such as Claims or Windows? In this article, I'll show you how easy it is to allow anonymous access to these pages without forcing a user to log in.

Create the project

  1. In Visual Studio 2010, create a new "Empty SharePoint Project" and name it "MyAnonymousWebPages".
    Click OK
  2. Enter your SharePoint site
  3. Select "Deploy as a farm solution"

Adding the custom Pages

Next, we need to map the 'layouts' directory to our project, so our files are deployed

  1. Right click on the project name 'MyAnonymousWebPages' > Add > SharePoint "Layouts"
    Mapped Folder
  2. SharePoint will automatically add a folder under 'layouts' with the name of our
    project. Right click on this folder >Add > New Item > Application Page
  3. Name the page "HelloWorld.aspx"
  4. Your project should now look like this:

Allowing anonymous access

So far, we haven't done anything special. The steps we've taken is the basic approach
to adding custom application pages. In the following steps, we will inherit from "UnsecuredLayoutsPageBase". This is a base class for application pages that do not require the user to have any permissions.

  1. Open up HelloWorld.aspx.cs
  2. By default, the page is inherited from "LayoutsPageBase". Change the inheritance
    from "LayoutsPageBase" to "UnsecuredLayoutsPageBase".

    public partial class HelloWorld : UnsecuredLayoutsPageBase
  3. Next, we need to override a property of the UnsecuredLayoutsPageBase, to allow the
    anonymous access. Be sure to change the 'getter' to return true

    protected override bool AllowAnonymousAccess { get { return true; } }
  4. Add some content to your markup in the 'PlaceHolderMain'

Deploy the solution

  1. Right click on your project name, and select 'Deploy'
  2. Once deployment is complete, browse to your page by navigating to
  3. See your custom page, like below (notice, by default, the ApplicationPage template
    in Visual Studio is designed to automatically inherit from the master page. You
    could instead replace the markup with markup from a standard aspx page template).

How to Rename the SharePoint Search Databases

If you have created the SharePoint Search Service Application using the GUI in SharePoint you have probably noticed that it has added some ugly GUIDs on the end of your database names. In this blog post I will show you how to get rid of them.

Renaming the Crawl Store and Property database will be pretty easy but the Admin database will take a little bit of work.

Renaming the Crawl Store and Property database

1.Open Central Administration and go to manage service applications. Now select your Search Service Application and hit manage.

2.At the bottom of the page and click modify in the Search topology Section.

3.Click on the Edit Properties for the crawl database.

4. On the Edit Crawl Database Screen go to the Database Name Field. Here you can highlight the GUID and delete it off the end of your database then click ok.

5. You can now repeat steps 3 and 4 for the property database.

Renaming the Search Admin Database

If you try to edit the Admin database you will notice that it is greyed out and you can't change it using the GUI. In order to change it we will need to use the SQL Server Managment Studio as well as powershell.

1. Open SQL server managment studio and find your search admin database. If you are not sure which one is your admin database you can find it in the Search Service application at the bottom of the page.

2. Back up the admin Database. You can do this by right clicking on the database selecting tasks then Back Up…

3. Now right click on on the Database folder and select Restore Database.

4. In the restore database window select From Device. Then click the radio button and select the backup of the admin database you just made. Now that you have the backup selected enter the new name you would like for the database in the to database field. Now you can click OK then click OK again at the database restored successfully screen.

5. Now get back on the SharePoint server and open the SharePoint Managment Shell as Administrator.

6. Now enter the following line into the PowerShell to set the identity of your Search Service Application:

$searchssa=Get-SPEnterpriseSearchServiceApplication -identity "Your Search Service Application".

7. Next we will want to pause the Search Service Application before we add the database. We will do this with the following PowerShell command:


8. Now we are going to add set our newly named database as the Search Admin database. We will do this with the following PowerShell command:

$searchssa | Set-SPEnterpriseSearchServiceApplication -DatabaseName "Your newly named SharePoint Admin DB" -DatabaseServer "Your SQL Server"

9. Now we will want to resume our Search Service Application. We will do this with the following PowerShell command:


10. Now go back to your Search Service Application and check your databases. The GUIDS will all be gone.

11. The database may look like it is no longer in SharePoint but if you run the command you will see that it still is:

get-spdatabase | Select Name, ID

12. Mark the ID for the old database from above then run this PowerShell command:

$olddb=get-spdatabase <ID of old DB>

13. Next we want to delete the old database so we will run the PowerShell command:


14. Now that we have deleted the old admin database from SharePoint the last step is to delete the old database from SQL. To do this we go into SQL Server Managment Studio right click on the old database and select delete.

15. On the next screen check the close existing connections box then click ok.