SharePoint: Purloining, Reusing and Customizing the ECB Menu System Part 1


In this series;

  1. Purloining, Reusing and Customizing the ECB Menu System Part 1
  2. Purloining, Reusing and Customizing the ECB Menu System Part 2

This series by Jan Tielens on customizing the ECB with Javascript is useful reading in conjunction with this post.

  1. Part 1
  2. Part 2
  3. Part 3

This post was supposed to be about performing long running operations using the LongRunningOperationJob class, however I got side tracked onto something else out of which popped this partial desconstruction of the SharePoint ECB menu system, and it’s always good to ken your tools.

The ECB is the drop down menu displayed against list and library items in a list or document library view, the menu items displayed are dependent on the list or library item, it’s state (checked-in/out) and is security trimmed according to your permissions with regard to the site and list/library item.

Document Library Item ECB
Document Library Item ECB
List Item ECB
List Item ECB
Version History ECB Menu
Version History ECB Menu

The menu consists of both Custom Actions and standard SharePoint actions which differ according to the list or library item and the list or library configuration (document library Moderation for example).

The ECB menu itself is constructed using a mix of server-side processing to output HTML markup into the page and client-side javascript which constructs the menu elements and handles the display/hover-state/click behaviour.

ECB Desconstruction.

The first part of the ECB’s inner workings is a small piece of VBScript which is output by the ListViewWebPart during rendering;

<script language=VBSCRIPT>
    On Error Resume Next
    Set EditDocumentButton = CreateObject(""SharePoint.OpenDocuments.3"")
    If (IsObject(EditDocumentButton)) Then
        fNewDoc3 = true
    Else
        Set EditDocumentButton = CreateObject(""SharePoint.OpenDocuments.2"")
        If (IsObject(EditDocumentButton)) Then
            fNewDoc2 = true
        Else
            Set EditDocumentButton = CreateObject(""SharePoint.OpenDocuments.1"")
        End If
    End If
    fNewDoc = IsObject(EditDocumentButton)
</script>

This is used by other client-side javascript functions to determine if SharePoint enabled editing applications are installed (i.e. Office).

The next part is a script block containing a javascript “Context” object which describes server settings, list settings and the current user, again this is output by the ListViewWebPart during rendering;

ctx = new ContextInfo();
ctx.listBaseType = 1;
ctx.listTemplate = 101;
ctx.listName = "{3F4F1B08-7782-4958-BAF2-77DB8EDFF351}";
ctx.view = "{55D111CD-2DCD-4164-BD3C-4EA02467AC6B}";
ctx.listUrlDir =  "\u002fDocuments";
ctx.HttpPath = "\u002f_vti_bin\u002fowssvr.dll?CS=65001";
ctx.HttpRoot = "http:\u002f\u002fportal";
ctx.imagesPath = "\u002f_layouts\u002fimages\u002f";
ctx.PortalUrl = "";
ctx.SendToLocationName = "";
ctx.SendToLocationUrl = "";
ctx.RecycleBinEnabled = -1;
ctx.OfficialFileName = "";
ctx.WriteSecurity = "1";
ctx.SiteTitle = "Test Site";
ctx.ListTitle = "Documents";
ctx.PortalUrl = null;
ctx.displayFormUrl = "\u002fDocuments\u002fForms\u002fDispForm.aspx";
ctx.editFormUrl = "\u002fDocuments\u002fForms\u002fEditForm.aspx";
ctx.isWebEditorPreview = 0;
ctx.ctxId = 1;
ctx.CurrentUserId = 1;
ctx.isModerated = true;
ctx.isForceCheckout = true;
ctx.EnableMinorVersions = true;
ctx.verEnabled = 1;
ctx.WorkflowsAssociated = true;
ctx.ContentTypesEnabled = true;
ctx1 = ctx;
Attribute Description
isVersions true or false, if true the context object is being used on the version history display page
listBaseType Equivilent to (int)list.BaseType
listTemplate Equivilent to (int)list.BaseTemplate
listName Equivilent to list.ID.ToString(“B”).ToUpper()
view A list view ID, appears to be used during list view filtering
listUrlDir The web relative Url of the list, this value is script encoded;
SPEncode.ScriptEncode
HttpPath The web relative Url to the OWSSVR.DLL, this value is script encoded;
SPEncode.ScriptEncode
HttpRoot The Url of the site or web, this value is script encoded;
SPEncode.ScriptEncode
imagesPath A relative (to HttpRoot) Url to an images folder used when creating ECB menu items
PortalUrl If not null, adds the “Add to My Links” action to the ECB menu
SendToLocationName If this and SendToLocationUrl are not null, an additional menu item is added to the ECB’s “Send To…” sub menu
SendToLocationUrl See above
RecycleBinEnabled If -1, deleted items are sent to the recycle Bin, otherwise they are Deleted
OfficialFileName If not null, allows sending the list/librray item to a records centre
WriteSecurity If 1, determines if the list/library item can be modified, subject to security permissions
SiteTitle The web or site title
ListTitle The list title
displayFormUrl The web relative Url to the list or library display properties form, this value is script encoded;
SPEncode.ScriptEncode
editFormUrl The web relative Url to the list or library edit properties form, this value is script encoded;
SPEncode.ScriptEncode
isWebEditorPreview For document libraries, if set to 0 (zero) the “Edit in …” desktop application ECB menu item is displayed
ctxId Identifies the context object and is set to an integer value by SharePoint, although it doesn’t appear to be used and can be set to a string value
CurrentUserId The integer Id of the current user
isModerated true or false, indicates if the list/library has Content Approval switched on
isForceCheckout true or false, indicates if the list/library has Require Check-in/out switched on
EnableMinorVersions true or false, indicates if the list/library has minor versions enabled
verEnabled true or false, indicates if the list/library has versioning enabled
WorkflowsAssociated true or false, indicates if the list/library, or any list content types, have workflow associations, equivilent to;
(list.WorkflowAssociations.Count > 0) || list.ContentTypes.Cast<SPContentType>().Any(ct => ct.WorkflowAssociations.Count > 0)
ContentTypesEnabled true or false, indicates if the list/library has Content Types enabled

