Monday, February 2, 2009

Custom PublishingConsole

I generally like the publishing features in MOSS 2007. And as a general-purpose page editor control, I think the PublishingConsole is quite alright. But I do have the following issues with it:
  1. It is difficult if not impossible to make it look just the way you'd like.
  2. The accessibility aspects of the control are bad. It simply does not comply with W3C or WCAG standards.
  3. Configuring the console requires modifications to the CustomQuickAccess.xml and CustomEditingMenu.xml files. These are database files who are very difficult to modify programmatically. This makes an automatic deployment without manual steps difficult.
Depending on requirements these issues may be more or less important. One might argue that since the publishing controls are usually only visible to administrators, who cares of the look n' feel and accessibility? Well, that's true often, but not always...

In my current project, all of the above turned out to be serious issues. So we ended up building our own control. This is how it looks in its current draft version:


(Sorry for the swedish, but the controls are undo, check out, check in, publish, remove and confirm.)


How it's done
Reusing what we could from the standard controls were important of course. By going through code and by using Lutz Roeder's Reflector I finally managed to get my own publishing console working.

Here's how:

In the page layout .aspx file, add the following code:

<%@ Register Tagprefix="PublishingWebControls" Namespace="Microsoft.SharePoint.Publishing.WebControls" Assembly="Microsoft.SharePoint.Publishing, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

<!-- Edit mode support without PublishingConsole --> <PublishingWebControls:PublishingContext id="mPublishingContext" runat="server" />

<PublishingWebControls:EditModePanel runat="server" id="mEditContent" PageDisplayMode="Edit" SuppressTag="true"> <PublishingWebControls:SaveBeforeNavigationControl id="mSaveBeforeNavigation" runat="server" ></PublishingWebControls:SaveBeforeNavigationControl> </PublishingWebControls:EditModePanel>

The above code generates the required standard controls. The "save before navigation" control is of course only required when in edit mode.

Create a web control that somehow gives your users the ability to send commands such as check out, check in, undo check out, publish and so on.

The buttons (or links, or...) need to support both a clientside javascript and a page postback. In my own case i created a generic CommandBarControl that is fed commands by the page layout using it.

A command includes the following:

  • Name/Title/Description information, depending on your needs
  • Logic for determining visibility and enabled states, depending on your needs (I used delegates for these purposes myself)
  • Client-side javascript
  • Logic for performing the action (I used a delegate here as well)

Define code behind methods for the commands. Please see below for examples of code I used for some typical commands. Note that the examples are simplified and thus I have not compiled and tested these examples. Personally I use a three-layered, service oriented and test-driven architecture with Spring.Net for IoC & AOP caching and so. ;-)

Check out
A javascript is required here to get the user interface to switch to edit mode. Add the following javascript to your button's "onclick"-event:

javascript:if (document.forms['aspnetForm']['MSOLayout_InDesignMode'] != null) document.forms['aspnetForm']['MSOLayout_InDesignMode'].value = 1;if (document.forms['aspnetForm']['MSOAuthoringConsole_FormContext'] != null) document.forms['aspnetForm']['MSOAuthoringConsole_FormContext'].value = 1;if (document.forms['aspnetForm']['MSOSPWebPartManager_DisplayModeName'] != null) document.forms['aspnetForm']['MSOSPWebPartManager_DisplayModeName'].value = 'Design'

The code behind-logic should be similar to this:

public void CheckOut()
{
SPListItem item = SPContext.Current.ListItem;
if (item.File.Level != SPFileLevel.Checkout)
{
PublishingPage page = PublishingPage.GetPublishingPage(item);
page.CheckOut();
}
SPContext.Current.FormContext.SetFormMode(SPControlMode.Edit, true);
}

Undo check out
The code behind-logic should be similar to this:

public void UndoCheckOut()
{
SPListItem item = SPContext.Current.ListItem;
if (item.File.Level == SPFileLevel.Checkout)
{
item.File.UndoCheckOut();
CleanRedirect();
}
}

The CleanRedirect() method is necessary to remove the edit mode from the user interface. Microsoft themselves to something similar (according to the Reflector):

private static void CleanRedirect()
{
HttpContext context = HttpContext.Current;
SPUtility.Redirect(context.Request.Url.AbsoluteUri, SPRedirectFlags.Default, context);
}

Check in
The code behind-logic should be similar to this:

public void CheckIn(SPListItem item)
{
SPListItem item = SPContext.Current.ListItem;
if (item.File.Level == SPFileLevel.Checkout)
{
PublishingPage page = PublishingPage.GetPublishingPage(item);
page.CheckIn(string.Empty);
CleanRedirect();
}
}

Publish
The code behind-logic should be similar to this:

public void Publish(SPListItem item)
{
// Note: You could add an automatic check in here, now it is
// only possible to publish after a manual check in.
if (item.File.Level != SPFileLevel.Checkout)
{
item.File.Publish(string.Empty);
CleanRedirect();
}
}


Well, that's basically it. I hope this will help you on your way! And - please give me feedback whether this was useful or not. :-)

3 comments:

Anonymous said...

The CheckIn method is not working for me. The version is increased, but the content in fieldcontrols remains the same as befor the edit.

Anonymous said...

OK, you got me.. of course before checking-in you have to call item.Update()

Terborn said...

Hehe, yes, that's important. :-)

Hopefully the code will work out well for you. Please let me know if you find any other issues or if you have any suggestions for improvements!

Best of luck,
Daniel