in

SharePoint Blogs

The Best Place for SharePoint-related Blogs

Tech MOSS Team

  • Waldek Mastykarz moved to http://blog.mastykarz.nl

    A bit more than a half year ago, when I decided to start blogging, I wasn't sure whether it would going to work. Most of all I was afraid about the number of interesting articles I would write and finding the right balance between my work, this blog and my personal life. That is why I initially decided to start this blog together with my two colleagues: Erik and John. We have decided to support each other in continuing providing of new and interesting content. If one of was busy the other one would take it over.

    Half year of blogging isn't that much I admit. Yet I have succeeded in dividing my time into work, my hobbies and my personal life. By getting more confidence into blogging I have decided to start my own blog at http://blog.mastykarz.nl. Next to starting a blog under my own name, and on my own domain (what sometimes provide you just the right amount of flexibility), my decision has good influence on the 'Innovation matters' program running here at Imtech ICT Business Solutions since a few months. More about the program and our work coming really soon.

    So far I have migrated all my articles from this blog so you will find all the articles on my new blog as well. Furthermore I'm going to pick it up just where I stopped: I'm going to post the new version of the SharePoint Solution Generator exporting Lists to List Definitions and continue sharing my experience with SharePoint and related technologies.

    This is my last post at Tech MOSS Team blog. Although this blog with stay available I'm not really sure for how long. I have already asked Dustin Miller to publish my new blog on SharePoint blogs so my articles will still be accessible to all of you subscribed to the SharePointBlogs.com feed.

  • Inconvenient SharePoint 2007 localization

    SharePoint 2007 has built-in support for Resource files helping you achieve localization. Using these files you are able to set localized labels for the resources being provisioned. Furthermore you are able to use Resource files within your assemblies to support localization on run time. Yet there is something wrong with localization in SharePoint 2007...

    One of the requirements for the project I'm currently working on is that the solution must be built on top of Dutch version of SharePoint 2007. Just to spare myself some unpleasant surprises I have decided to use that particular locale from the very beginning of the project. I got really surprised when I got a vague exception in Dutch while activating one of the Features we deliver standard with our projects. After a little research I have found out the reason of the exception: I have tried to access the Master Page Gallery using SPWeb.Lists["Master Page Gallery"]. However, since I was working with the Dutch locale it didn't exist. There was that strange thing called "Galerie met basispagina's" instead.

    Looking for a solution I took a look at the SharePoint object model. SPListCollection gives you three ways to obtain a list: you can use either the ID (GUID), list index (???) and the list name. It's not really convenient as all these methods use parameters which refer to variable values. Of course you could try loading the Dutch resource file shipped with the Dutch language pack for SharePoint and then retrieving the "Galerie met basispagina's" string - it just seems a little too much to me. Why you can't get a list using the only constant information like... it's URL?!

    If you take a look at the SPList class you will find that there is no Url property. Instead there is this thing called RootFolder (SPFolder) which does contain a Url property which contains the site relative url of the particular list - _catalogs/masterpage in case of "Galerie met basispagina's".

    Recently I have started working with Extension Methods. Briefly: they allow you to add a custom method to an existing type, for example: SPListCollection.GetListByRootFolderUrl(string rootFolderUrl). Personally I think it's a great possibility to extend existing object models allowing you to work with the code in a more natural way.

    The solution

    public static SPList GetListByRootFolderUrl(this SPListCollection lists,
    string rootFolderUrl) { SPList list = null; foreach (SPList l in lists) { if (String.Compare(rootFolderUrl,
    l.RootFolder.Url,
    StringComparison.CurrentCultureIgnoreCase) == 0) { list = l; break; } } return list; }

    The solution is really straight-forward: just iterate through all available lists in the SPListCollection and return the one which has the RootFolder Url which corresponds to the rootFolderUrl parameter. If you implement the above method in a public static class, you will be able to use it like SPWeb.Lists.GetListByRootFolderUrl("_catalogs/masterpage") what will solve your SharePoint 2007 localization problem... this time.

  • Inconvenient SharePoint Solution Generator

    While working with the SharePoint Solution Generator (a utility which ships with Visual Studio 2005 extensions for Windows SharePoint Services 3.0) I have stumbled upon an interesting by-design feature which affects the way you develop Features - at least if you're using or planning to use the SharePoint Solution Generator (SPSolGen).

    SPSolGen allows you to create a List Definition based on an existing list. It is extremely useful since the CAML schema definition is almost unreadable if you work with complex lists. What SharePoint Solution Generator does is it allows you to select an existing list using a GUI and then creates for you a Visual Studio 2005 project which contains the exported List Definition wrapped up in a SharePoint Feature.

    In my previous article I have described the problems I have faced while using SharePoint Solution Generator on my development environment using Visual Studio 2008. Briefly: SharePoint Solution Generator doesn't work at all. While working on a solution for this problem I have had a look at the code of SharePoint Solution Generator to figure out the way it works.

    After I have finished my solution I have started it. Exporting a list created using one of the standard List Definitions (like Documents, Custom List, etc.) worked perfectly. Then I have added the exported List Definition into my existing ContentDefinition Feature which wraps up provisioning custom Columns, Content Types and List Definitions. After creating a list based on my newly exported List Definition I have noticed that I'm not able to export it using SharePoint Solution Generator anymore. An export resulted each time in an exception: Object reference not set to an instance of an object. After some research I have found out the reason of this error.

    In my previous article I have mentioned that SPSolGen modifies the Schema.xml after it has been exported. These modifications are required in order to make the List Definition work. At some point SPSolGen checks to which Feature the List Definition belongs. This information is obtained using the XmlNode.SelectSingleNode method which, I have figured out, was the source of the exception I have faced.

    At the point of deployment my Feature contained the definition of custom Columns, Content Types and List Definitions in this particular order. The SelectSingleNode method, used by SharePoint Solution Generator for obtaining Feature information, retrieves the first node which complies to the given XPath parameter. In case of my Feature the Columns definition instead of the Element Manifest containing my List Definition. As the Columns definition doesn't contain the ListTemplate element, the export method throws the exception I have mentioned before. Turning the Element Manifests around and putting the List Definitions before Columns solved the problem eventually. The Element Manifest containing the List Definition has to be the first Element Manifest defined in your Feature in order to be able to export a list based on this List Definition using SharePoint Solution Generator.

    Such an approach has a major drawback: you are not able to provision multiple List Definitions from one Feature. Both methods for obtaining Feature and List Definition information use the SelectSingleNode method which retrieves the first node selected by the passed XPath query. This fact changes the approach to the custom development in SharePoint 2007 as it requires you to define a Feature for each List Definition you want to provision.

    In my previous article I have mentioned that I will try to publish a custom StsAdm command allowing you to export existing lists to List Definitions in development environments using Visual Studio 2008 as soon as possible. Before the release I will try to think off a better alternative to the SelectSingleNode method which will provide the developers some more flexibility. Feel free to comment if you have any ideas or suggestions.

  • Exporting List Definitions in a development environment using Visual Studio 2008

    Visual Studio 2005 extensions for Windows SharePoint Services 3.0 (VSeWSS) ship with a useful tool called SharePoint Solutions Generator. One of its features I use most often is creating a List Definition based on an existing list. Since the CAML schema of a list isn't really straight-forward - especially if the list uses complex settings and multiple custom views it's almost undoable to create such schema manually. Another possibility could be using List Templates instead of List Definitions. List Templates are unfortunately impossible/difficult to customize and should be used, in my opinion, in SharePoint configuration scenarios when there is no other choice. SharePoint Solution Generator comes very useful if you want to work with List Definitions but you don't want to type all the CAML yourself.

    VSeWSS has however one major flaw: it doesn't work on development environments using Visual Studio 2008. First of all the extensions will not even install as the setup checks the Visual Studio 2005 dependency. Even bypassing the setup and copying the installed version of SharePoint Solution Generator to a development environment using VS 2008 doesn't work: SharePoint Solution Generator throws an exception during the start. The fact that there is no tool for exporting an existing list to a List Definition has suddenly become a serious risk in the project I'm currently working on. Installing VS 2005 next to VS 2008 might be a solution. I haven't tried it personally because it simply didn't seem right to me: installing a huge IDE in order to be able to run a tiny (yet very useful) utility.

    While exploring the possible solutions I have found out that SharePoint Solution Generator exports lists to List Definitions using the /_vti_bin/owssrv.dll. The call is very simple: http://yoursite/_vti_bin/owssrv.dll?cmd=ExportList&List=<List GUID>. This call returns the contents of the Schema.xml file of a List Definition. The list forms (AllItems.aspx, NewForm.aspx, etc.) can be retrieved using the Files property of a list. Further research has proven unfortunately that the exported Schema.xml doesn't work: creating a list based on this schema results in an exception. Looking deeper into SharePoint Solution Generator I have noticed that the tool alters the exported Schema.xml after it has been exported. It's quite odd that the WSS team has created a functionality for exporting the Schema.xml which cannot be exported unless altered!

    Eventually I have succeeded in wrapping it all together into a working package. I have created a custom StsAdm command called ocdexportlist (ocd stands for One Click Deployment - the development strategy we have designed and developed together with some custom tools. More about OCD coming soon). I will try to publish the command before I go to the Microsoft Office System Developer Conference 2008. Stay in touch for updates.

  • Configuring SSL in SharePoint 2007 development environment

    A SharePoint 2007 development can get quite complex depending on the business case and requirements of your customer. Last year I have worked on a few SharePoint 2007 solutions. During the development I have noticed that it is extremely helpful if you know how the customer's infrastructure will look like. It will help you even more if you will configure your development environment to resemble the customer's infrastructure as much as possible.

    One of the things you should definitely consider is working with anonymous access and SSL support from the very beginning if applicable because they have major impact on the custom code you might need to create. Examples of the things you should be considering are Regular Expression for url parsing and privileges elevation if required to access some of the SharePoint properties. Finding out that your solution doesn't work in the real environment might be painful - especially if it's after it all has been deployed.

    Setting up anonymous access in SharePoint 2007 is really straight forward and can be done by turning on two checkboxes. It is a bit more difficult to set up a working SSL certificate on your development machine though. It's all get difficult if you don't have the access to a server issuing certificates and all you want is a dummy certificate for development purposes only.

    Let's begin with creating a new SharePoint 2007 Web Application which will use SSL:

    SharePointSSL_WebApp

    The most important here is setting up the port to 443 and enabling SSL support. Configuring these settings correctly should automatically create the correct load balanced url beginning with https and ending with :443.

    Now we have the Web Application, we are ready to create and link the SSL certificate. I have assumed you don't have access to a certificate server and you need to create an SSL certificate by yourself. To do so, you will first of all need the IIS 6.0 Resource Kit Tools. It contains a tool called SelfSSL which will create and link the dummy SSL certificate. After the installation you are almost ready to run the tool. The last detail you need to have is the ID of your Web Application which is required by SelfSSL. You can obtain it quite easily by running the IIS Manager > Properties of your Web Application and then opening the Logging Properties dialog.

    SharePointSSL_LoggingProperties

    The Web Application ID is the long number following W3SVC and in our case is 75208739:

    SharePointSSL_SiteID

    Now we have all the details we must run SelfSSL by calling from the command prompt:

    SelfSSL.exe /S:75208739 /T /Q

    The SSL certificate will get automatically created and linked to our Web Application which will allow us to work with SharePoint through SSL.

    Summary

    Making your SharePoint 2007 development environment resemble the customer's production environment turns very useful during custom development. It allows you to debug your solution earlier and much more accurately. Furthermore you are able to test your deployment procedure much earlier in your development process what will spare you some unpleasant surprises afterwards.
    Configuring anonymous access and SSL support if applicable isn't very difficult and covers the most common development issues. It is therefore worth making an integral part of your SharePoint 2007 development environment initiation.

  • Which Doctype to use with SharePoint 2007?

    Designing and developing accessible web sites on top of SharePoint 2007 gets more and more attention in the community. But the more developers try to reach the required accessibility or standards compliancy level, the more challenges they face and the more questions pop up. One of such questions is which doctype should be used for standards compliant and accessible web sites.

    As for SharePoint 2007 the answer is simple: XHTML - it is the only possibility. SharePoint 2007 is built upon ASP.NET 2.0: it makes use of the ASP.NET 2.0 runtime and enriches it with extra functionality. ASP.NET 2.0 has been designed to render XHTML output. Although the compliancy level can be set in web.config not all controls adjust their output to this configuration. Furthermore the runtime itself uses hidden fields for State Management: all these fields contains the ID attribute which begins with __ (double underscore). Unfortunately such names are allowed only in XHTML so there is no chance for fallback to HTML 4.01 unless you want to rewrite the ASP.NET 2.0 runtime.

    The choice for XHTML as the only possible doctype for ASP.NET 2.0 and SharePoint 2007 applications is quite odd knowing it's the only doctype so far officially not supported by Microsoft browsers. According to the specification all XHTML pages should be served with the application/xhtml+xml mime-type. This allows the parser (no matter whether it is a User Agent or another information system) to process the file as if it was an XML document. None of the Internet Explorer versions can deal with the application/xhtml+xml mime-type. Microsoft Internet Explorer 6.0 doesn't render the page at all and Windows Internet Explorer 7.0 renders it as an XML document. Although there are some tricks available to bypass this default behavior and you could vary the doctype depending on the User Agent requesting the page using content negotiation and browser headers, it would lead to serving two different content versions - both standards incompliant. Because of the ASP.NET 2.0 framework XHTML remains the only doctype allowing you to achieve standards compliancy.

    Luckily XHTML pages can be served with the text/html mime-type known from the HTML 4.01 pages. All currently available Internet browsers parse XHTML pages correctly regardless of the mime-type. Even the W3C Validation service returns no errors during the validation of an XHTML page served with the text/html mime-type - not even a warning.

    There are some major drawbacks for using the text/html mime-type with XHTML pages though. These are important particularly when you want to process XHTML pages by external information systems. In such situations the complexity of such documents arises as there are some workarounds required. One of the elements affected by the incorrect mime-type are the JavaScript scripts and CSS styles within the XHTML document which required additional markup for their comments. Using in-page script and style elements isn't a best practice yet it's definitely quite a plausible scenario.

    Another major issue is the output produced by the authoring tools available in SharePoint 2007. The standard Rich Text Editor (RTE) produces output which is far from XHTML compliant. Even it's well known substitute from Telerik can't do anything about it as the generated markup (which is XHTML compliant) is being altered on saving by SharePoint 2007 runtime.

    Knowing all this, the perfect choice would be the XHTML 1.0 Transitional doctype. Unfortunately some accessibility guidelines, among which the Dutch government guidelines (Webrichtlijnen: http://webrichtlijnen.overheid.nl), don't allow you to use the Transitional variants. This has serious consequences for the authoring environment and the content presentation. Because the XHTML pages are being processed like XML documents, even the smallest syntax error will result in an XML error message instead of rendering the document. SharePoint 2007 standard authoring environment produces standards incompliant markup. Furthermore SharePoint 2007 doesn't even allow you to use external tools as the markup is being modified on save anyway.

    By eliminating the possible solutions we came to the presentation layer of SharePoint 2007. As it is highly customizable - especially in Publishing Sites - it seems almost a perfect solution. Well it is, almost. You can very easily encapsulate the required logic in a custom control. The downside is, you will end up cleaning the output on the run-time so caching the rendered page will be required in most environments.

    Summary

    As a developer of an accessible or a standards compliant web site built upon SharePoint 2007 you have to deal with multiple challenges. First of all you have to use the XHTML doctype. If you're lucky you can apply the Transitional variant which will support you while dealing with the incompliant markup created by the SharePoint 2007 RTE. No matter if you use the Strict or the Transitional variant of XHTML you are very likely to need to do something about the markup generated by SharePoint 2007. As there isn't much choice left you will end up with a custom control responsible for rendering the markup compliant with the the XHTML standard. It doesn't seem to be the perfect solution but it works. Getting it done in a real life scenario will require a caching solution to suppress the extra load caused by content cleansing on the run-time.

  • SharePoint 2007 redirect solved: using 301 instead of 302 redirects

    Each time you request a Site Collection (http://domain/) or a Site (http://domain/foo/) of your Publishing Site you get redirected to the .aspx">http://domain/Pages/<WelcomePage>.aspx. SharePoint 2007 uses the 302 header (location temporarily moved) for this purpose. Surprisingly even WSS uses the 302 header to redirect a root url to the default.aspx. In comparison ASP.NET uses an internal redirect to render the default page when the root url requested: there is no redirect in this situation.

    The whole issue about the 302 headers is that the redirected locations don't get crawled by search spiders which don't follow temporarily moved pages. While it's not really an issue for intranet environments it has major impact on indexing the content of Internet-facing web sites and making them searchable using a search engine.

    Looking for an answer I have researched the SharePoint runtime: SPHttpHandler, SPRequestModule and PublishingHttpModule classes. As none of these has given me a clear answer I have noticed that there are multiple references to the Redirect method present in the code which uses the 302 header as well.

    To solve the issue I have designed a custom redirect HttpModule which uses 301 headers instead.

    The requirements

    The module must rewrite all request for a Site Collection or Site. Url's of these request might but don't have to contain trailing slash (/). Furthermore the module must distinct a WSS request from a Publishing Site / Publishing Web request. Also the module has to be aware of Variations if used by the Site Collection.

    The work

    Firs of all we create a new HttpModule. As we want the redirect to find place as soon as possible we will hook it up in the BeginRequest event. Furthermore we want the module to be the first one to interact with the request. As we use an external assembly we need to define it as the first element in the httpModules section of web.config.

    namespace Imtech.SharePoint.Enhancement.HttpModules
    {
    public class RedirectModule : IHttpModule
    {
    #region IHttpModule Members

    public void Dispose()
    { }

    public void Init(HttpApplication context)
    {
    context.BeginRequest +=
    new EventHandler(context_BeginRequest);
    }

    void context_BeginRequest(object sender, EventArgs e)
    {
    HttpApplication app = (HttpApplication)sender;
    string requestUrl = app.Request.Url.ToString();
    }

    #endregion
    }
    }

    Because we will need the Request url later on in quite a few places I have decided to store it in a separate variable.

    The first requirement states that the module should redirect only requests for Site Collections and Sites. If the requirement wouldn't have say that the trailing slash is optional you could solve it using a simple if (requestUrl.EndsWith("/")). In our situation we will have to use a Regular Expression in order to figure out whether we need to rewrite the url or not.

    Regex regEx =
    new Regex(@"^https?://.*(?<itemUrl>/[^/]+\.[^/\.]+)$");
    if (regEx.IsMatch(requestUrl))
    return;

    if (!requestUrl.EndsWith("/",
    StringComparison.CurrentCulture))
    requestUrl += "/";

    If the url matches the regular expression it means it's a page request and should be passed on along the request pipeline unaltered. Later in the module we will combine the request url with the page url. As the trailing slash is optional I have decided to add it at the end if not present - just to be sure that combining the destination url of different parts will produce correct result.

    The next requirement is distinction between WSS and Publishing Site requests.

    string destinationUrl = String.Empty;

    SPSecurity.RunWithElevatedPrivileges(delegate()
    {
    try
    {
    using (SPSite site = new SPSite(requestUrl))
    {
    using (SPWeb web = site.OpenWeb())
    {
    if (PublishingWeb.IsPublishingWeb(web))
    destinationUrl = String.Concat(requestUrl,
    publishingWeb.DefaultPage.Url);
    else
    destinationUrl = String.Concat(requestUrl,
    "default.aspx");
    }
    }
    }
    catch { }
    });

    Based on the request url we create a new instance of SPSite and then open the requested web. As we can fail at this point already (for example when passing a list url) I have decided to catch the thrown exception to avoid turning the request into an error message. The distinction itself is quite straight forward and makes use of the IsPublishingWeb method. One important thing: because we are very likely to use the module for anonymous users we need to run the code with elevated privileges: the IsPublishingWeb method requires some extra permission in order to run.

    Our last requirement was making the redirect module aware of Variations if used by the Site Collection. Depending on the requirements defined by your customer you might need to implement the standard SharePoint Variation logic which chooses the variation basin on the User Agent language settings. Unfortunately most users are not aware of the existence and usage possibilities of the language settings most of our customers choose to load the Dutch variation by default. If your customer requires the standard SharePoint approach you would need to implement the logic from the VariationRootLanding User Control in the ControlTemplates directory. I will focus on the scenario we're using.

    PublishingWeb publishingWeb = PublishingWeb.GetPublishingWeb(web);
    if (publishingWeb.DefaultPage.Url.EndsWith("/VariationRoot.aspx",
    StringComparison.CurrentCultureIgnoreCase))
    {
    string defaultPage = String.Empty;
    using (SPWeb nlWeb = site.OpenWeb("nl"))
    {
    defaultPage =
    PublishingWeb.GetPublishingWeb(nlWeb).DefaultPage.Url;
    }

    destinationUrl = String.Concat(requestUrl, "nl/", defaultPage);
    }
    else
    destinationUrl =
    String.Concat(requestUrl, publishingWeb.DefaultPage.Url);

    In most scenarios the variation redirect finds place at the Site Collection level. The default page of the root web is then set to Pages/VariationRoot.aspx. Knowing this we can check whether we need to use the variation redirect or not. The rest is quite straight-forward: we obtain the Dutch site and its Welcome Page.

    The last part is the redirect itself using the 301 header:

    if (!String.IsNullOrEmpty(destinationUrl))
    {
    app.Response.AddHeader("Location", destinationUrl);
    app.Response.StatusCode = 301;
    }

    The destination url might be empty if an exception has occurred during the request processing. We will therefore redirect only if a destination url has been set by our module.

    Putting it all together:

    namespace Imtech.SharePoint.Enhancement.HttpModules
    {
    public class RedirectModule : IHttpModule
    {
    #region IHttpModule Members

    public void Dispose()
    { }

    public void Init(HttpApplication context)
    {
    context.BeginRequest +=
    new EventHandler(context_BeginRequest);
    }

    void context_BeginRequest(object sender, EventArgs e)
    {
    HttpApplication app = (HttpApplication)sender;
    string requestUrl = app.Request.Url.ToString();
    Regex regEx =
    new Regex(@"^https?://.*(?<itemUrl>/[^/]+\.[^/\.]+)$");
    if (regEx.IsMatch(requestUrl))
    return;

    if (!requestUrl.EndsWith("/",
    StringComparison.CurrentCulture))
    requestUrl += "/";

    string destinationUrl = String.Empty;

    SPSecurity.RunWithElevatedPrivileges(delegate()
    {
    try
    {
    using (SPSite site = new SPSite(requestUrl))
    {
    using (SPWeb web = site.OpenWeb())
    {
    if (PublishingWeb.IsPublishingWeb(web))
    {
    PublishingWeb publishingWeb =
    PublishingWeb.GetPublishingWeb(web);
    if (publishingWeb.DefaultPage.Url.
    EndsWith("/VariationRoot.aspx",
    StringComparison.CurrentCultureIgnoreCase))
    {
    string defaultPage = String.Empty;
    using (SPWeb nlWeb = site.OpenWeb("nl"))
    {
    defaultPage =
    PublishingWeb.GetPublishingWeb(nlWeb)
    .DefaultPage.Url;
    }

    destinationUrl = String.Concat(requestUrl,
    "nl/", defaultPage);
    }
    else
    destinationUrl = String.Concat(requestUrl,
    publishingWeb.DefaultPage.Url);
    }
    else
    destinationUrl = String.Concat(requestUrl, "default.aspx");
    }
    }
    }
    catch { }
    });

    if (!String.IsNullOrEmpty(destinationUrl))
    {
    app.Response.AddHeader("Location", destinationUrl);
    app.Response.StatusCode = 301;
    }
    }

    #endregion
    }
    }

    To see it working build the project, copy the assembly to the bin directory of your web application and add the following element to the httpModules section of the web.config:

    <add name="ImtechRedirectModule"
    type="Imtech.SharePoint.Enhancement.HttpModules.RedirectModule" />

    Summary

    Redirects using the 302 header can form a serious issue on Internet-facing web sites as it comes to indexing the content of a web site. Using custom HttpModules to overrule the standard behavior of SharePoint is a flexible solution for this challenge.
    The example above should work good enough in most scenarios. Depending on the requirements of your customers you might need to extend it with some extra functionality like for example standard Variations logic support. Custom HttpModules prove the extensibility and flexibility of SharePoint 2007 and the way it can be made to fit various requirements and scenarios.

  • The impact of developing an accessible web site in SharePoint 2007

    The development process of a typical Web Content Management solution based on Microsoft Office SharePoint Server 2007 consists of three main areas: User Experience, Functionality and Deployment.

    SharePointSolution

    The development process begins mostly with designing the total User Experience. Based on the business requirements the designers determine the particular areas on the web site and the corresponding functionality. The typical products of this stage are the User Interface (UI) design and the Interaction design – the way the UI responds to the user input.

    The next stage is translating the User Interface into code: HTML, Cascade Style Sheets (CSS) and JavaScript (if any client-side interaction required). As the products of this cycle form the baseline for the solution to be delivered it is crucial to name all the requirements of the total product and in particular the accessibility aspects.

    During the last stage of the User Experience phase the translated User Interface is being incorporated into SharePoint 2007. At this moment the Master Pages and Page Layouts are being formed and the content areas and the Web Part Zones are being determined and placed within the Page Layouts.

    In many cases the User Interface incorporated in SharePoint 2007 is being hand over to the developers who are going to fill it with the dynamic controls they are going to build. Using the original User Experience translations the developers start their work. First of all basing on the original User Experience they design the look & feel and the behavior of the dynamic control and Web Parts. Then they take the pieces of the User Interface translation and incorporate them within the custom controls.

    As soon as the development stage is finished and the whole solution has been tested, it is being prepared to be deployed on various environments such as test or production.

    The decision of developing an accessible WCM solution based on SharePoint 2007 has a major impact on the development process. The affected areas have been marked orange (product independent) and green (requires product specific knowledge) on the figure below.

    SharePointSolutionAccessibility

    First of all the designers have to take accessibility into consideration while designing the User Interface. Drop down menu’s dependant on JavaScript and mouse centered interface for example can lead to a totally inaccessible web site. Choices like these can have major impact on the overall accessibility of the web site and the presented information therefore they need to made very carefully and according to best practices for developing accessible web sites.

    The products made during the translation stage form the baseline for the whole web site. The accessibility of the information depends on the quality of the delivered code. That is why it is crucial to be sure that the right patterns and practices have been chosen to translate the drawing into code. The translation can also have impact on the total performance of the web site therefore it definitely shouldn’t be underestimated.

    During the incorporation of the translated User Interface into SharePoint the designers are very likely to face many of the undocumented features of SharePoint 2007. Originally SharePoint’s interface is table-based and my experience shows that even a little alteration can have impact on the SharePoint engine. If made carefully the decisions taken at this point can make the development stage easier and can guarantee very high level of accessibility.

    The developers are responsible for designing and developing custom controls which in many cases present dynamic content. Many of these developers are .NET developers with some SharePoint experience and very little to none accessibility knowledge. At this stage the web site has the biggest risk of getting inaccessible. Because of the little accessibility knowledge the most developers have, they are not able to estimate the impact of their choices on the accessibility aspects of a web site.

    Technical Challenges

    Microsoft Office SharePoint Server 2007 is built upon the ASP.NET 2.0 framework. In many aspects it enriches this platform and extends the available functionality. Unfortunately the dynamic interface and the extensibility have major impact on the standards compliance and the rendered output.

    Challenges

    The figure above presents various areas of custom development within ASP.NET 2.0 and SharePoint 2007. The green areas are the one which are fully controlled by the developers. The orange areas are the challenges that the developers will face while trying to deliver an accessible SharePoint 2007 solution. Though the impact on the accessibility depends on the particular area, all of them are required to make a web site fully accessible. The last piece – the Editors Area is left unaltered because it has no influence on the accessibility of the public side of a web site - the area you will focus on most of the times.

    Once again developing an accessible web site in SharePoint 2007 is doable yet very challenging. It requires understanding of the accessibility guidelines, SharePoint 2007 and ASP.NET 2.0 internals and accessibility issues within the both platforms.

  • Automatically marking up abbreviations and acronyms in SharePoint 2007

    Accessibility is a broad term and reaches way beyond the standards compliant code only. Accessibility is in my belief a set of features improving the understanding of information presented by an information system. I have to admit though compliant and semantic HTML is a very important factor of accessibility as it hosts the information. As I have recently solved the issue of standards compliant HTML in SharePoint 2007 I have started looking for new challenges and accessibility improving solutions. Almost immediately I have stumbled upon automatically marking up abbreviations in content.

    I have faced exactly the same challenge during the Rock My Website competition last year when John, Martijn and myself were building an accessible web site in ASP.NET. I wanted to implement a solution which would automatically markup all known abbreviations in de content using some kind of dictionary. As we weren't using any Content Management System we would need to think of another way to maintain the abbreviations dictionary. Eventually we have dropped the idea then, but now we have SharePoint 2007.

    Looking at standard SharePoint 2007 features I have almost immediately came up with a solution for this challenge.

    The requirements

    First of all to Provide a user friendly way to maintain the abbreviations dictionary. Storing it in a central location will decrease the amount of work required to maintain the dictionary and keep the definitions consistent. Then markup all the abbreviations found in the content so that HTML will become <abbr title="HyperText Markup Language">HTML</abbr> instead. Last but not least: replace only the first occurrence of an abbreviation on the page as it will provide enough information for a visually impaired visitor.

    The work

    I thought the best way for storing and maintaining the abbreviation dictionary would be a custom list consisting of two columns: Term and Definition. Let's call the list Abbreviations.

    AbbreviationsList

    Then the replacement logic. As replacing the abbreviations could be done in numerous ways I though it would be the easiest to customize the FieldValue web control and extend it with the required properties. You can find more information on this approach in one of my previous posts.

    I have decided to add two properties to our extended FieldValue control: boolean MarkupAbbreviations to be able to turn it on and off easily and AbbreviationsList to pass the URL of the list as a parameter instead of hard coding it.

    namespace Imtech.SharePoint.Compliancy.Controls
    {
    [ToolboxData("<{0}:FieldValue runat=\"server\" />")]
    public class FieldValue : WebControl
    {
    private Dictionary<string, string> abbreviations;

    private string _FieldName;
    [Bindable(true), Localizable(false)]
    public string FieldName
    {
    get { return _FieldName; }
    set { _FieldName = value; }
    }

    private bool _MarkupAbbreviations;
    [Bindable(true), Localizable(false)]
    public bool MarkupAbbreviations
    {
    get { return _MarkupAbbreviations; }
    set { _MarkupAbbreviations = value; }
    }

    private string _AbbreviationsList;
    [Bindable(true), Localizable(false)]
    public string AbbreviationsList
    {
    get { return _AbbreviationsList; }
    set { _AbbreviationsList = value; }
    }
    }
    }

    I have also added a dictionary to store the abbreviations obtained from the abbreviations dictionary list.

    Let's load the available abbreviations now:

    protected override void CreateChildControls()
    {
    LoadAbbreviations();
    base.CreateChildControls();
    }

    private void LoadAbbreviations()
    {
    try
    {
    Regex regEx =
    new Regex("(?<SiteUrl>/.*)/?Lists/(?<ListName>[^/]+)");
    Match m = regEx.Match(_AbbreviationsList);

    using (SPWeb site = SPContext.Current.Site.OpenWeb(
    m.Groups["SiteUrl"].Value))
    {
    SPList list = site.Lists[m.Groups["ListName"].Value];
    abbreviations = new Dictionary<string,string>(list.ItemCount);
    foreach (SPListItem abbreviation in list.Items)
    abbreviations.Add(abbreviation.Title,
    abbreviation["Comments"].ToString());
    }
    }
    catch { }
    }

    I have decided to get the URL of the list and the site where it resides by using regular expressions. After opening the site I open the list as we want to obtain all available items within it. Before we will walk through the available items we also need to instantiate the abbreviations variable to store the abbreviations and their definitions in code. Adding the the abbreviations to the dictionary is straight forward. In a real life scenario you might add an extra check just to get sure that you won't add the same abbreviation with various definitions twice.

    As we have the abbreviations available in code we can proceed and do the replacing in the content.

    protected override void Render(HtmlTextWriter writer)
    {
    string content = SPContext.Current.Item[_FieldName].ToString();
    markedAbbreviations = new List<string>(abbreviations.Count);

    foreach (KeyValuePair<string, string> abbreviation in abbreviations)
    {
    Regex regEx = new Regex(String.Format(
    CultureInfo.CurrentCulture, @"\b{0}",
    abbreviation.Key),
    RegexOptions.IgnoreCase);
    content = regEx.Replace(content, String.Format(
    CultureInfo.CurrentCulture,
    "<abbr title=\"{0}\">{1}</abbr>",
    abbreviation.Value,
    abbreviation.Key), 1);
    }

    writer.Write(content);
    }

    We will do the replace in the Render method. First of all we will need the content of the chosen field. To do the replace we will use the regular expressions again. This time we will use the abbreviation in combination with a word boundary (\b): if looked for HTML for example we want to find occurrences of HTML but not XHTML. Word boundary will pick only the complete matches we want. The last thing we want to add is the 1 telling the regular expression engine to replace only the first occurrence. That's it. Let's see how it works:

    Result

    In the preview example I have used Mozilla Firefox as it underlines the abbreviations with a dotted line and shows tooltips with the definition on mouse hover. The solution works according to the requirements: we have an easily maintainable abbreviations dictionary and an automatic replace of the first occurrence of an abbreviation only. The downside is that the replace occurs within a field. Should you have multiple fields containing content with abbreviations on one page and you would still want to replace the first occurrence only you would have to think of a solution to store the already replaced abbreviations in a page wide available place.

    Another extra feature might be extending the abbreviations dictionary with an extra column keeping the type of abbreviation so that they will be spoken out correctly when read using a screen reader. You would first define the CSS rules like for example:

    <style type="text/css">
    acronym {speak : normal;}
    abbr.initialism {speak : spell-out;}
    abbr.truncation {speak : normal;}
    </style>

    and then markup the various kinds of abbreviations:

    <acronym title="North Atlantic Treaty Organisation">NATO</acronym> 
    <abbr title="Hyper Text Mark-up Language" class="initialism">HTML</abbr>
    <abbr title="Europe" class="truncation">Eur</abbr>

    You could also make easily a distinction between abbreviations and acronyms (kind of abbreviations which can be pronounced as word). I hope that this solution proves how highly extensible SharePoint 2007 is and that it can support accessibility solutions as well. I would love to hear now your ideas on improving the accessibility experience in SharePoint 2007.

  • Automatically generating a hierarchical Title in <title> element

    Recently while working on an Internet facing web site for one of our customers I thought of creating a control which would automatically create a hierarchical Title in the <title> element, like: Site Collection - Current Site - Current Page. Standard SharePoint 2007 allows you to define the title on the Master Page and within a Page Layout. Default SharePoint 2007 displays the Page Title in the <title> element. As I've been recently researching the accessibility issues I have noticed that such a behavior can cause loosing the context - especially if the visitor is vision impaired. Secondly it might cause search engines indexing a page and not linking it to the organization (Site Collection) or for example a division within it (Site). You could solve it using the standard features like displaying the Site's Title using <SharePoint:ProjectProperty> but still it wouldn't provide me the flexibility I wanted it to have. That's why I have decided to make a control which would automatically generate a title based on the existing hierarchy.

    The requirements

    The most important is generating a title consisting of information from the three levels: Site Collection, Site and Page the user is currently on. As you might want to hide the Site Collection title in some cases it should be also optional whether it should be displayed or not. In my example I have used a minus as a separator: however it should be possible to set the separator without rewriting the control. The control should also be intelligent enough to be able to distinct whether the user is on the Root Web and Default Page. Another option is the possibility to reverse the title so it would display Page - Site - Site Collection instead Site Collection - Site - Page: some customers believe that it improves the readability and context. Personally I would love to leave this choice to them. Last but not least the control have to work with anonymous access - it's definitely something you should keep in mind from the very beginning as it influences the way we will be obtaining site information.

    The work

    First of all let's define the properties we will need:

    private string _Separator;
    public string Separator
    {
    get { return _Separator; }
    set { _Separator = value; }
    }

    private bool _Reversed;
    public bool Reversed
    {
    get { return _Reversed; }
    set { _Reversed = value; }
    }

    private bool _SuppressSiteCollectionTitle;
    public bool SuppressSiteCollectionTitle
    {
    get { return _SuppressSiteCollectionTitle; }
    set { _SuppressSiteCollectionTitle = value; }
    }

    Then let's implement the logic. As it's quite straight forward let's put it all simply in the Render method. Furthermore it will provide us with the required control over the rendered output as we don't want any extra elements rendered with the title.

    protected override void Render(HtmlTextWriter writer)
    {
    List<string> items = new List<string>(3);
    Guid siteId = SPContext.Current.Site.ID;
    Guid webId = SPContext.Current.Web.ID;
    }

    First of all we define some variables we are going to need later on: a list where we will store the pieces of the title (it can contain default 3 only 3 items because we will be needing only the titles of the Site Collection, Site and Page). We will need the ID's of both Site Collection and Site to open new instances with elevated privileges. As we need to obtain the titles of both of these for anonymous users as well we need to run this piece of code with elevated privileges.

    protected override void Render(HtmlTextWriter writer)
    {
    List<string> items = new List<string>(3);
    Guid siteId = SPContext.Current.Site.ID;
    Guid webId = SPContext.Current.Web.ID;

    SPSecurity.RunWithElevatedPrivileges(delegate()
    {
    SPSite elevatedSite = new SPSite(siteId);
    SPWeb elevatedWeb = elevatedSite.OpenWeb(webId);
    if (!SuppressSiteCollectionTitle)
    items.Add(elevatedSite.RootWeb.Title);
    if (!SPContext.Current.Web.IsRootWeb || SuppressSiteCollectionTitle)
    items.Add(SPContext.Current.Web.Title);

    if (String.Compare(SPContext.Current.ListItem.Url,
    PublishingWeb.GetPublishingWeb(elevatedWeb).DefaultPage.Url,
    true) != 0)
    items.Add(SPContext.Current.ListItem.Title);

    elevatedWeb.Dispose();
    elevatedSite.Dispose();
    });
    }

    First of all we create new instances of the Site Collection and Site as we will be needing them to obtain the titles. Then we make the first check to find out whether the user has chosen to suppress the Site Collection's title part. As we go further we need to obtain the title of the Site but only if the visitor is not currently on the root site or if the developer has chosen to suppress the Site Collection's title. Without this check you would get the Site Collection's title twice while being on the home page of the web site.

    The last piece of the title is the page title. It should be displayed only if the current page is not a welcome page: in this case the page's title is equal to the Site's title. For the title comparison I'm using the Compare method instead String.ToLower() != String.ToLower() as it creates new instance of the compared strings in the memory.

    We still one requirement left:

    protected override void Render(HtmlTextWriter writer)
    {
    List<string> items = new List<string>(3);
    Guid siteId = SPContext.Current.Site.ID;
    Guid webId = SPContext.Current.Web.ID;

    SPSecurity.RunWithElevatedPrivileges(delegate()
    {
    SPSite elevatedSite = new SPSite(siteId);
    SPWeb elevatedWeb = elevatedSite.OpenWeb(webId);
    if (!SuppressSiteCollectionTitle)
    items.Add(elevatedSite.RootWeb.Title);
    if (!SPContext.Current.Web.IsRootWeb || SuppressSiteCollectionTitle)
    items.Add(SPContext.Current.Web.Title);

    if (String.Compare(SPContext.Current.ListItem.Url,
    PublishingWeb.GetPublishingWeb(elevatedWeb).DefaultPage.Url,
    true) != 0)
    items.Add(SPContext.Current.ListItem.Title);

    elevatedWeb.Dispose();
    elevatedSite.Dispose();
    });

    if (Reversed)
    items.Reverse();

    string pageTitle = String.Empty;
    foreach (string item in items)
    {
    if (String.IsNullOrEmpty(item))
    continue;

    if (pageTitle.Length > 0)
    pageTitle += Separator;
    pageTitle += item;
    }

    writer.Write(pageTitle);
    }

    To handle the reversed order of the title we use the Reverse method of a List generic collection. All we need to do is to put the title together and render it.

    Using this control will allow you to display consistent titles across the Site Collection and will provide context information to the visitors. The best way to use it is to put it in the Master Page between the <title></title> elements. What you need to do is to hide the PlaceHolderPageTitle. You can't remove it from the Master Page and what's even more important you can't remove it from the Page Layouts as well. I have explained it why in one of my previous posts.

  • Windows Live Writer Plugin: Imtech Word Count

    Reading the Windows Live Writer wish list forum I have stumbled upon a request for a Word Count plugin. As the request has been done quite a while ago and there hasn't been any response yet I have decided to make one myself. As the solution wasn't that difficult I was able to stuff making it between work and personal stuff.

    WordCountPlugin

    The plugin I have made works pretty simple: it grabs the selected text, removes any HTML elements (the string you get in the plugin method, contains the source view of a post content), decodes HTML entities and then makes the count using Regular Expression. It's the first version of the plugin and I might have missed some exceptions as I'm not a professional editor myself. Let me know if you have any suggestions or improvement/extension ideas.

    I have already posted the plugin to the Live Gallery. Yet somehow I don't believe it's going to be available there really soon (the Find & Replace plugin still isn't by the way...), so I have put it at our CodePlex site as well.

  • Windows Live Writer Plugin: Imtech Find & Replace

    I use Windows Live Writer to post on my blog. Sometimes I just miss the Replace functionality which in my opinion is a must-have of each content editor - even the Notepad proves it. If I'm not wrong the Live Writer development team has promised to include the Replace in the next version. As a developer you have two options: waiting or just doing it yourself. I have chosen for the second one....

    ReplacePluginDialog

    The Find & Replace Plugin I have made support plain search as well as Regular Expressions. Furthermore you can do a replace on the Preview (F11 View) or the Source (SHIFT+F11 View).

    You can download the Plugin from our CodePlex site. I have posted it to the Live Writer Plugins gallery as well, but I don't know how long will it take to have it published. In the meanwhile be the first one to have it: Imtech Find & Replace in Windows Live Writer.

    ReplacePluginAbout

    I have wrapped the Plugin in an MSI installer. Just run the setup and the Plugin with be automatically put in the Plugins directory of Live Writer.

  • Windows Live Writer Plugin: Convert RGB color to Hex

    Since a while I've been using the Windows Live Writer. Though it's a nice and handy tool that works way better than any web interface, it misses some functionality like pasting the code from Visual Studio, Search and Replace, etc.

    Some time ago I have found a nice plugin to paste the Visual Studio keeping the formatting, yet there is one downside to it. This blog doesn't support the colors definition using the rgb syntax, like: style="color: rgb(0,0,255)". Community Server renders the colors properly in it's edit mode (preview mode of Tiny MCE) but the text is all black after publishing. After doing the replace by hand a few time, I have decided to write my own Live Writer Plugin to do it for me.

    All I need to do now is to Select All and then click on the plugin to do the job for me. Writing the Live Writer plugins isn't very difficult and you can do it easily within a few minutes. I suppose the mechanism is quite powerful yet the most simple things like replacing can be done by writing a few lines of code.

    You can download the plugin from the CodePlex site. I haven't wrapped the plugin in any fancy installer. Simply unzip it and put it in the Plugin folder of the Live Writer and it will automatically appear on your Insert bar.

  • Using WebClient in SharePoint context

    Recently while working on one of my projects I needed to use the WebClient in order to obtain a rendered version of a Publishing Page using a url. My environment consisted of an Publishing Site using Active Directory authentication and it's extend equivalent using Anonymous Access as well. My goal was to obtain the rendered page before it's published to be able to check the standards compliance of the rendered code.

    As I have coded the most basic piece of WebClient usage:

    byte[] response;
    using (WebClient wc = new WebClient())
    {
        wc.UseDefaultCredentials = true;
        response = wc.DownloadData("http://SharePoint/Pages/default.aspx");
    }

    I have discovered I've received from the response the rendered version of the published version (1.0) instead of the current minor version (1.1). As I've been logged in using valid credentials and I've configured the WebClient instance to use default credentials I couldn't figure out the reason of this behavior.

    After a while trying some other approaches out I have noticed I've been using the Site Collection using Anonymous Access. After changing it to the editors Site Collection (the one without AD authentication only) I have finally retrieved the major version of the page. It turned out that whenever possible the WebClient will try to make the request without any authentication. It will use the credentials only if asked by the server - what will never happen by the way if the anonymous access is turned on.

    I think it's definitely something you should remember if you work with the WebClient within SharePoint. WebClient is a very useful feature still it might just spare you some precious minutes trying to figure out why it doesn't work as you would expect it to.

  • W3C Validator for SharePoint 2007

    SharePoint 2007 is a very extensible development platform. Thanks to its flexibility the developers keep coming with new features which extend the user experience. One of the areas where the things are still missing is the Web Content Management: accessibility in particular leaves plenty of challenges to be tackled and features to be added by the accessibility enthusiasts and developers. One of these is the W3C Validator for SharePoint 2007.

    As I've mentioned some time ago we are busy here at Imtech ICT Business Solutions designing, developing and testing our own accessibility toolkit for SharePoint 2007. As the whole solution works perfectly I have started looking for some other ideas on how to extend it and enrich the editors' experience. One of the ideas I've got was incorporating the W3C Validator support into SharePoint 2007.

    First of all the requirements. As it's supposed to be editors' tool it needs to reside on the Publishing Console. It's supposed to validate the page: preferably before it's published so an editor can correct his mistakes if any found. Then the report: embedded into SharePoint 2007 or just a redirect? As embedding might enhance the total experience it would require you to provide new versions of the SOAP response parser (as the response you get from the W3C Validator is in the SOAP 1.2 format). Because most of the people dealing with accessibility and standards compliancy are familiar with the generated output that's the approach I have chosen.

    As our toolkit targets the Dutch standards you might be asking why we haven't chosen for that one. Well, the Dutch Quickscan supports URL's only. And because one of the requests was validating the page before actually publishing it it's a no go unfortunately.

    Completing the first step was quite straight forward: simply edit the CustomQuickAccess.xml file. The provisioning of such an alteration is slightly more challenging as the XML file is being provisioned by one of the Publishing Features and you are not allowed to replace during the provisioning a file provisioned by another Feature. And still, even if it was allowed, it wouldn't definitely be the best practice as the file can already contain other buttons. Replacing the file would remove them from the Publishing Console. Luckily I have done this before and could reuse a method I wrote which is comparable to the WebConfigModification method but then allowing me to edit any XML file.

    W3CValidatePageButton

    The validation process turned out to be the most challenging of the whole solution. Looking back at the requirements you will notice that I mentioned validating the page before actually publishing it. Posting the complete page which can contain the Site Actions menu, the Publishing Console and more of the inaccessible items which are required for the proper working of the editors area is unfortunately a no go. Taking this into consideration leaves you with two possibilities.

    First of all I've been thinking on iterating through the collection of fields available within the Content Type of the requested Publishing Page. Obtaining the values and making a new HTML page of them would be an option. The biggest downside of this approach is that you skip the rendering part of the value: remember once stored value can be displayed in many ways within a Page Layout using various Web Controls and Web Parts. Querying for a value is unfortunately not enough as it passes that stage of rendering and doesn't check whether a control alters the value making it inaccessible/incompliant. So then I came up with another approach.

    If you take a look at the client-side DOM accessible by JavaScript you can see that you are able to obtain an element iterating through the DOM nodes or using for example the element's ID. Using this approach would allow you to query the page as it is (including the SharePoint menus and toolbars) and then simply pick the content within the given element and send it to the W3C Validator. That led me to a new requirement: be able to set the ID of the content wrapper. We are definitely going to reuse this feature in other projects and because we work with different designers we need to have the possibility to configure this property.
    Realizing this task was not really difficult using the Ted Pattison's best practices for developing application pages. As this feature would eventually make part of our compliancy toolkit I have decided to extend the configuration page we already have.

    CompliancyToolkitConfiguration

    Then again, validation itself using server side DOM. Using the resources available within the .NET framework you are left with the MSHTML assembly if you want to use some DOM features in your .NET code. It does work, but you need to fix its interpretation of your document as it alters it stripping for example quotes from some attributes. Another challenging part is getting the instance of an HTMLDocument. In a Windows Application you would use an instance of the WebBrowser to do this, but does it remain the only way even if you have nothing to do with System.Windows.Forms and all you want is a plain instance of HTMLDocument? Luckily it isn't. But let me leave you something to research.

    Anyway, with the content of the rendered page accessed using server-side DOM, it was the time to send it to the W3C Validator to get the results.

    Instead of choosing for the immediate redirect to the W3C Validator I have put another application page between: just to have a status view and to be able to catch any exceptions or errors the validator could have encounter while obtaining the requested Publishing Page and parsing its content.

    W3CValidatorValidating

    The whole concept works pretty good. The user is being redirected to the W3C Validator and can precisely see what the reasons of the problems are if any found. In our case: 100% valid thanks to Imtech SharePoint Compliancy Toolkit.

    W3CValidatorCompleted

    Incorporating the W3C Validator for SharePoint 2007 in SharePoint self we hope to enhance the editors' experience and make the editing of compliant content easier by allowing them to validate the content they have entered way earlier in the editing process. I hope that I have given you a little idea on how the SharePoint 2007 environment can be extended to make it more user-friendly.

    The W3C Validator for SharePoint 2007 is a part of the Imtech SharePoint Compliancy Toolkit. The other ideas I have mentioned? Well, these will follow pretty soon.

  • Extending the SharePoint:FieldValue WebControl

    How many times did you try to incorporate some extra rules or formatting into Page Layouts? Formatting a date is just one of the many examples solving which can cause you spent many hours trying to using the out-of-the-box available controls. The SharePoint:FieldValue is probably the most commonly used control, especially in Page Layouts where you don't want to use the Edit-in-place experience. Yet this control doesn't have any customization or formatting possibilities. That's why some time ago me and my colleagues at Imtech ICT Business Solutions have thought about creating our version of this control with some more formatting properties. As soon as we have created it, we have started benefiting from it: because we had the code of it, extending it with extra functionality is just a piece of cake. Anyway, let me introduce you the Imtech:PageProperty control.

    I would like to guide you through a process of creating a simple yet powerful custom WebControl. At the end of this post you should be able to use the example we will build here and extend it with new functionality to make it fit to your needs.

    I assume you know the basics of working with WebControls within SharePoint 2007. Furthermore I assume already have a project, which builds into an assembly. I also assume that it's content is available within SharePoint. If you are a beginner in SharePoint and have questions about these steps, please react in the comment so others will be able to learn from it too.

    We start by creating a new public class deriving from the System.Web.UI.WebControls.WebControl class to provide the basic functionality of a WebControl:

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Web.UI.WebControls;
    using System.Web.UI;
    
    namespace WebControls
    {
        [ToolboxData("<{0}:PageProperty runat=\"server\" />")]
        public class PageProperty : WebControl
        {
        }
    }
    

    Then you need to define the attributes to use within the control. If you look at the SharePoint:FieldValue control, you see you can pass the name of the field of which the value you want to display. This guarantees the flexibility and reusability of the control. Let us do the same:

    private string _Property;
    
    [Bindable(true), DefaultValue(""), Localizable(false)]
    public string Property
    {
        get { return _Property; }
        set { _Property = value; }
    }
    Because we have called our control PageProperty I have chosen for the name Property instead of FieldName, but you are free to choose the one which suits you most.

    Let's test the control now. At this stage the control should be able to display the value of the Field passed through the Property attribute. To display this value we need to override the Render method of the control. Another option would be overriding the RenderContents method but it would cause your control to render a span or any other element around the contents which isn't desired in WCM scenario's when you deal with predefined layouts.

    protected override void Render(HtmlTextWriter writer)
    {
        try
        {
            writer.Write(
    SPContext.Current.Item[Property].ToString()
    ); } catch { } }

    Let's move on with adding the formatting logic. In our control we have decided to split this routine in two pieces: defining the type of the value and passing the desired date format. Without the first part you might end up trying to format a text string as a date. Let us define the two properties we require. Second of all choosing for this approach will allow you to use various formatting routines for various data types.

    public enum PropertyType
    {
        String,
        Number,
        Date
    }
    
    private PropertyType _Type;
    [Bindable(true), DefaultValue(""), Localizable(false)]
    public PropertyType Type
    {
        get { return _Type; }
        set { _Type = value; }
    }
    
    private string _Format;
    
    [Bindable(true), DefaultValue(""), Localizable(false)]
    public string Format
    {
        get { return _Format; }
        set { _Format = value; }
    }
    I have chosen for an enumeration to pass the data type to ensure the input within this attribute more easily. Using a string would work as well. Still it's not a best practice typing the possible values over and over again.

    As we have the required attributes we can move on to actually implementing the formatting routine. We will do it by extending the Render method we have defined at the beginning.

    try
    {
        string fieldValue =
    SPContext.Current.Item[_Property].ToString(); switch (_Type) { case PropertyType.Date: fieldValue =
    DateTime.Parse(fieldValue).ToString(Format); break; } writer.Write(fieldValue); } catch { }
    The switch block contains only one case at this moment. You could easily extend it though for example with extra logic for formatting numbers.

    The control is ready. You can use it within your Page Layouts as follow:

    <Imtech:PageProperty Property="Modified"
    Format="d MMMM" Type="Date" runat="server"/>

    The last piece is to register your newly created control within all Page Layouts or simply once in web.config - approach I prefer.

    The PageProperty WebControl allows you to replace the SharePoint:FieldValue control. Extending it with new features will allow you to box the functionality and avoid unnecessary complicated constructions in your Page Layouts or queries. The downside is you need to be a developer to do this and you must be allowed to deploy custom code on the web server.