Thursday, 17 July 2008

Master the order of WebConfigModifications

There are plenty of blogs describing WebConfigModifications so I'm just going to stress what I feel are the two most important properties on the WebConfigModifications class.

The name and the path.

There are plenty of descriptions of these properties but the most important thing to note is that when combined they form the xpath used to locate the node on which to operate.

Therefore, the following 2 examples have the same effect.



This is because when the name and path attributes are combined they result in the same xpath.

configuration/system.web/httpHandlers/add[@verb='*' and @path='*.asmx']

The reason I am stressing this so much is that I've noticed lots of complaints about how the sequence attribute does not appear to affect the order in which the modifications are applied. This is because the sequence attribute is only taken into account when the names of the modfications are the same. This isn't much use. This means that if I try to insert the following lines into the web.config

<clear />
<add verb="*" path="*.asmx" />
it would always appear

<add verb="*" path="*.asmx" />
<clear />
because alphabetically the names are

add[verb='*' and path='*.asmx']
clear
Because the names are different the sequence would be ignored and the modifications would be applied alphabetically. Therefore adds would come before clears and removes would be last.

That sucks!! But help is at hand. Now that we now the name attribute just forms part of the xpath to locate the element in the web.config file we can use this to our advantage.

The earlier examples formed the xpath

configuration/system.web/httpHandlers/add[@verb='*' and @path='*.asmx']

Well lets alter this example (or will we). If we add a condition to httpHandlers but make sure it always equates to true, the xpath would return the same node but the name used in the WebConfigModification construct would be slightly different.

configuration/system.web/httpHandlers[1=1]/add[@verb='*' and @path='*.asmx']

The condition always returns true and will be ignored but lets look at how this changes the code.



The names are now (in alphabetic order)

httpHandlers[1=1]/clear
httpHandlers[2=2]/add[@verb='*' and @path='*.asmx']

and a therefore will be applied as wanted/expected.

<clear />
<add verb="*" path="*.asmx" />

Monday, 31 March 2008

Execute entire page with elevated privileges

I recently tried to make an entire page run under elevated privileges (just to see if I could). The idea was to override the IHttpHandler ProcessRequest method, elevate the privileges and then continue running the page.

This isn't something I would recommend doing. The idea came to me whilst trying to resolve a different issue with Form Digest during code executed under RunWithElevatedPrivileges. The solution to which can be found here Form Digest and SPSecurity.RunWithElevatedPrivileges.

However, I did manage to elevate the privileges of the entire page and this is how I did it.



The ProcessRequest method implements the IHttpHandler interface and is the entry point into the page. What I discovered is that both the SPContext class and the SPControl class both depend on objects initialised in the context.Items collection.
Elevating the privileges at this point means that the form digest control will be created in the context of the privileged account.

One issue that may arise is that client script sometimes updates the FormDigest value this could potentially invalidate the digest.

Leave a comment with your thoughts.

Form Digest and SPSecurity.RunWithElevatedPrivileges

A colleague of mine recently had an issue when running code using SPSecurity.RunWithElevatedPrivileges. The SPWeb's AllowUnsafeUpdates attribute had been set to true but an error was still occuring when calling methods that checked the FormDigest of the current page.

I found an article that said the answer to this is to disable the FormDigest settings on the web application using

SPSite.WebApplication.FormDigestSettings.Enabled = false

This means that when the form digest is validated it will always report that it is valid. My opinion on this though is that it isn't very safe. If you're creating a page that runs code under elevated privileges the last thing you want to do is introduce the ability to bypass form digest.

The Solution
That was when I discovered that the form digest is only validated once per request and then a flag is set in the page's context. Therefore, if you call the ValidateFormDigest method on the SPWeb object. This can easily be done using

SPUtility.ValidateFormDigest()

This will validate the form digest and cache the result ensuring that when the ValidateFormDigest is called within the SPSecurity.RunWithElevatedPrivileges wrapper it will always validate.

This solution means that FormDigest on the page will not be comprimised and the code will still run. You will still need to set AllowUnsafeUpdates to true.

Also for a bit of fun I decided to see if it was possible to run the entire page under elevated privileges. I'm not sure if you'd ever want to do this but this is what I came up with. Execute entire page with elevated privileges

Wednesday, 19 March 2008

Open Containing Folder in Search Results

I was recently asked if it was possible to display a "Open containing folder" link in search results when the returned result was a document. My initial thought was that as seen as search results are created by applying a xsl stylesheet to some xml it shouldn't be a problem. I'll just modify the xsl in the search results web part...

Ah, right, ok then ...

I became stuck when I tried to determine the url of the containing folder. I originally intended to call some script embeded in the xsl that would work out the url but then I discovered that the xsl transformation in a DataViewWebPart, from which the search results web parts inherits, uses XSLTSettings.Default which disables scripting.

Then I got an idea. I would create a ficticious element, like the ie:menuitem elements sharepoint creates for the drop down menus, called ie:link with an attribute called url that I assign the results url to. This output is ignored by the browser but then at the end of my xsl stylesheet I render some javascript onto the page that returns all the ie:link elements and creates a link to the containing folder of the url (via a bit of string manipulation) and substitutes it back into the page.

To make this link only appear when rendering documents the output of the ie:link element is rendered inside a xsl:if element that checks the isdocument element.





