Second Life of a Hungarian SharePoint Geek

September 21, 2011

Creating ECB menu items based on specific list properties using the client object model

In my past posts I’ve already described how to hide and modify ECB menu items through JavaScript and the SharePoint client object model.

Now I show how to create new menu items on demand based on the properties of the list.

Since it is not so different from the former examples, I do not provide the full solution for the current sample, only include the most important codes.

Our first Elements file contains the ScriptLink custom actions. The Sequence attribute of the CustomAction elements is important, as the later scripts depend on the former ones.

First we add a reference to jQuery, next we add our custom menus.js file that contain the logic for adding the new menu. Last we add a script block that (after the page is loaded) wait for the ECMAScript client OM script (sp.js) to be loaded, then calls the createEcbMenus method (included in menus.js).

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  3.   <CustomAction
  4.      ScriptSrc="scripts/jQuery/jquery-1.6.1.min.js"
  5.      Location="ScriptLink"
  6.      Sequence="1000" />
  7.   <CustomAction
  8.       Location="ScriptLink"
  9.       ScriptSrc="scripts/menus.js"
  10.       Sequence="1100" />
  11.   <CustomAction
  12.     Location="ScriptLink"
  13.     ScriptBlock="$.noConflict(); jQuery(document).ready(function() { ExecuteOrDelayUntilScriptLoaded(createEcbMenus, 'sp.js'); });"
  14.     Sequence="1200" />
  15. </Elements>

Following code is the content of the menus.js that we deploy to the Layouts\scripts folder.

  1. var inProgress = false;
  2. var list;
  3. var selectedListId;
  4.  
  5. function createEcbMenus() {
  6.     ExecuteOrDelayUntilScriptLoaded(createEcbMenusEx, 'Core.js');
  7. };
  8.  
  9.  
  10. function createEcbMenusEx() {
  11.     if (!inProgress) {
  12.         try {
  13.             inProgress = true;
  14.             var selection = SP.ListOperation.Selection;
  15.             if (selection != null) {
  16.                 this.selectedListId = selection.getSelectedList();
  17.                 if (selectedListId != null) {
  18.                     var context = SP.ClientContext.get_current();
  19.                     web = context.get_web();
  20.                     this.list = web.get_lists().getById(this.selectedListId);
  21.                     // we will use the Title property of the list later on
  22.                     // so add it to the query
  23.                     context.load(this.list, "Title");
  24.                     context.executeQueryAsync(Function.createDelegate(this, this.gotList), Function.createDelegate(this, this.failed));
  25.                 }
  26.             }
  27.         }
  28.         catch (e) {
  29.             alert("Error: " + e);
  30.             inProgress = false;
  31.         }
  32.     }
  33. }
  34.  
  35. function failed(sender, args) {
  36.     alert("Operation failed: " + args.get_message());
  37.     inProgress = false;
  38. }
  39.  
  40. function gotList() {
  41.     if (this.list.get_title() == 'My List') {
  42.         // just to be sure jQuery is loaded
  43.         if (jQuery) {
  44.             var id = "ECBItems_" + this.selectedListId.toLowerCase();
  45.  
  46.             jQuery('div[id*="ECBItems"]').each(
  47.                 function () {
  48.                     if (jQuery(this).attr('id') == id) {
  49.                         jQuery('div[id*="ECBItems"]').each(
  50.                         function () {
  51.                             if (jQuery(this).attr('id') == id) {
  52.                                 jQuery(this).append('<div><div>Custom menu item</div><div></div><div>javascript:doSomethingWithListItem({ItemId})</div><div>0x0</div><div>0x0</div><div>List</div><div>100</div><div>1000</div></div>')
  53.                             }
  54.                         });
  55.                     }
  56.                 });
  57.         }
  58.  
  59.     }
  60.     inProgress = false;
  61. }
  62.  
  63.  
  64. function doSomethingWithListItem(listItemId) {
  65.     alert("List item ID: " + listItemId);
  66. }

The createEcbMenus method we call from the last ScriptLink custom action (see above) simply waits for the loading of the Core.js file, then it calls the createEcbMenusEx method.

The createEcbMenusEx method submits a query using the client OM to get the Title property of the current list, that we use in this sample to decide if we should add the new menu item or not.

The most important part is the gotList callback method that is called when the submitted query succeeded. If the list is named “My List” then we check for the ECBItems DIV, and add the DIV structure for the new menu item to it. See former posts mentioned above for details of the format. In this case the menu title will be Custom menu item, and it calls the doSomethingWithListItem method when clicked.

Theoretically this code should work, but in practice, sometimes it simply does not. It has a very straightforward reason, namely it does not find the ECBItems DIV where it should to add the new item to. It happens when no custom action deployed for the EditControlBlock.

Note: If you need more info about it, check the former posts referred to above for the RenderECBItemsAsHtml method. BTW, that method is called by XsltListViewWebPart.OutputECB method that is called by the BaseXsltListWebPart.RenderWebPart method during the list view rendering. If you would like to know more about the custom action internals, I suggest you to read this post.

As a workaround we should add a dummy custom action element file that is never displayed. In this case I used the RegistrationId=”12345” and RegistrationType="List", so if you don’t have a list based on a template with ID 12345, you should be OK.

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  3.   <CustomAction Description="Dummy Custom Action that should be never displayed"
  4.               Id="MyCompany.DummyAction"
  5.               Location="EditControlBlock"
  6.               ImageUrl="/_layouts/images/dummy.png"
  7.               Sequence="1000"
  8.               Title="Dummy"
  9.               RegistrationType="List"
  10.               RegistrationId="12345" >
  11.     <UrlAction Url="javascript:alert('It is a dummy CA')"/>
  12.   </CustomAction>
  13. </Elements>

Deploying the dummy custom action forces the ECBItems DIV to be rendered, so our script will able to add the new item to the menu.

1 Comment »

  1. Any idea on using the same thing in SharePoint 2013 , div*=ecbitems does not seem to worl

    Comment by guillaume LaTour — February 3, 2014 @ 16:47


RSS feed for comments on this post. TrackBack URI

Leave a comment

Blog at WordPress.com.