Data validation in publishing pages in SharePoint 2010

Like many things in SharePoint – this should be simple but turns out it is not. I had a requirement to ensure that a DateTime field (Next Review Date) on the page is equal to or greater than the current date. This field is part of a content type and is included on the page layout for editing.

My first attempt was to add a column validation formula for the Next Review Date column. This worked well when the properties of the page was being edited through the list (i.e. edited as a list item). However, this did not work when the page was being edited as a publishing page (i.e. content editing). If the user saves the page with a bad value in the field, he/she would simply get a Save Conflict dialog and the validation error message is not displayed anywhere. There is no way for the user to work out what’s going on.

My second attempt was to add an ASP.NET validator control (e.g. CompareValidator) to the page layout. The problem here is that all ASP.NET validators (except for the CustomValidator) can only validate certain types of controls, and the SharePoint DateTimeField is not one of them. Try referencing the SharePoint field in the validator and you will get the error below in the ULS:

System.Web.HttpException: Control ‘DateTimeField’ referenced by the ControlToValidate property of ‘validator’ cannot be validated.

My final approach was to tweak the Template of the field on the page layout and using a custom control that inherits from the OOTB one. This is described below. While custom code was required in this instance, this could lead to no code solutions in other scenarios.

Normally the markup of the field on the page layout looks like below:

<SharePointWebControls:DateTimeField FieldName="NextReviewDate" runat="server" />

SharePoint uses Rendering Template to render the markup above. If you open up the file C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\CONTROLTEMPLATES\DefaultTemplates.ascx and search for “DateTimeField” you will find the markup below:

<SharePoint:RenderingTemplate id="DateTimeField" runat="server">
	<Template>
		<SharePoint:DateTimeControl id="DateTimeField" runat="server"/>
	</Template>
</SharePoint:RenderingTemplate>

This means SharePoint will render the DateTimeField using the DateTimeControl.

We can actually override this in our page layout. First, update the markup in the page layout to be:

<SharePointWebControls:DateTimeField FieldName="NextReviewDate" runat="server">
	<Template>
		<SharePoint:DateTimeControl id="DateTimeField" runat="server"/>
	</Template>
</SharePointWebControls:DateTimeField>

Deploy and test to make sure everything still works. Now, I tried adding a Validator control to the Template and reference the DateTimeControl, but this is also not a control that can be validated (doh!).

Luckily, the OOTB DateTimeControl has a MinDate and MaxDate properties, which work quite nicely. So update the markup in the page layout to below and deploy and test:

<SharePointWebControls:DateTimeField FieldName="NextReviewDate" runat="server">
	<Template>
		<SharePoint:DateTimeControl id="DateTimeField" MinDate="30/08/2011" runat="server"/>
	</Template>
</SharePointWebControls:DateTimeField>

This should work very nicely, but obviously the MinDate needs to be today’s date and cannot be hardcoded. You could set the MinDate using the syntax below:

MinDate="<%# DateTime.Today.ToShortDateString(); %>"

However, SharePoint by default will prevent this, and you will get the error message: Code blocks are not allowed in this file. You could modify the web.config to enable code blocks in the page and be done with it, but I wasn’t keen on this.

To workaround this, I created a simple custom DateTimeControl class that inherits from the OOTB one. This class will have 2 custom properties: RestrictToTodayOrGreater and RestrictToTodayOrLess, which when set to true will set the MinDate and MaxDate to today’s date. Below is the complete code:

public class MyDateTimeControl : DateTimeControl
	{
		protected override void OnInit(EventArgs e)
		{
			if (this.RestrictToTodayOrGreater)
			{
				base.MinDate = DateTime.Today;
			}
			if (this.RestrictToTodayOrLess)
			{
				base.MaxDate = DateTime.Today;
			}
			base.OnInit(e);
		}

		///
<summary> /// Sets or gets whether the MinDate will be set to today's date.
 /// </summary>
		public bool RestrictToTodayOrGreater
		{
			get;
			set;
		}

		///
<summary> /// Sets or gets whether the MaxDate will be set to today's date.
 /// </summary>
		public bool RestrictToTodayOrLess
		{
			get;
			set;
		}
	}

Now update the markup on the page layout to use our custom DateTimeControl:

<SharePointWebControls:DateTimeField FieldName="NextReviewDate" runat="server">
							<Template>
								<MyControls:MyDateTimeControl id="DateTimeField" RestrictToTodayOrGreater="true" runat="server"/>
							</Template>
						</SharePointWebControls:DateTimeField>

We will also need to register our control at the top of the page layout:

<%@ Register tagprefix="MyControls" namespace="MyControls" assembly="[strong name goes here]" %>

Finally, we need to add our custom control to the Safe Controls list. Use the Package Explorer in Visual Studio to do this.

That’s it. Go ahead and deploy your solution.

Posted in Page Layout, SharePoint 2010 | Leave a comment

Custom PortalSiteMapDataSource to display full site structure in left navigation in SharePoint

I had a requirement to ensure that all sites within the site collection would display the full site structure (up to the 1st level sub-site) on the left navigation. Given the site structure below for example:

Home
   Site 1
      Site 1.1
      Site 1.1.1
   Site 1.2
      Site 1.2.1
   Site 2
      Site 2.1
         Site 2.1.1
      Site 2.2

For any site underneath Site 1, the following should be shown on the left nav:

Site 1
   Site 1.1
      Site 1.1.1
   Site 1.2
      Site 1.2.1

For any site underneath Site 2, the following should be shown on the left nav:

Site 2
   Site 2.1
      Site 2.1.1
   Site 2.2

This can be achieved by tweaking the StartingNodeOffset property of the PortalSiteMapDataSource. The PortalSiteMapDataSource is a SharePoint control that typically sits on the masterpage and provides data for the menu controls on the page. See http://blogs.msdn.com/b/ecm/archive/2007/02/10/moss-navigation-deep-dive-part-1.aspx for more information about this control and other important elements related to navigation in SharePoint.

When the StartFromCurrentNode = true and StartingNodeOffset = -1, the first node in the left nav will be the parent of the current site. If StartingNodeOffset = -2, then the first node is the parent of the parent of the current site, and so on.

The problem however is OOTB the PortalSiteMapDataSource is defined in the masterpage and hence the StartingNodeOffset is static and the same for all sites. If you set it to -2, it will work for site 2.1.1 but will not work for site 2.1 or 2.1.1.1 for example.

To address this you can developed a custom PortalSiteMapDataSource which will inherit the OOTB class. It is actually relatively simple.

Step 1: Create the custom PortalSiteMapDataSource

In this class we override the GetHierarchicalView method and calculate the ‘level’ of the current site and adjust the StartingNodeOffset property accordingly.

public class MyPortalSiteMapDataSource : PortalSiteMapDataSource
{
	protected override HierarchicalDataSourceView GetHierarchicalView(string viewPath)
	{
		int siteLevel = GetCurrentWebSiteLevel(SPContext.Current.Web);
		this.StartingNodeOffset = siteLevel == 0 ?
			0 : 0 - siteLevel + 1;
		return base.GetHierarchicalView(viewPath);
	}
	private int GetCurrentWebSiteLevel(SPWeb currentWeb)
	{
		//We can't really work out the level from the URL because the URL
		//might contain managed path that is not part of the site collection
		//(e.g. http://server/ or http://server/sites/).
		int level = 0;
		var tempWeb = currentWeb;
		while (!tempWeb.IsRootWeb)
		{
			level++;
			tempWeb = tempWeb.ParentWeb;
			//Official guidance from MS is that we do not need to call Dispose on SPWeb.ParentWeb.
		}
		return level;
	}
}

Step 2: Update the master page to use the custom PortalSiteMapDataSource

Remember to never modify the OOTB master pages. Create a copy of the master page (e.g. v4.master, or nightandday.master) and add the following to the top of the page to register our custom PortalSiteMapDataSource as a user control:

<%@ register tagprefix="MyNavigation" namespace="MyProject"
       assembly="MyProject, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f7f490906818fff6" %>

Tip: To quickly get the strongly name of your assembly see this post: http://blah.winsmarts.com/2009-12-SharePoint_Productivity_Tip_of_the_day.aspx. This is one of the first things I setup on my dev environment.

In the master page search for (this is from nightandday.master):

