Friday 30 November 2007

Applying a theme to SPLongOperation

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

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

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


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

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



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


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

Wednesday 14 November 2007

RSViewer WebPart and Managed Urls

I have recently been developing a SharePoint solution that runs Reporting Services in SharePoint integration mode. I have a Site Definition for provisioning site collections and found that when I created a page with a ListView WebPart and connected it to a Reporting Services Report Viewer WebPart I received the following message in the report viewer web part.


The item 'http://www.mysite.com/[managedurl]/[SiteCollectionUrl]/[managedurl]/[SiteCollectionUrl]/[ReportLibrary]/MyReport.rdl' cannot be found. (rsItemNotFound)

I quickly found that this is because the list view web part provides a a datarowview with a column called DocUrl that contains the server relative url to the document. But on consumption, the report viewer wewbpart prefixes this url with the site collection's url.


Example

I have a report library called "My Reports" that contains a report called "Report1.rdl". When on the root of the web application the site collection url is "http://www.mysite.com/" and the server relative url of the document is "/My Reports/Report1.rdl". The report view web part adds them together to get.

http://www.mysite.com/My Reports/Report1.rdl - That's fine

Now lets consider the effect of the site collection being on a managed url.

The site collection url would become "http://www.mysite.com/sites/site1" the server relative url of te report would be "/sites/site1/my reports/report1.rdl". Put them together

http://www.mysite.com/sites/site1/sites/site1/My Reports/Report1.rdl - ooops



To fix this I created a web part to act as a proxy between the ListView web part and the RS Viewer WebPart.


Code

using System;
using System.Runtime.InteropServices;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Serialization;

using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.WebPartPages;
using System.ComponentModel;
using System.Data;
using System.Collections.Generic;

public class MyWebPart : System.Web.UI.WebControls.WebParts.WebPart, IWebPartRow
{
    Queue callbackQueue = new Queue();
    object rowData;
    PropertyDescriptorCollection rowSchema;

    public MyWebPart()
    {
        this.ExportMode = WebPartExportMode.All;
    }

    protected override void CreateChildControls()
    {
        return;
    }

    [ConnectionConsumer("AttrReportPathRowConsumer", "ReportDefinition", AllowsMultipleConnections = false)]
    public void SetRowProvider(IWebPartRow reportPathProvider)
    {
        if (reportPathProvider != null)
        {
            reportPathProvider.GetRowData(new RowCallback(this.OnReportPathFromProvider));
        }
    }

    [ConnectionProvider("Row", "blagh", typeof(DataFormProviderConnectionPoint), AllowsMultipleConnections = true)]
    public IWebPartRow GetProviderInterface()
    {
        return this;
    }

    protected override void RenderContents(HtmlTextWriter writer)
    {
        if (this.rowData != null)
        {
            DataRowView view = this.rowData as DataRowView;
            if (view != null)
            {
                int index = view.Row.Table.Columns.IndexOf("DocUrl");
                if (index != -1)
                {
                    writer.Write(view.Row.ItemArray[index].ToString());
                }
            }
        }
    }

    private void OnReportPathFromProvider(object rowData)
    {
        DataRowView view = rowData as DataRowView;
        if (((view != null) && (view.Row != null)) && ((view.Row.ItemArray != null) && (view.Row.ItemArray.Length > 0)))
        {
            int index = view.Row.Table.Columns.IndexOf("DocUrl");
            if (index != -1)
            {
                if (!SPContext.Current.Site.ServerRelativeUrl.Equals("/"))
                {
                    string url = SPContext.Current.Web.Site.ServerRelativeUrl;
                    string serverRelativeReportPathUrl = view.Row.ItemArray[index].ToString();
                    string siteRelativeReportPathUrl = serverRelativeReportPathUrl.Remove(0, url.Length);
                    view.Row[index] = siteRelativeReportPathUrl;
                    view.Row.AcceptChanges();
                }
            }
        }
        this.rowData = view;
        this.rowSchema = null;
        sendRow();
    }

    private void sendRow()
    {
        while (callbackQueue.Count > 0)
        {
            RowCallback callback = callbackQueue.Dequeue();
            callback(this.rowData);
        }
    }


    #region IWebPartRow Members

    void IWebPartRow.GetRowData(RowCallback callback)
    {
        if (this.rowData != null)
        {
            callback(this.rowData);
        }
        else
        {
            callbackQueue.Enqueue(callback);
        }
    }

    PropertyDescriptorCollection IWebPartRow.Schema
    {
        get
        {
            if (this.rowSchema == null)
            {
                try
                {
                    this.rowSchema = TypeDescriptor.GetProperties(this.rowData);
                }
                catch (Exception)
                {
                    return new PropertyDescriptorCollection(null);
                }
            }

            return this.rowSchema;
        }

    }

    #endregion
}