ECB Menu Item Placement.

The next major piece, is the placement of an element to which an ECB should be attached, think of this as an ECB source item (document library item or listitem) which provides additional context information that controls how the ECB for this item is displayed and what menu options appear on it.

This piece is a heavily attributed HTML TABLE element with a single row and 2 table cells;

  1. The displayed content i.e. for a document library item it is an A element linking to the document, for a list item it is an A element linking to the display form for the item.
  2. The menu item image cell displayed when you hover over the entire TABLE element.
<TABLE 	id="5"
			class="ms-unselectedtitle"
			onmouseover="OnItem(this)"
			cellSpacing="0"
			height="100%"
			SUrl=""
			UIS="515"
			CId="0x010100C7F9C33B37F3454F98F93D38FB6AE72701010101"
			CType="Project Marketing"
			MS="3"
			CSrc=""
			HCD=""
			COUId=""
			OType="0"
			Icon="icpptx.gif|Microsoft Office PowerPoint|SharePoint.OpenDocuments"
			Ext="pptx"
			Type=""
			Perm="0x7fffffffffffffff"
			DRef="Documents"
			Url="/Documents/Beer Marketing Project.pptx"
			CTXName="ctx1"
			COut="0"
			SRed="">
<TBODY>
<TR>

<TD class="ms-vb" width="100%">
{your content goes here}
</TD>

<TD><IMG style="VISIBILITY: hidden" alt="Edit" src="/_layouts/images/menudark.gif" width="13"></TD>

</TR>
</TBODY></TABLE>

As you can see, the TABLE element (via attributes) describes the list/library item quite comprehensively;

Attribute Description
id The list or library item ID
SUrl The source url of a copied item, if specified and not null or a space, the “Publish to Source Location” ECB menu item is displayed
UIS The integer version number of the list/library item;
equivilent to listitem[“_UIVersion”]
CId The content type ID
CType The content type name
MS The integer representation of the items moderation status, equivilent to;
item.ModerationInformation != null ? ((int)item.ModerationInformation.Status).ToString() : “0”
CSrc For document library items the copy source, equivilent to item.CopySource, if specified and not null, the “Goto Source Item” ECB menu item is displayed
HCD If not null and set to “1”, adds the “Existing Copies” item to the ECB menu
COUId Integer, if the item is checked-out, the Id of the checked-out user
OType “0” for list items, for document library items it is the integer representation of the object type;
equivilent to (int)item.FileSystemObjectType
Icon For list items this is  icgen.gif||For document library items, it is the items icon, editing application text and open control prog id seperated by the vertical bar character.See section “MapExtToAll”.
Ext Empty for list items, for document library items it is the documents file extension without the preceding . (period) character
Type Empty for list items, for document library items it is the Prog ID of the document type open control
Perm A hex string, the list/library items effective permissions for the current user, equivilent to;
item.EffectiveBasePermissions.ToString(“x”)
DRef The server relative Url of the list or library, there must be no leading / character
Url The server relative Url of the list or library item, there must be a leading / character; for list items the Url includes the list item ID, as shown below;
/Lists/Tasks/3_.000
CTXName The name of the javascript “Context” object described previously, this ties the ECB menu to a specific context object
COut For document library items, “1” if checked-out offline, otherwise “0”
SRed Appears to be involved with server file redirects when navigating to documents which can/must be viewed in the browser rather than a desktop application e.g. InfoPath Browser Forms