Download sample xsl for search results web part.

Monday, 10 March 2008

How to customise the SharePoint application.master file

Complete with source code.

So you want to customise your site. You create a master page; you even create your own theme. Just look at your perfect, beautiful site. Then you realise that every time you view a page in the layouts directory your master page is ignored.

This is because all these pages use the /_layouts/application.master master page. So how does one change this setting? In short the answer is you don't. Microsoft do describe two methods for cusomising application pages in the Layouts folder but both are lacking. Each method has advantages and over the other but neither satisfy my requirements.

I'd like to be able to
  • specify an alternate master page for any site (SPWeb) and have all sub-webs inherit the master page
  • configure the url to the master page via the UI
  • still use the uncustomised version of the application.master file


There are several components to the solution of this issue which can be download at the end of this post (including source code).

Changing the Master Page
In order to change the master page we need a way to check every page requested to see if we need to change the master page. To do this we will use a HttpModule. This will allow us to inspect the page being requested and if required attach an event to modify the master page.

The HttpModule will get the page handler and check the MasterPageFile property to see if it is set to the application.master. If it is, it will check the current web's property bag to see if an alternate master page has been specified. If no alternative is set, it will check the parent webs and inherit the setting. If an alternate is found an event handler will be attached to the page's init event that will then change the master page.

Configuring the Master Page
A custom action will be used to create a link in the Site Administration section of the site settings page. This will load a new application page that allows a site admin to set the alternate url for the master page that will be stored in the current web's property bag.

Sticking it all together
Nobody likes fixes that require manual changes to the file system and to the web.config files, so the aim is to create a single web application feature the can be installed, deployed and activated. The HttpModule requires modifications to the web.config file, this will be handled via a feature receiver that applies the web config modifications.

The feature will also install the application page that allows the site administrator to modify the URL to the alternate master page and add a link to the page into site settings under the Site Administration section.

To try this solution download the SharePoint solution file and install and deploy it on your farm. In Central Administration go to Manage Web Application Features and activate the Application Master Page feature on the required web application. After doing this if you go to the site settings page of any site on the web application you will see an Application Master Page link. Change the url to the location of your new master page.

Finally the Master Page
To test this solution you may wish to simply copy the application.master and make a small alteration so that you can see the difference. My recommendation for a full solution would be to create master pages using the application.master file as a template and deploy them to the layouts folder as part of your solution.

Download the solution here seed.hf2.wsp.

Download the source code here seed.hf2.zip

Please leave a comment if you found this post helpful or if you have any suggestions on how to improve it.

Tuesday, 19 February 2008

Fix For Bug With CustomAction (with source code)

I recently overheard a colleague discussing an issue with using CustomAction elements to extend the UI in MOSS, details of his issue can be read here.

http://chrissyblanco.blogspot.com/2007/12/bug-with-customaction-in-actions-menu.html

To sum up, if you define a CustomAction as part of a feature and use tokens in the URL e.g. <UrlAction Url="~site/Test.aspx?List={ListId}"/>, the custom action will work fine in a document library. However, if you add a web part representing the same document library to another page the tokens in the url do not get replaced. This is a very irratating bug.

The fix.
The only way I could see to fix this, was to replace the tokens using javascript. The menu options are available from the dom and the webpart also creates a context object in javascript to represent the displayed list.

The challenge.
To fix every occurance in a web app.

The idea.
Control Adapters. I simple idea but it presented a few difficulties to get this working in SharePoint.

The solution.
The SharePoint solution contains a WebApplication feature that when activated submits a JobDefinition to modify the compat.browser file already provisioned in the web app. The modification is a single control adapter entry to render all listviewwebparts using my control adapter.

The control adapter then allows the listviewwebpart to render as normal and then renders some javascript afterwards that fixes the rendered listviewwebpart.

Download the solution here seed.hf1.wsp. After installing and deploying the solution, run

stsadm -o activatefeature -name seed.hf1 -url <target url>

Download the source code here seed.hf1.zip

More
I am planning to blog in more detail about the techniques used in this solution, but I want to break them down into a small series of more detailed blogs. In the meantime, have a look through my source code and please leave any question or comments.

Friday, 30 November 2007

Applying a theme to SPLongOperation

At some point in SharePoint development you'll probably write some code that takes a long time to execute and you'll run into this fella, the SPLongOperation.

Unfortunately though, you may feel let down when you run your code and the page displayed is not branded like the rest of your site.

This is because no theme is applied in the html output, but don't despair, help is at hand. The property LeadingHTML is exactly that, HTML, so I used it to inject a stylesheet link element that references the stylesheet for the current theme.


string style = @"<link rel=""stylesheet"" type=""text/css"" href=""" + currentWeb.ThemeCssUrl + @""" />";

SPLongOperation op = new SPLongOperation(this);
op.LeadingHTML = style + "[Enter your message here]";
op.Begin();



After doing this I noticed a flicker at the top of the screen. This was because of the comment tags being rendered by the SPLongOperation. To fix this I also included a style element to hide the element that flickered.


string style = @"<link rel=""stylesheet"" type=""text/css"" href=""" + currentWeb.ThemeCssUrl + @""" /><style>td.ms-globalbreadcrumb {display:none;visibility:hidden;}</style>";