5

I have two custom actions that are currently deployed to a SharePoint 2013 Forms Library. They are working perfectly fine with the exception of a less than favorable method.

What happens now is that when the user clicks on the custom action, it then redirects to a Default.aspx page and then executes my JavaScript. Here is a very similar thread but it had contradicting answers: Call JavaScript function for a custom action in an app for SharePoint 2013

Here is the code:

Elements.xml for Menu Item Custom Action

NOTE: Whenever I do this, Visual Studio won't allow me to deploy the app due to how the UrlAction Url is formatted.

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <CustomAction Id="dbba1823-1524-4dd9-be18-0b1a547a2a32.LaunchApp"
                RegistrationType="FileType"
                RegistrationId="xml"
                Location="EditControlBlock"
                Sequence="10001"
                ImageUrl="myPics/myImage.ico"
                Title="Launch In Windows 8 app">
    <!-- 
    Update the Url below to the page you want the custom action to use.
    Start the URL with the token ~remoteAppUrl if the page is in the
    associated web project, use ~appWebUrl if page is in the app project.
    -->
    <UrlAction Url="~appWebUrl/Pages/Default.aspx?HostUrl={HostUrl}&amp;ItemURL={ItemUrl}&amp;Source={Source}" />
  </CustomAction>
</Elements>

What I would like to do is take away the uneccessary step of redirecting to another page to execute that JavaScript. I would like it to be executed once the user hits the custom action. I have seen a few threads on this but none appear to work. This is basically what I want:

Preferred method in Elements.xml file

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <CustomAction Id="dbba1823-1524-4dd9-be18-0b1a547a2a32.LaunchApp"
                RegistrationType="FileType"
                RegistrationId="xml"
                Location="EditControlBlock"
                Sequence="10001"
                ImageUrl="myPics/myImage.ico"
                Title="Launch In Windows 8 app">
    <!-- 
    Update the Url below to the page you want the custom action to use.
    Start the URL with the token ~remoteAppUrl if the page is in the
    associated web project, use ~appWebUrl if page is in the app project.
    -->
    <UrlAction Url="../scripts/myScript.js" />
  </CustomAction>
</Elements>

Current Attempt

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <CustomAction ScriptSrc="~sites/Scripts/App.js" Location="ScriptLink" Sequence="1">

  </CustomAction>
  <CustomAction Id="decb57e5-4b37-4d87-af85-fd67173e4e91.RibbonAction"
                RegistrationType="List"
                RegistrationId="115"
                Location="CommandUI.Ribbon"
                Sequence="10001"
                Title="action">
    <CommandUIExtension>
      <!-- 
      Update the UI definitions below with the controls and the command actions
      that you want to enable for the custom action.
      -->
      <CommandUIDefinitions>
        <CommandUIDefinition Location="Ribbon.Library.Settings.Controls._children">
          <Button Id="Ribbon.Library.Settings.RibbonActionButton"
                  Alt="action"
                  Sequence="100"
                  Command="Invoke_RibbonActionButtonRequest"
                  LabelText="action"
                  TemplateAlias="o1"
                  Image32by32="https://mysite.sharepoint.com/sites/spdev/demo/SiteAssets/myicon.ico" 
                  Image16by16="https://mysite.sharepoint.com/sites/spdev/demo/SiteAssets/myicon.ico" /> 
        </CommandUIDefinition>
      </CommandUIDefinitions>
      <CommandUIHandlers>
        <CommandUIHandler Command="Invoke_RibbonActionButtonRequest"
                          CommandAction="javascript:myFunction();"/>
      </CommandUIHandlers>
    </CommandUIExtension >
  </CustomAction>
</Elements>

Thanks in advance for any helpful input.

1
  • Hi. Please see my answer below from 9th February 2016 and consider marking it as a solution.
    – flayman
    Nov 22, 2017 at 12:29

3 Answers 3

4

*Edited to actually provide a (hopefully) useful answer

It is unfortunate that there is so much confusion in the MS developer community about what is allowed in custom actions in SharePoint 2013. Trying to be helpful, many have offered answers but given examples for SharePoint 2010. The solutions offered are not relevant to SharePoint 2013 add-in development and such answers are only adding to the confusion. They do not even build in SharePoint Add-in Visual Studio projects. Like many others, I have been looking for a way to do this. I have finally found it.

I used this article as a guide: https://lixuan0125.wordpress.com/2014/03/04/add-custom-action-through-csom-with-app/ (credit goes to the author, lixuan0125)

You need to add the custom action in the App Installed event handler with client side code. Some others have alluded to this technique without enough detail to be getting on with. The article gives an example of a static method, which is apparently meant to be called from the event receiver that is created when you set "Handle Add-in Installed" to True in the project properties for the Add-in project. I simplified the approach and my AppEventReceiver class looks a bit like this:

public class AppEventReceiver : IRemoteEventService
{
    public SPRemoteEventResult ProcessEvent(SPRemoteEventProperties properties)
    {
        SPRemoteEventResult result = new SPRemoteEventResult();

        switch (properties.EventType)
        {
            case SPRemoteEventType.AppInstalled:
                HandleAppInstalled(properties);
                break;
        }

        return result;
    }

