Building accessible HTML controls for SharePoint

Search

Accessible SharePoint WebSites
Download ARF

Building accessible HTML controls for SharePoint

http://blog.thekid.me.uk

In a previous article I suggested that using webparts to produce accessible or highly styled websites was very difficult and that using publishing controls or standard ASP.Net controls was the way to go. In this article I plan to show how to create a control which solves a very common requirement...listing pages in the site.

To develop your accessible SharePoint publishing site visit SPWorks to download ARF, a free development framework.

Previously I have given some examples of the SPSiteDataQuery and showed how to get the results from the query in XML. We will use these to build a control which allows you to specify the CAML for the ViewFields, Webs, Query and Lists properties of the SPSiteDataQuery class.

Create the website

For the purpose of this article will I assume you have created a standard publishing site and still have the standard layouts, sites and pages. However, the control which gets created will work with any site (WSS or MOSS).

Create the VS project

In VS 2005 create a standard C# class library and call it PublishingSamples. Once this has been created you need to setup the project to allow access the SharePoint API and to make development easier.

First open the project properties and go to the build events.  Here we will add the following command to the post-build events.

copy $(TargetPath) "C:\Inetpub\wwwroot\wss\VirtualDirectories\website80"

Where 'website80' is the virtual directory of your SharePoint publishing site. This will automatically copy the DLL to the bin of the SharePoint site everytime you build the project.

Next you will need to enable the assembly signing, by clicking on the 'signing' section of the properties page and selecting new. Enter 'key' as the name of your new key.

Lastly you will need to add a reference to both Microsoft.SharePoint.dll and System.Web.dll.

Once the settings have been changed rename the Class1.cs file to SiteQuery.cs (this should automatically rename the class for you) and then ensure it inherits from System.Web.UI.Control.

public class SiteQuery : System.Web.UI.Control

Add the code to the class

Firstly we will add the properties required for the SPSiteDataQuery class...

private string _lists = "<Lists ServerTemplate='850'/>";

private string _query = "";

private string _webs = "<Webs Scope='Recursive' />";

private string _viewFields = "<FieldRef Name='Title' /><FieldRef Name='ID' /><FieldRef Name='FileRef' /><FieldRef Name='ContentType' />";

private uint _rowLimit = 10;

 

public uint RowLimit

{ get { return _rowLimit; } set { _rowLimit = value; } }

 

public string Lists

{ get { return _lists; } set { _lists = value; } }

 

public string Query

{ get { return _query; } set { _query = value; } }

 

public string Webs

{ get { return _webs; } set { _webs = value; } }

 

public string ViewFields

{ get { return _viewFields; } set { _viewFields = value; } }

These will allow us to set the parameters of the control in the page layout and so configure the control to list the content we want. Here I have given the some the properties some default values which will list all pages in the current site and all sub sites.

Next we need to add the code to generate the XmlDocument we will use to create the HTML. Firstly add the following using statements to the top of the class file...

using Microsoft.SharePoint;

using System.Data;

using System.Xml;

This will give us access to the classes we need. Next add a new property to the class called Document which returns an XmlDocument. This property will execute the query to return the content defined by our SPSiteDataQuery parameters.

public virtual XmlDocument Document

{

    get

    {

        SPSiteDataQuery q = new SPSiteDataQuery();

        q.Lists = Lists;

        q.Query = Query;

        q.Webs = Webs;

        q.ViewFields = ViewFields;

        q.RowLimit = RowLimit;

 

        DataTable t = SPContext.Current.Web.GetSiteData(q);

        t.TableName = "row";

 

        DataSet ds = new DataSet("rows");

        ds.Tables.Add(t);

 

        XmlDocument oResults = new XmlDocument();

        oResults.LoadXml(ds.GetXml());

 

        return oResults;

    }

}

Here we use the properties of our new class to initialize the SPSiteDataQuery class and perform our query. We use the DataSet GetXml() method (which I mentioned in a previous article) to turn our results into an XML document...this has the benefit of ensuring consistency in your XML and will make re-using XSLT much easier.

Next we need to add the properties that allow the XSL to be defined in the page layout. Add the following properties to the class...

private string _xslLocation = "/Style Library/XSL Style Sheets/";

private string _xslName = null;

 

public string XslLocation

{get { return _xslLocation; } set { _xslLocation = value; }}

 

public string XslName

{ get { return _xslName; } set { _xslName = value; }}

Here we are allowing the location of the XSLs and the name of the XSL file to be specified. The default location for the XSL is in the root of the site collection in the 'XSL Style Sheets' folder. This is where the XSL for the ContentByQuery WebPart is stored. In order to use the XSL to transform the document we need to override the Render method of the control.

First add the following using statement...

