Working with CommandAction and UrlAction


CommandAction and UrlAction elements are used to do something whenever a user clicks on a Custom Action. They could do something as simple as redirect a user to a url or execute some JavaScript Code. In a subsequent post, I will demonstrate how you can execute some C# server side code whenever a Custom Action is clicked. CommandAction element is used when the location of a custom action is the Ribbon whereas UrlAction element is used whenever the location of the action is anywhere else other than the Ribbon.

Assume you have created a custom action that looks like this. Since the location of the Custom Action is the ECB, we use the UrlAction element.

    <CustomAction Id="TestAction1"
                  Location="EditControlBlock"
                  RegistrationType="List"
                  RegistrationId="101"
                  Sequence="10000"
                  Title="Test Action1">
        <UrlAction Url="/_layouts/somepage.aspx"/>
    </CustomAction>

And another one which looks like this. This time we use the CommandAction element since the location of the Custom Action is the Ribbon

    <CustomAction Id="TestAction2"
                  Location="CommandUI.Ribbon"
                  RegistrationType="List"
                  RegistrationId="101">
        <CommandUIExtension>
            <CommandUIDefinitions>
                <CommandUIDefinition Location="Ribbon.Documents.Workflow.Controls._children">
                    <Button Id="StartWorkflowButton1"
                            Image16by16="/_layouts/Images/Workflows/Check_16x16.png"
                            Image32by32="/_layouts/Images/Workflows/Check_32x32.png"
                            LabelText="Test Action"
                            Command="TestCommand"
                            Sequence="10000"
                            TemplateAlias="o1"/>
                </CommandUIDefinition>
            </CommandUIDefinitions>
            <CommandUIHandlers>
                <CommandUIHandler Command="TestCommand"
                                  CommandAction="/_layouts/somepage.aspx"
                                  >
                </CommandUIHandler>
            </CommandUIHandlers>
        </CommandUIExtension>
    </CustomAction>