    private void HandleAppInstalled(SPRemoteEventProperties properties)
    { 
        using (ClientContext clientContext = TokenHelper.CreateAppEventClientContext(properties, false))
        {
            if (clientContext != null)
            {
                try {
                    string title = "Example custom action";

                    clientContext.Load(clientContext.Web);
                    List list = clientContext.Web.Lists.GetByTitle("The title of your list"); // or use GetByID or whatever
                    UserCustomActionCollection collUCA = list.UserCustomActions;
                    clientContext.Load(list);
                    clientContext.Load(collUCA);
                    clientContext.ExecuteQuery();

                    bool found = false;
                    UserCustomAction newUCAToRemove = null;
                    for (int i = 0; i < collUCA.Count; i++)
                    {
                        if (collUCA[i].Title == title)
                        {
                            newUCAToRemove = collUCA[i];
                            found = true;
                            break;
                        }
                    }

                    if (found)
                    {
                        newUCAToRemove.DeleteObject();
                    }

                    // Always replace existing custom action
                    UserCustomAction action = collUCA.Add();
                    action.Location = "CommandUI.Ribbon";
                    action.Sequence = 1000;
                    action.Title = title;
                    // Note: Location attribute can be any valid ribbon node with controls
                    action.CommandUIExtension = @"<CommandUIExtension><CommandUIDefinitions>"
                           + "<CommandUIDefinition Location=\"Ribbon.Documents.Manage.Controls._children\">"
                           + "<Button Id=\"InvokeAction.Button\" TemplateAlias=\"o1\" Command=\"Invoke_Command\" CommandType=\"General\" LabelText=\"Say hello to me\" Image32by32=\"_layouts/15/images/placeholder32x32.png\" Image16by16=\"_layouts/15/images/placeholder16x16.png\" />"
                           + "</CommandUIDefinition>"
                           + "</CommandUIDefinitions>"
                           + "<CommandUIHandlers>"
                           + "<CommandUIHandler Command =\"Invoke_Command\" CommandAction=\"javascript:alert('hi there');\" />"
                           + "</CommandUIHandlers></CommandUIExtension>";

                    action.Update();

                    clientContext.ExecuteQuery();
                } catch(Exception ex)
                {
                    throw new System.Net.WebException("Error when handling App Installed event.", ex);
                }
            }
        }
    }
}

You need to make sure that you give the Add-in sufficient permission to set custom actions on the list. This gave me some grief for a while. "Allow the add-in to make app-only calls to SharePoint." is essential in the AppManifest. It will work if you give FullControl permission for the Web scope and the installing user is able to grant it. I don't know yet whether anything lower or more granular will work.

3
  • I am afraid this does not give an answer. If you are not convinced with other answers, then you have an option to vote. Or else if you have some better answers, please share it.
    – Asad Refai
    Feb 9, 2016 at 10:52
  • Unfortunately I do not yet have the reputation score to leave comments, which is what I would have liked to do. This is the only way that I can add my 2 pence. By all means, please bump me up so that I can actually leave comments under unhelpful answers.
    – flayman
    Feb 9, 2016 at 10:58
  • 1
    I have edited the answer to provide a solution that I have now managed to verify as working.
    – flayman
    Feb 10, 2016 at 12:01
2

You can call javascript function in UrlAction:

<UrlAction Url="javascript:YourMethod();" />

Make sure you have js reference in master page or you can add it using following lines in your element file:

<CustomAction ScriptSrc="/scripts/myScript.js" Location="ScriptLink" Sequence="1"> </CustomAction>

10
  • 1
    Would that CustomAction be nested or outside and on top of the other CustomAction? I get the following error: The 'ScriptSrc' attribute is not declared. <CustomAction ScriptSrc="/Scripts/App.js" Location="ScriptLink" Sequence="1"> </CustomAction>
    – mwilson
    Aug 7, 2014 at 0:33
  • It should be inside <Elements> For Example: <?xml version="1.0" encoding="utf-8"?> <Elements xmlns="schemas.microsoft.com/sharepoint"> <CustomAction ScriptSrc="/scripts/myScript.js" Location="ScriptLink" Sequence="1"> </CustomAction> </Elements> Aug 7, 2014 at 0:57
  • Yes, I added it directly below the elements tag.
    – mwilson
    Aug 7, 2014 at 1:01
  • 1
    Did that and still received the same error. I see MSDN articles that say ScriptSrc is an attribute of CustomAction but visual studio returns it's not declared. Is it not supported for SharePoint 2013?
    – mwilson
    Aug 7, 2014 at 1:07
  • 1
    Please see my answer from Feb 9 '16 at 10:26. The example given here is for pre-2013 and has nothing to do with add-in development. JavaScript is not allowed in the CustomAction urls in SP 2013 add-in development. You can cheat it by having the App event receiver create the custom action programmatically.
    – flayman
    Jun 14, 2017 at 11:27
0

I tried the same approach using SharePoint 2013 provider hosted app but did not work out. I ended up writing CSOM code to add custom action for ScriptLink control. CSOM code executes when App is installed using AppInstalled remote event receiver. Please refer to blog here for more.

Also, to simplify user experience for the page redirection, you can launch the SharePoint dialog using few properties provided in CustomAction XML node. Please refer to my blog here for more.

1
  • Links no more working....
    – zainul
    Nov 30, 2019 at 5:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.