using System.Xml.Xsl;

Next override the Render method...

protected override void Render(HtmlTextWriter writer)

{

    XmlDocument oXsl = new XmlDocument();

    oXsl.LoadXml(SPContext.Current.Site.RootWeb.GetFileAsString(XslLocation + XslName));

 

    XslCompiledTransform oTransform = new XslCompiledTransform();

    oTransform.Load(oXsl);

 

    XsltArgumentList args = new XsltArgumentList();

 

    foreach (string sKey in Page.Request.QueryString.AllKeys)

        args.AddParam("QS-" + sKey, "", Page.Request[sKey]);

 

    oTransform.Transform(Document, args, writer);

}

Here we retrieve the XSL document from the XSL document library, load it into a transform class and call Transform passing the Document property we created previously. The Transform method uses the HtmlTextWriter to write out the generated HTML.

Build your project and ensure it compiles.

And that, as they say, is that. This class will perform the query to find the content you require, turn that into an XML document and use XSL to tranform that into HTML. The only HTML produced will be from your XSL...SharePoint will not add anything extra.

The completed class file can be downloaded from here.

Modify web.config

Building the control will automatically copy the DLL produced to our site's bin folder. We now need to modify the web.config of the site to mark the control as safe and to enable it to run. We will also make some changes to make debugging SharePoint easier.

Before we can make the changes we need to find out the assembly's FQN (Fully Qualified Name). Microsoft explain how to do this, but I prefer to use Reflector, as I normally have it hand.

Firstly add the assembly to the SafeControls section of web.config...

<SafeControl Assembly="PublishingSamples, Version=1.0.0.0, Culture=neutral, PublicKeyToken=15a83b848317a32e" Namespace="PublishingSamples" TypeName="*" Safe="True" />

Next turn custom errors off by modifying the following line...

<customErrors mode="Off" />

 Next change the SafeMode as described in a previous article...

<SafeMode MaxControls="200" CallStack="true"...

Then change the trust to Full...

<trust level="Full" originUrl="" />

Changing the trust to full will permit SharePoint to use our DLL even though it is only in the bin folder. Our DLL will run with full trust, but so will all other assemblies. This is fine for developing...it allows us to build the DLL & copy directly to the bin folder so that SharePoint will use it immediately without an IISRESET. However, for a production site the DLL should either be in the GAC or preferably configured with the correct code access security (CAS).

Save web.config and we are ready to modify the layout in SharePoint Designer and add our new control to it.

Add the control to your site

Firstly you need to register your assembly and the namespace. Add the following line to the top of your layout (or master page)...

<%@ Register Tagprefix="SQ" Namespace="PublishingSamples" Assembly="PublishingSamples, Version=1.0.0.0, Culture=neutral, PublicKeyToken=15a83b848317a32e" %>

Remember, the assembly FQN will be different for your assembly and so this should be changed to the FQN of your assembly.

Next add the control where you would like the output to appear...

<SQ:SiteQuery runat="server" XslName="List.xsl"/>

Here we have specified 'List.xsl' as the XSL to use for the transformation. This should be located in the "/Style Library/XSL Style Sheets/" folder of your site (the default specified in the class). To create this file use SharePoint designer...create the List.xsl and copy the following XSLT into it.

<xsl:stylesheet version="1.0"

    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

 

    <xsl:template match="/">

        <xsl:apply-templates select="rows/row"/>

    </xsl:template>

 

    <xsl:template match="row">

        <h2>

            <xsl:value-of select="Title"/>

        </h2>

    </xsl:template>

 

</xsl:stylesheet>

This is a fairly simple transformation which outputs the titles of the content which matched the query. This XSL will be applied to the XML which will have a format similar to below...

<rows>

    <row>

        <ListId>0F4344D9-37EE-4732-9B13-B6F3BAF38F91</ListId>

        <WebId>617FE4B1-A040-4BE5-9B55-70B34B8A8AC4</WebId>

        <ID>1</ID>

        <Title>Web Page 1</Title>

        <ContentType>Page</ContentType>

    </row>

    <row>

        <ListId>0F4344D9-37EE-4732-9B13-B6F3BAF38F91</ListId>

        <WebId>617FE4B1-A040-4BE5-9B55-70B34B8A8AC4</WebId>

        <ID>2</ID>

        <Title>Web Page 2</Title>

        <ContentType>Page</ContentType>

    </row>

</rows>

Save the layout (or master page) and navigate to the page...if everything has gone correctly you should see a list of all the pages in the current site.

You can then try modifying the control to display different content. The example below shows how to list news articles from the entire site collection.

