Apr
15
2008

SharePoint Manager 2007 - A Must have tool every SharePoint Developer should download

AWESOME tool for SharePoint developer just like .NET Reflector for .Net developer.

SharePoint Manager 2007 

from the same guy who wrote WSPBuilder (another must have tool.. I think)

Words are not enough to describe how amazing this tool is. Go download it now! And you will addict to it.

Finally....we can all stop writing console app just for checking SharePoint Object Model properties. (well.. in most cases)

Download counts for this tool is surprisingly still low (1106 downloads at 16-April-08). That's one of the reasons I post it here to spread word :)

 

Here are some screenshots

View every Solutions installed on server..

James Tsai Blog .Net SharePoint ASP.NET C# - SharePoint Manager 2007 Solutions View

 

View Lists, Lists Definition or even in Browser view..

James Tsai Blog .Net SharePoint ASP.NET C# - SharePoint Manager 2007 Site Collections View

 

View List Schema..

James Tsai Blog .Net SharePoint ASP.NET C# - SharePoint Manager 2007 Schema View

Apr
11
2008

How to query cross-site lists in DataFormWebPart - Part 2. Use XSLT generated from SharePoint Designer to display data

Part 1. Build your own data source for Data Form Web Part

Part 2. Use XSLT generated from SharePoint Designer to display data  

In last post I have described how to build a data source and use it in custom DataFormWebPart to query cross-site lists.

Now, I am going to explain how you can display result data easily by using XSLT from SharePoint Designer.

Like I have mentioned in previous post, you should build data result in specific format. The format respects the XSLT that will be generated by SharePoint Designer. The format looks like this


<dsQueryResponse>

    <Rows>

        <Row Attribute1="" Attribute2=""  Attribute3=""/>

        <Row Attribute1="" Attribute2=""  Attribute3=""/>

    </Rows>

</dsQueryResponse>

Atrribute 1,2,3 can be field name.  

1. Create a new page with DataFormWebPart

Once you have data present in above format. You can then go into SharePoint Designer and open the library/list that your cross-site query will be queried on. And create a new temporary .aspx page in this library.

 NewAspxPage_thumb  

In our case, this page can be created in any sub-site's page library.

cross_list_structure_thumb1  

After you created a new page, insert a data view (DataFormWebPart)  control to this page.

InsertDFWP_thumb  

And select a data source from the data source panel for this control

 ClickForDataSource_thumb1

DataSourceLibrary_thumb1  

In this example we select "Pages" library as our data source. Because we want to use the site columns for the content type used in this page library as display fields. (Same fields we queried in result data in Part 1.)

SelectedLibrarySource_thumb1  

Surely You can create this temporary .aspx page in any place you like. But create it under right site avoid extra works for set up data source library manually. To get site columns you want.

ConnectToDiffLibrary_thumb

 

2. Customise the presentation of DataFormWebPart

Now, you can select all the fields you wanted to display in your cross-site lists DataFormWebPart. And insert them to the control.

InsertSelectedFields_thumb1 

Sample data view will be displayed on the page for you to preview the result.

 

3. Copy XSLT

If everything looks good to you, right click on the DataFormWebPart on the page and select "Web Part Properties..".

In XSL Editor box, copy the XSLT and save it to separate file for your custom cross-list DataFormWebPart to use.  

WebPartProperties_thumb2  


//XSLT saved in CrossList.xsl and use it via code.
//XslLink can also be set from Web Part Properties UI
CustomDataFormWebPart dfwp = new CustomDataFormWebPart();
dfwp.XslLink = "/Style Library/XSL Style Sheets/CrossList.xslt"  

Now, You should be able to display data without any changes to this XSLT. Because display fields matches attributes of our result XML data.

At this stage you have both custom cross-site DataFormWebPart control and XSLT presentation file.

Note

If you changed the display layout in Step 2 to enable Sorting and Filtering on column headers. You may find filtering does not work properly. It returns no data for you to perform filtering when you click on column headers.

 EnableFIlteringSorting_thumb1 

NoFilteringData_thumb1

It is because the default implementation of the method (the one return filtering data)  in DataFormWebPart class does not support our custom data source.

 

Coming up..

In Part 3. I will post about how to get filtering to work on column headers!

Apr
3
2008

Code blocks are not allowed in this file - SharePoint error with custom page layout

Yes, I do have C# code in custom page layout. And you probably thinking it is just another post about putting "<PageParserPath />" tag inside web.config.

But the answer is No. I managed to pin down the problem without having <PageParserPath /> tag. Well.. at least in my case.

Problem -

I got this error when I was creating a new publishing page, base on the custom page layout I have deployed to SharePoint site.

James Tsai .Net Blog - C# ASP.NET SharePoint VSTO - Page Layout code block error

Solution

The reason I know <PageParserParth /> is not the answer to this problem is that, only two out of seven page layouts I deployed having this issue.

So I opened those "broken" page layouts from the site in SharePoint designer and found following code got injected to the bottom of my custom page layouts


<html xmlns:mso="urn:schemas-microsoft-com:office:office"
xmlns:msdt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882"><head> <META name="WebPartPageExpansion" content="full"> <!--[if gte mso 9]><xml> <mso:CustomDocumentProperties> <mso:PublishingPreviewImage msdt:dt="string"></mso:PublishingPreviewImage> <mso:ContentType msdt:dt="string">Page Layout</mso:ContentType> <mso:MasterPageDescription msdt:dt="string"></mso:MasterPageDescription> <mso:PublishingAssociatedVariations msdt:dt="string">
</mso:PublishingAssociatedVariations> <mso:PublishingHidden msdt:dt="string">0</mso:PublishingHidden> <mso:PublishingAssociatedContentType msdt:dt="string">
;#Agenda item;#0x010100C568DB52D...;#</mso:PublishingAssociatedContentType> </mso:CustomDocumentProperties> </xml><![endif]--> <title>Dummy Content Type</title></head>

I couldn't figure out how this got into my page layouts . But I am sure that SharePoint treated those "broken" page layouts differently as to other working page layouts.

I compared them side by side, read about PlaceHolderPageTitle is the root of all evil  post. I found in my broken page layouts they all got lower case <asp:content /> tag for PlaceHolderPageTitle place holder. So I changed <asp:content /> to <asp:Content />, and it fixed the problem!


<asp:Content contentplaceholderid="PlaceHolderPageTitle" runat="server"> <SharePointWebControls:FieldValue id="PageTitle"
FieldName="Title" runat="server"/> </asp:Content>

Unlike Waldek's situation in the his post, I do have PlaceHolderPageTitle place holder in my page layouts. So this place holder is case sensitive? (because all my other place holders still use lower case <asp:content /> tag. )

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.