Mar
21
2008

How to query cross-site lists in DataFormWebPart - Part 1. Build your own data source for DataFormWebPart

Problem - We have a site which contains many sub-sites. On this site we want to use DataFormWebPart to display all the pages in Pages library of all sub-sites.

James Tsai Blog - C# ASP.Net SharePoint VST. How to query cross-site lists DataFormWebPart Pages Library

Possible solutions:

1. We could use OOTB DataFormWebPart with SharePoint Designer to configure SPDataSource of the web part to do cross-site query. On SharePoint Designer Team Blog they have detailed article about how to do this. But I couldn't get this working after followed the instructions described in the article. And it isn't easy to find out which part of my configuration was causing the error.

2. Create a custom DataFormWebPart (inherit from DataFormWebPart class) with SPSiteDataQuery to query the data we want. And use XmlDataSource as data source for our custom web part.

The steps are:

a. Build your query

We created a SPSiteDataQuery with following configurations

SPSiteDataQuery qry = new SPSiteDataQuery();
qry.Lists = "<Lists ServerTemplate='850' Hidden='TRUE' />"; //Pages library has template Id 850
qry.Webs = "<Webs Scope='Recursive' />"; //Set scope to Recursive. To query current site and all sub-sites
qry.ViewFields = "<FieldRef Name='Title' /><FieldRef Name='Comments' /><FieldRef Name=Type/>"; //The fields we want to display
qry.RowLimit = 1000; //number limit of results
qry.Query = "<Where><Eq><FieldRef Name='ContentType' /><Value Type='Text'>My Content Type</Value></Eq></Where><OrderBy><FieldRef Name='Title' Ascending='FALSE' /></OrderBy>"; //query logic
DataTable tbl = web.GetSiteData(qry); // query SPWeb and store result as DataTable

Above code returns all pages created with "My Content Type" content type in Pages Library of parent site and all sub-sites.

b. Save result data collection to XML format

Next step is to transform result in DataTable into XML. We respect default XML format expected by DataFormWebPart to make sure minimal changes needed for XSL in later stage.

XmlDocument doc = new XmlDocument();
XmlNode queryResponse = doc.AppendChild(doc.CreateElement("dsQueryResponse"));
XmlNode root = queryResponse.AppendChild(doc.CreateElement("Rows"));
foreach (DataRow row in tbl.Rows)
{
         XmlElement rowNode = doc.CreateElement("Row");
         foreach (DataColumn col in row.Table.Columns)
         {
               string val = row[col].ToString();
               XmlAttribute att = doc.CreateAttribute(col.ColumnName);
               att.Value = val;
               rowNode.Attributes.Append(att);
          }
          root.AppendChild(rowNode);
}

Sample result of above code looks like this


<dsQueryResponse>

        <Rows>

                <Row ListId="7DC60945-8C38-47D9-BD82-422D2B6D873C" WebId="A696D3FB-998F-4C7E-BDF9-3EECA4BE8A34" ID="2" Title="General Information" Comments="subsite 2 page 1" Type="Info" />

                <Row ListId="389949C9-D040-4DD4-A18F-75F4401F580A" WebId="E00F6CCB-9893-4155-A811-5D4D6DED5054" ID="4" Title="Important Information" Comments="subsite 1 page 2" Type="" />

                <Row ListId="389949C9-D040-4DD4-A18F-75F4401F580A" WebId="E00F6CCB-9893-4155-A811-5D4D6DED5054" ID="2" Title="Important Policy" Comments="subsite 1 page 1" Type="" />

       </Rows>

</dsQueryResponse>

c. Create XmlDataSource (The data source for custom DataFormWebPart)

The last step at this stage is to create a XmlDataSource and bind XML data we created in previous step with this XmlDataSource.

XmlDataSource source = new XmlDataSource();
source.Data = doc.InnerXml;

All above code are placed inside the overrided DataBind() method of custom DataFormWebPart. DataBind() method will be called when SharePoint try to render custom DataFormWebPart.

public class ExtendedDataFormWebPart : DataFormWebPart
{

    public override void DataBind()
    {

          /* all code described in above steps */

          this.DataSource = source;

          base.DataBind();

    }

}

That's all for Part 1 of How to query cross-site lists in DataFormWebPart. In Part II, I will describe how to display our query result on page using XSL.

Mar
15
2008

How to change RichHtmlEditor control settings in ListFormWebPart (on NewItem.aspx, EditItem.aspx pages)

Problem
We want to change the settings of Rich HTML Editor (RTE) controls inside NewItem.aspx and EditItem.aspx pages (disable the ability to allowing user to change the font styles). NewItem.aspx and EditItem.aspx are the pages you see when you want to add/edit item in SharePoint list library.

If RTE control is put directly on the page then we can easily set its properties by setting the attributes of the control element. (for example we can do something like this <SharePointPublishingWebControl:RichHtmlField id='rteControl' AllowFonts='False'/>.

But what can we do if RTE control is part of ListFormWebPart?

James Tsai Blog .NET C# SharePoint VSTO ListFormWebPart RichHtmlEditor Control 

Solution

1. Modify the .js files in 12hive. I would not recommend this approach, because changing OOTB files in 12hive could result unknown issues to the system.

2. My solution is to override the JavaScript functions used for RTE control rendering. We can put our new override functions inside NewItem.aspx/EditItem.aspx. And when page initializing RTE, it will use the override functions instead the OOTB ones.

To override functions, we can basically copy and paste the original functions from OOTB .js files to our pages. And change the functions implementation WITHOUT modify the signature of the functions.

RTE_ConvertTextAreaToRichEdit and RTE2_ResetAllToolBarStates are two most common functions you may have to change.

James Tsai Blog .NET C# SharePoint VSTO ListFormWebPart RichHtmlEditor Control

Above screen shot shows RTE settings has been changed by new JavaScript functions.

Mar
9
2008

How to enable target audiences for custom content types. In custom list definition.

We often take following steps to create custom list and add it to the site

a. Create custom content types with custom site columns.

b. Create custom list definition and references custom content types.

c. Create list instance as feature and activate it on the site we want to have this custom list.

To enable audience targeting for custom content type during list instance creation. We have to add one more field reference to content type.

step 1.


<ContentType ID="ENTER_GUID_HERE"

             BaseType="ENTER_BASE_TYPE_GUID_HERE"

              Group="CONTENT_TYPE_GROUP"

             Name="Custom Content Type"

             Description="My custom content type">

            <FieldRefs>

                <!-- other custom site columns omitted -->

                <FieldRef ID="{61cbb965-1e04-4273-b658-eedaa662f48d}" Name="Target_x0020_Audiences"/>

            </FieldRefs>

</ContentType>


Above adds target audiences setting field to content type.

James Tsai .NET C# SharePoint VSTO Target Audiences Content Type

Step 2. we also have to add the following field to list definition.


<!-- List schema.xml -->

<Fields>

<!--other field refs omitted -->

<Field ID="{61cbb965-1e04-4273-b658-eedaa662f48d}"

         Type="TargetTo"

          Name="Target_x0020_Audiences"

          DisplayName="Target Audiences"

          Required="FALSE"

          StaticName="Target_x0020_Audiences"

          SourceID="http://schemas.microsoft.com/sharepoint/v3"

      />

</Fields>

After you added custom list on the site, you can see that target audiences has been enabled for content type.

James Tsai .NET C# SharePoint VSTO Target Audiences Content Type

Note: If you enable target audiences from the UI and you have more than one content types for a list. Target audiences field will be enabled only for the first content type.

You have to add target audiences field reference (step 1) to all your content types in list. For them to support target audiences.