<PublishingNavigation:PortalSiteMapDataSource
			            ID="SiteMapDS"
			            runat="server"
			            EnableViewState="false"
			            SiteMapProvider="CurrentNavigation"
			            StartFromCurrentNode="true"
			            StartingNodeOffset="0"
			            ShowStartingNode="false"
			            TrimNonCurrentTypes="Heading"/>

The key here is the SiteMapProvider=”CurrentNavigation” part. Change this to

<MyNavigation:MyPortalSiteMapDataSource
ID="SiteMapDS"
runat="server"
EnableViewState="false"
SiteMapProvider="CurrentNavigation"
StartFromCurrentNode="true"
StartingNodeOffset="0"
ShowStartingNode="true"
TrimNonCurrentTypes="Heading"/>

The key here is changing from the OOTB class to our class. We also need to set ShowStartingNode to true.

Step 3: Register custom PortalSiteMapDataSource as safe control

This is the final step. If you have built the custom PortalSiteMapDataSource in its own project and reference it from your main project, then you can use Visual Studio’s Package Explorer to add the project to the package and add our custom PortalSiteMapDataSource as a safe control.

If you have everything in one single project, then you will have to manually edit the manifest template of the package to include the PortalSiteMapDataSource as a safe control as Visual Studio does not seem to pick it up automatically.

That’s it – go ahead and deploy your solution.

Posted in Branding, SharePoint 2010, Uncategorized | 1 Comment

Gotcha when working with master pages and page layouts in SharePoint Designer and Visual Studio

A common thing to do when editing master pages and page layouts in SharePoint is to edit them in SPD and then copy the source code to Visual Studio for check-in to source control. There is a gotcha here.

When SPD is used to edit a master page/page layout, it will insert the tag below into the <% Page %> directive:

meta:webpartpageexpansion=”full”

This tag causes the page to be recognised as having been customised, i.e. unghosted. The problem is that when a page layout/master page is provisioned (via the Module feature element), and the file already exists at the targeted location, it will not be updated if it has been customised.

So by copying the source code as is (i.e. with the above tag) from SPD to VS and then build and deploy, our files will automatically be unghosted on deployment. This means that the next time you make a small update to the file and deploy again, the file will not be updated. This can be particularly annoying in dev cycles where you typically make big changes in SPD and minor tweaks in VS.

So, to avoid this, go ahead and copy the source code from SPD to VS, but be sure to remove the tag above from the code in VS.

Posted in Branding, SharePoint 2010, SharePoint Designer | Leave a comment

Woohoo! I’m now certified!

..in SharePoint 2010 Application Development :).

Just passed the TS 70-573 exam.

Next in sight is the MCPD in SharePoint 2010 app dev :).

Posted in Stuffs | 2 Comments

Making web requests to external sites from SharePoint

In most enterprise settings you will need to go through a proxy server to connect to external sites. In Windows the proxy settings in IE actually affects the apps on that machine. If you write a console app to test your code for example, it will work fine and able to connect to the external site (provided proxy settings in your IE are correct).

Copy the same code to SharePoint and it stops working. This is because by default SharePoint is not configured to use the proxy settings in IE. To get this to work there are 2 options.

Option 1: Modify SharePoint’s web.config to specify the proxy

  • Edit the web.config of the SharePoint web app.
  • Search for the defaultProxy element. It will look like below
<system.net>
    <defaultProxy />
</system.net>
  • Change it to
<system.net>
    <defaultProxy>
      <proxy
        usesystemdefault="false"
        proxyaddress="http://123.456.78.9:1234"
        bypassonlocal="true"
      />
    </defaultProxy>
</system.net>

Option 2: Set the proxy in code for the HttpWebRequest object

WebProxy proxy = new WebProxy("123.456.78.9", 1234);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestUrl);
request.Proxy = proxy;

HttpWebResponse response = (HttpWebResponse)request.GetResponse();

If your proxy requires credentials you will get the exception: (407) Proxy Authentication Required. To fix this add these lines:

if (request.Proxy != null)
{
	request.Proxy.Credentials = CredentialCache.DefaultNetworkCredentials;
}

Update regarding the (407) Proxy Authentication Required exception:

Setting the proxy’s credentials as above worked for me on my dev machine, which was a single-server farm. It did not work when I deployed the code to a multi-servers farm environment, and I kept getting the (407) Proxy Authentication Required exception. I found that wrapping the code that makes the request in a SPSecurity.RunWithElevatedPrivileges block solved the issue. Can’t explain what’s going on, but it works now :).

Posted in SharePoint | 2 Comments

IgnoreIfAlreadyExists attribute when provisioning files in SharePoint

The IgnoreIfAlreadyExists attribute of a File element when provisioning a file can be mysterious. Most of the posts on the net are saying that:

  • When this attribute is set to FALSE, an error will be thrown if the file already exists at the targeted location
  • When set to TRUE, an error will not be thrown if the file already exists, but the existing file will not be updated.
I tested this with ghostable files and found that this is not quite true. My tests showed that:

  • When set to FALSE, an error is raised if the file already exists
  • When set to TRUE, no error is raised, and the existing file is updated if it has not been unghosted (e.g. customised post deployment using SharePoint Designer). The file is not updated if it has been unghosted/customised.
I tested this with files deployed to the master pages and page layouts gallery and the Type attribute was GhostableInLibrary.
Posted in SharePoint 2010 | 1 Comment

Should I call Dispose on SPWeb.ParentWeb?

I was coding away and wondered if I should call Dispose the parentWeb object below?

SPWeb parentWeb = web.ParentWeb;
parentWeb.Dispose(); //Should I?

I found some official guidance on this and thought I’d blog it here, partly to help jolt my memory later on.

The short answer is: No, calling Dispose() is not required on SPWeb.ParentWeb:

From MSDN (http://msdn.microsoft.com/en-us/library/aa973248(v=office.12).aspx):

“SPWeb.ParentWeb Property

Updated Guidance

An earlier version of this article recommended that the calling application should dispose of the SPWeb.ParentWeb. This is no longer the official guidance. The dispose cleanup is handled automatically by the SharePoint framework.”

—————————————————–

Update: What about SPSite.RootWeb – should I call Dispose() on that? Official guidance (also in the above link) is: NO. Do not call SPSite.RootWeb.Dispose().

Posted in SharePoint 2010 | Leave a comment

Weird rendering behaviour in SharePoint with self-closing DIV tags

I was doing branding in SharePoint 2010 and noticed that SharePoint will render elements declared after a self-closing DIV as children of that DIV. So, if my master page has something like this:

<div style="float: left">Hi world</div>
<div style="clear: both"/>
<div>new section</div>

The page will be rendered as:

<div style="float: left">Hi world</div>
<div style="clear: both">
   <div>new section</div>
</div>

Changing the clear DIV to be <div style=”clear: both”></div> and the page rendered as expected.

Posted in Branding, SharePoint | Leave a comment

Weird error deploying SharePoint workflow using Visual Studio

It was working all fine, until one day I started getting the error below when deploying the workflow using Visual Studio:

Error occurred in deployment step ‘Activate Features’: Unable to locate the workflow’s association data. To restore the association data to the workflow, restart the workflow settings wizard by selecting the workflow node in Solution Explorer and then clicking the ellipsis button (…) on one of the properties in the Properties window that has an ellipsis button.

Not sure exactly what’s going on, but selecting the workflow SPI in the Solution Explorer, then in the Properties pane, set Auto Associate to False fixes the issue.

Posted in Visual Studio 2010, Workflow | Leave a comment

‘WARNING: Template is not found and is not applied’ when using custom WebTemplate with PowerShell

WebTemplate is the new way of doing site definitions in SP2010 (see this for a good overview: http://sharepointchick.com/archive/0001/01/01/site-definitions-vs.-webtemplates.aspx).

Where you need to refer to the custom WebTemplate, you’d use the ‘{FeatureID}#WebTemplateName’ syntax, e.g:

{56C8E551-565B-482A-A038-CCC18A878607}#MyWebTemplate

I however have found that this does not work with the PowerShell New-SPWeb cmdlet (when used with the -Template parameter). You’d get the error message: WARNING: Template is not found and is not applied.

To get around this, call the New-SPWeb without the -Template parameter. Then call ApplyWebTemplate on the resulting object. E.g.:

$web = New-SPWeb $url …

$web.ApplyWebTemplate($template)

Posted in PowerShell, SharePoint 2010, WebTemplate | 4 Comments