MapExtToAll.

For this application, and many others I think, you need access to SharePoints file type extension and open control mapping XML file (docicon.xml in {SharePoint Root}/TEMPLATE/XML), as discussed here, the classes in the object model giving access to this information are marked internal, but we can use the following piece of code to give us access, though obviously this may not be supported in versions beyond 2010.

private static string MapExtToAll(string extension)
{
	var ddwrt =
		Assembly.Load(
			"Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c").GetType(
				"Microsoft.SharePoint.WebPartPages.DdwRuntime").GetConstructor(Type.EmptyTypes).Invoke(null);
	var type = ddwrt.GetType();
	var mi = type.GetMethod("MapToAll");
	var pi = type.GetProperty("Wp");
	pi.SetValue(ddwrt, new DataViewWebPart(), null);

	var extAll = (string)mi.Invoke(ddwrt, new object[] { "", extension });
	return extAll;
}

Most of the information discussed here has been groked by looking at INIT.JS, Core.JS and OWS.JS and other classes in Microsoft.SharePoint.dll, this only goes so far as much of the list rendering for instance is done within the (big big) parts of unmanaged SharePoint internals.

ECB Menu Custom Action Extensions.

That’s not quite to whole story though, as you know we can add our own items to the ECB menu by developing custom actions and deploying them via feature CAML as shown;

<CustomAction Id="MyCustomAction"
		RegistrationType="ContentType"
		RegistrationId="0x0101"
		ImageUrl="/_layouts/images/someimage.gif"
		Location="EditControlBlock"
		Sequence="101"
		Title="My Custom Action">
	<UrlAction Url="" />
</CustomAction>

You can register a custom action by Content Type, File Type, List/Library  or Prog ID and when appropriate these are added to the ECB, so where do they come from?

The class responsible for rendering this information is Microsoft.SharePoint.SPCustomActionElement in Microsoft.SharePoint.dll, and sadly its marked internal, and once again, the actual implementation resides in unmanaged code.
The only public means of accessing this class is to call SPList.RenderAsHtml(SPQuery query), and what this class renders (amongst a bunch of other stuff) is a hidden table containing all applicable extension ECB menu items, the Javascript code which builds an ECB menu for an item determines which of the extension ECB items are included, a sample of this is shown below.

<TABLE ID="ECBItems" style="display:none" height="0" width="0">
<TR>
	<TD>Edit in Browser</TD>
	<TD>/_layouts/images/icxddoc.gif</TD>
	<TD>/_layouts/formserver.aspx?XsnLocation={ItemUrl}&OpenIn=Browser</TD>
	<TD>0x0</TD>
	<TD>0x1</TD>
	<TD>FileType</TD>
	<TD>xsn</TD>
	<TD>255</TD>
</TR>
<TR>
	<TD>Edit in Browser</TD>
	<TD>/_layouts/images/icxddoc.gif</TD>
	<TD>/_layouts/formserver.aspx?XmlLocation={ItemUrl}&OpenIn=Browser</TD>
	<TD>0x0</TD>
	<TD>0x1</TD>
	<TD>ProgId</TD>
	<TD>InfoPath.Document</TD>
	<TD>255</TD>
</TR>
<TR>
	<TD>Edit in Browser</TD>
	<TD>/_layouts/images/icxddoc.gif</TD>
	<TD>/_layouts/formserver.aspx?XmlLocation={ItemUrl}&OpenIn=Browser</TD>
	<TD>0x0</TD>
	<TD>0x1</TD>
	<TD>ProgId</TD>
	<TD>InfoPath.Document.2</TD>
	<TD>255</TD>
</TR>
<TR>
	<TD>Edit in Browser</TD>
	<TD>/_layouts/images/icxddoc.gif</TD>
	<TD>/_layouts/formserver.aspx?XmlLocation={ItemUrl}&OpenIn=Browser</TD>
	<TD>0x0</TD>
	<TD>0x1</TD>
	<TD>ProgId</TD>
	<TD>InfoPath.Document.3</TD>
	<TD>255</TD>
</TR>
</TABLE>

The 3 table cells containing numbers are the security rights required for the ECB custom action to be added to the menu and the ECB menu items sequence;

  1. The first cell contains the required rights which are compared to the High 32bits of the Perm attribute of the table element discussed in the “ECB Menu Item Placement” section
  2. The second cell contains the required rights which are compared to the Lo 32bits of the Perm attribute of the table element discussed in the “ECB Menu Item Placement” section
  3. The third cell contains the ECB menu item sequence number

How it All Fits Together.

