Tuesday, 19 February 2008
Fix For Bug With CustomAction (with source code)
http://chrissyblanco.blogspot.com/2007/12/bug-with-customaction-in-actions-menu.html
To sum up, if you define a CustomAction as part of a feature and use tokens in the URL e.g. <UrlAction Url="~site/Test.aspx?List={ListId}"/>, the custom action will work fine in a document library. However, if you add a web part representing the same document library to another page the tokens in the url do not get replaced. This is a very irratating bug.
The fix.
The only way I could see to fix this, was to replace the tokens using javascript. The menu options are available from the dom and the webpart also creates a context object in javascript to represent the displayed list.
The challenge.
To fix every occurance in a web app.
The idea.
Control Adapters. I simple idea but it presented a few difficulties to get this working in SharePoint.
The solution.
The SharePoint solution contains a WebApplication feature that when activated submits a JobDefinition to modify the compat.browser file already provisioned in the web app. The modification is a single control adapter entry to render all listviewwebparts using my control adapter.
The control adapter then allows the listviewwebpart to render as normal and then renders some javascript afterwards that fixes the rendered listviewwebpart.
Download the solution here seed.hf1.wsp. After installing and deploying the solution, run
stsadm -o activatefeature -name seed.hf1 -url <target url>
Download the source code here seed.hf1.zip
More
I am planning to blog in more detail about the techniques used in this solution, but I want to break them down into a small series of more detailed blogs. In the meantime, have a look through my source code and please leave any question or comments.
Friday, 30 November 2007
Applying a theme to 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
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
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
}