<SQ:SiteQuery runat="server" XslName="List.xsl"

    Query="<Where><Eq><FieldRef Name='ContentType'/><Value Type='Text'>News</Value></Eq></Where>"

    Webs="<Webs Scope='SiteCollection' />" />

Conclusion

This is a fairly basic example and, in its current state, is definitely not useable on a production site. Hopefully it will give you a good start in creating your own controls which use XML and XSLT to produce HTML. In order to extend this control for production you would (at the very least) need to add error handling and caching. Caching is most important as this control could seriously affect the performance of your site without it.

Possible extensions to this class could include...

  • Add a URL property to a specify site or list on which to perform the query.
  • Turn it into an XmlDocument data source so that other controls on the page can consume the XmlDocument.
  • Add a content type property so that only pages of a particular content type are returned. This would negate the need to specify the SPSiteDataQuery parameters on the control.
  • Change it to inherit from BaseFieldControl and store the query in a column of the page...allowing the Author to define the content displayed.
  • Inherit from RichLinkField and instead of using the SPSiteDataQuery to produce the XML, use the link to get the web page and produce your own XML from the columns. This way you can create your own 'Featured Article' control which gives the page Author the ability to select the article.

Essentially this class could provide a useful base on which you can build other controls...I will extend this class with some of these concepts when I get time.

If you would like to use the same concept to develop your accessible SharePoint publishing site visit SPWorks to download ARF, a free development framework.

Posted by Vincent Rothwell on Tuesday, 8 May 2007 22:32  - 10 Comments
Orininally printed from http://thekid.me.uk - Copyright Vincent Rothwell 2007
 

Comments

Sunday, 27 Jul 2008 10:36 by Thomas
Is there any way to write XSL output to a virtual document instead of using the HTML text writer?

Sunday, 27 Jul 2008 10:36 by Thomas
I also have tryed to get to htmltextwriter to write the page but there is no sign of text being writen to the page. Is there any thing that may need to get the writer to actually write the output? Thanks.

Sunday, 27 Jul 2008 10:36 by Vince
Thomas, You can write to your own HtmlTextWriter.... HtmlTextWriter tw = new HtmlTextWriter(new System.IO.StringWriter()); oTransform.Transform(xmlDoc, args, tw); string sResults = tw.InnerWriter.ToString(); --Vince

Sunday, 27 Jul 2008 10:36 by Thomas
Thanks, but I am still having problems. I am using the following to get the current list XML, but when I transform it with my XSL all I get is the html output of the XSL transform. I have tryed to view just the XML, and I get valid XML, but I only have problems with the XSL output. Is there another way to get this same information, or am I doing this completly wrong? XmlDocument _listXML = new XmlDocument(); SPList _list = SPContext.Current.List; SPView _lView = SPContext.Current.List.DefaultView; _listXML.LoadXml(_list.GetItems(_lView).Xml.ToString()); return _listXML; P.S. This is my first time using C#, so please go easy on me. Thanks

Sunday, 27 Jul 2008 10:36 by Thomas
Here is a link to my inputs and the output: <a href="http://tommyandkatie.com/downloads/output_error.zip">http://tommyandkatie.com/downloads/output_error.zip</a>

Sunday, 27 Jul 2008 10:36 by Vince
Thomas, I take it you got that XSL from the 'print list' sample on my site? Essentially the XSL is not for the XML you are using, try chaging the row template... <xsl:template match="z:row"> <tr> <xsl:for-each select="@*"> <td style="border-bottom:1px solid silver;padding:2px;"> <xsl:value-of select="."/> </td> </xsl:for-each> </tr> </xsl:template> This should give you your values. I would also recommend XsltMajic (from Microsoft). Its a bit old, but it does help with XML/XSLT, even allowing you to step through the transformation line by line. http://download.microsoft.com/download/8/d/f/8df935f4-7cce-4ef0-afca-ca0d8ab9ee88/xsltmajic%20profiler.exe --Vince

Sunday, 27 Jul 2008 10:36 by Thomas
Thanks, this helped a lot. I was just wondering if you knew any good sites or books on learning more about XSL. Thanks again.

Sunday, 7 Sep 2008 06:44 by lola
t4ZVJP please look at this <a href=" http://xanaxnx.u.yuku.com/ ">xanax addiction </a> %-DDD <a href=" http://vicodinnx.u.yuku.com/ ">vicodin online </a> 18047 <a href=" http://propecianx.u.yuku.com/ ">cheapest propecia</a> mqvptn <a href=" http://somanx.u.yuku.com/ ">soma online</a> mcr <a href=" http://diazepamnx.u.yuku.com/ ">purchase diazepam</a> 8[[

Thursday, 9 Oct 2008 07:29 by

Friday, 28 Nov 2008 01:12 by ghdfgh
dfgh



Url

Email

Comments