Building or creating an ECB menu for an item starts with the attributed TABLE element. This element’s onmouseover event is set to OnItem(this), so hovering over the TABLE element starts the chain of events which result in a fully built ECB menu.

Most of the functions mentioned below reside in CORE.JS.

<table id="5" class="ms-unselectedtitle" onmouseover="OnItem(this)" cellspacing="0">.....</table>

The call to OnInit ends up calling OnItemDeferCall.

OnItemDeferCall.

Sets TABLE.onclick to CreateMenu

Sets onmouseout to OutItem

Sets the following attributes of the IMG element within the TABLE’s image cell TD element;

.src is set to menudark.gif

.alt is set to “Edit” (localised)

.style is set to visibility:visible

Sets the TABLE’s image cell TD element class to “ms-menuimagecell

Sets the TABLE class to “ms-selectedtitle

OutItem.

Sets the TABLE’s image cell TD element class to “”

Sets the TABLE class to “ms-unselectedtitle

Clicking on the TABLE now calls the CreateMenu function which in turn calls the CreateMenuEx function, which actually builds the ECB menu.

CreateMenuEx.

The term “ctx” refers to the “Context” object discussed previously.

If the ctx.isVersions attribute is true, calls AddVersionMenuItems to add the version history ECB menu items

Version History ECB Menu

If the ctx.listBaseType=1 calls AddDocLibMenuItems which adds document item specific menu items

If the ctx.listTemplate=200 calls AddMeetingMenuItems which adds meeting list item specific menu items

For all other ctx.listBaseType and ctx.listTemplate values, calls AddListMenuItems which adds list item specific menu items

AddListMenuItems and AddDocLibMenuItems in CORE.JS initially check for the existence of functions named Custom_AddListMenuItems and Custom_AddDocLibMenuItems respectively, these functions if defined allow you to customize (Add) menu items to the ECB being built, if you return true from these Custom_XXX functions the SharePoint menu items are not added to the ECB menu and only the ones you added in your Custom_XXX function will be displayed.

See Jan Tielens series (links at the top of this post) for more information on customising the ECB menu using Javascript.

Both AddListMenuItems and AddDocLibMenuItems call through to AddSharedNamespaceMenuItems to add menu items common across both types of item

Both AddListMenuItems and AddDocLibMenuItems call through to AddWorkflowsMenuItem to add the “Workflows” menu item

The “Workflows” menu item is only added if:-

1. The WorkflowsAssociated attribute of the “Context” object is true

2. The user has rights to start Workflows

3. The CId attribute of the TABLE element is null/empty or if it does not begin with 0x010801, the ID of the Workflow Task content type

Next up is a call to AddFeatureMenuItems, which adds menu items from the selection of Custom Action ECB menu options (from the hidden TABLE element) which are applicable for the list or library item

While building the Custom Action ECB menu items, the ReplaceUrlTokens is used to translate Custom Action tokens used in the feature declaration to actual values;

{ItemId} is replaced by the id attribute of the TABLE element

{ItemUrl} is replaced by the Url attribute of the TABLE element

{SiteUrl} is replaced by the HttpRoot attribute of the “Context” object

{ListId} is replaced by the listName attribute of the “Context” object

The ECB menu is now full constructed and the final bits of CreateMenuEx position the menu and make it visible.

Where We Are.

This post has hopefully described how the ECB menu system works, how it’s built and where all of its component pieces come from.

In the next post, I’ll demonstrate how all of this information can be used in your custom development and configuration efforts, and although probably more suited to custom development with C# and Visual Studio, it is certainly possible to achieve similar results using nothing more that client side development technologies (Content Editor WebPart, SPServices, Client Object Model etc).

What can we do with this information:-

  1. Purloin – you want to display the SharePoint ECB menu (unmolested) for some list/library item from your own custom controls, webparts etc.
  2. Customize – you want to display a mix of your own custom menu items and the SharePoint ECB menu items for some list/library item from your own custom controls, webparts etc.
  3. Reuse – from your own custom controls or webparts, you want to use the ECB menu framework to display menus of your own, without having to roll your own or import a 3rd party dropdown menu solution, plus it will inherit any custom branding you’ve applied to your site.
ECB Reuse
ECB Reuse

Published by

Phil Harding

SharePoint Consultant, Developer, Father, Husband and Climber.

3 thoughts on “SharePoint: Purloining, Reusing and Customizing the ECB Menu System Part 1

  1. Thank you for this interesting post. I am looking forward to part2.

    Is it possible to use ECB in webpart where documents are queryed from multiple libraries?

  2. Hi Phil,

    For a given ECB, I would like to know whether it is associated with a list Item or a folder. Is that possible with the parameters ctx or m? Or perhaps another means?

    Thanks,
    Mike

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s