Both by default, assume that the value assigned to them is a URL. So if you did something like this, then on click of the action, SharePoint will attempt to redirect the user to a page called alert(…..

<CommandUIHandler Command="StartWorkflowCommand"
                  CommandAction="alert('Hello World')">
</CommandUIHandler>

If you want to execute some JavaScript code in response to the Action, then prefix it with the javascript keyword as follows –

<CommandUIHandler Command="StartWorkflowCommand"
                  CommandAction="javascript:alert('Hello World')">
</CommandUIHandler>

You can even have multiple lines of JavaScript code like this –

                <CommandUIHandler Command="TestCommand"
                                  CommandAction="javascript:alert('Hello'); 
                                                            alert('Hello again');">
                </CommandUIHandler>

OR

        <UrlAction Url="javascript:alert('Hello'); 
                                   alert('Hello again');"/>

However if you have multiple line of JavaScript to execute, it would be much better to encapsulate them in a function like this –

                <CommandUIHandler Command="TestCommand"
                                  CommandAction="javascript:SayHello();
                                                function SayHello() {
                                                    alert('Hello');
                                                    alert('Hello Again');
                                                }">
                </CommandUIHandler>

Sometimes you may want to use the ID of the list or selected list item in your code. In such cases, SharePoint provides a list of tokens you can use. For example the {ItemId} token provides the ID of the selected list item, {ListId} provides the ID of the list associated with the Action and {SiteUrl} provides the URL of the site containing the action. There are other tokens as well, but these are the most commonly used ones.

Out of all the tokens available, {ItemId} can be used only if the location of the Custom Action is the EditControlBlock. This is because the {ItemId} token returns just one ID and the only way in which one and only one ID can be returned is if you are working with the ECB. Actions in other locations like the Ribbon can be applied to multiple list items at a time and hence cannot use the {ItemId} token.

Either ways, the tokens cannot be used INSIDE the JavaScript code. For example, if you tried to do something like this, it wouldn’t work.

        <UrlAction Url="javascript:SayHello();
                        function SayHello() {
                            alert('You selected item id :' + {ItemId});
                            alert('from list id :' + {ListId});
                        }" />

Instead the tokens should be evaluated and their values passed to functions like this –

        <UrlAction Url="javascript:SayHello({ItemId}, '{ListId}');
                        function SayHello(itemid, listid) {
                            alert('You selected item id :' + itemid);
                            alert('from list id :' + listid);
                        }" />

In the above XML, notice that {ListId} is enclosed in quotes. This is important as List IDs are Guids and need to be passed as a string value to the function. The same is not necessary for {ItemId} as ItemIds are integers.

If you tried to so the same thing in the CommandAction attribute like this, it wouldn’t work.

<CommandUIHandler Command="TestCommand"
                    CommandAction="javascript:SayHello({ItemId}, '{ListId}');
                                function SayHello(itemid, listid) {
                                    alert('You selected item id :' + itemid);
                                    alert('from list id :' + listid);
                                }">
</CommandUIHandler>

Since {ItemId} token is not valid in the CommandAction attribute, it doesn’t get evaluated and it results in a JavaScript exception. If you look at the screenshot, then the {ListId} token has been successfully evaluated, whereas the {ItemId} token has been ignored.

image

To get the selected list items, you will need to write the following JS code in the function –

CommandAction="javascript:SayHello('{ListId}');
            function SayHello(listid) {
                var selecteditems = SP.ListOperation.Selection.getSelectedItems();
                                                    
                alert('You selected item id :' + selecteditems[0].id);
                alert('from list id :' + listid);
            }">

getSelectedItems method, as expected returns an array of selected list items.

As your code gets more and more complex, it would be very cumbersome to continue writing it as a string value assigned to the CommandAction or UrlAction attribute. Not only is it cumbersome because of lack of intellisense, color coding etc., it would also be virtually impossible to debug this code in case it doesn’t work as expected. Code reuse is also something that would not be possible. For example, maintaining this kind of code would be a nightmare.

<CommandUIHandler Command="StartWorkflowCommand"
                    CommandAction="javascript:RedirectToWorkflowLaunchPage('{SiteUrl}', '{ListId}')
                                  
                    function RedirectToWorkflowLaunchPage(siteurl, listid) {
                        //Some feedback to the user, while we re-direct to the application page.
                        SP.UI.Notify.addNotification('Redirecting...');

                        //Get a list of all the selected items (the items on which the workflow is supposed to run)
                        var items = SP.ListOperation.Selection.getSelectedItems();

                        //Iterate thru the array of selected items and create a comma seperated string consisting of all the selected item ids.
                        var selecteditems = '';
                        for (i in items) {        
                            selecteditems += items[i].id + ',';
                        }

                        //strip out the last ','
                        selecteditems = selecteditems.substr(0,selecteditems.length-1);

                        //redirect the user to a dummy aspx page.
                        //We pass the id of the list and the selected items in the querystring to the page
                        window.location = siteurl + '/_layouts/Workflows/workflowlauncher.aspx?listid=' + listid + ' &amp; itemids=' + selecteditems;
                    }"
                                  
                    EnabledScript="javascript:CheckSelectedItems()">                    
</CommandUIHandler>

To overcome all these issues, we can take our JavaScript code and move it to an external JS file.

The CommandAction or UrlAction attribute now becomes so much more cleaner.

<CommandUIHandler Command="StartWorkflowCommand"
                    CommandAction="javascript:RedirectToWorkflowLaunchPage('{SiteUrl}', '{ListId}')"                                  
                    EnabledScript="javascript:CheckSelectedItems()">                    
</CommandUIHandler>

In order to ensure that the JS file in which you have embedded your code is available to the pages in which your custom action is going to be rendered, you need to create another action and specify it’s location as ScriptLink. The ScriptSrc attribute points to the JS file.

    <CustomAction Id="ScriptSource1"
                  ScriptSrc="Workflows/RibbonActionCommands.js"
                  Location="ScriptLink"
                  Sequence="100">
    </CustomAction>

Once this is done, not only is it easy to debug the JavaScript code, we can also use the Developer Tools provided by the browser to debug it easily as well.

To debug it, follow these steps –

  1. Open the page containing the action in your browser. I am using IE 9
  2. Press F12 to open the Developer Tools window.
  3. If you are working with IE, then go to the Scripts tab. Use the scripts dropdown and select the .JS file containing your JavaScript code.

image

 

4. Put a breakpoint on the line from where you want to start debugging and click the Start debugging button.

5. Click on your custom action and you should hit your breakpoint and start debugging your code.