Take the 2-minute tour ×
SharePoint Stack Exchange is a question and answer site for SharePoint enthusiasts. It's 100% free, no registration required.

I'm trying to add a custom dropdownlist in the publishing rich editor control of SharePoint.

Basically, my goal is to set a specific class and a specific data value on an hyperlink. This will be handled by a jQuery plugin in another script.

My dropdown list should provides :

  • "Default": <a href="somewhere">Teleport</a>
  • "Value 1": <a href="somewhere" class="myplugin" data-myplugin="1">Teleport</a>
  • "Value 2": <a href="somewhere" class="myplugin" data-myplugin="2">Teleport</a>

and so on.

However, my drop down does not even appears in the ribbon.

As I want to setup link, I plugged my drop down within the link editing tab.

I'm quite sure my Ids are correct, because I also added a checkbox that is visible. This checkbox is similar as it will also toggle a specific css class. This checkbox works as expected.

Here is my custom action definition :

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <CustomAction
    Id="MyProject.RichEditorCustomisation"
    Location="CommandUI.Ribbon"
    Title="[Me] Customation Rich Editor">
    <CommandUIExtension>
      <CommandUIDefinitions>
        <CommandUIDefinition
          Location="Ribbon.Link.Link.Behavior.Controls._children">
          <CheckBox Command="MyProject.Ribbon.PageComponent.ToggleMyClass"
                    QueryCommand="MyProject.Ribbon.PageComponent.QueryToggleMyClass"
                    LabelText="toggle myclass"
                    Id="MyProject.RichEditorCustomisation.ToggleMyClass"
                    Sequence="30"
                    Alt="TODO"
                    TemplateAlias="o1" />
        </CommandUIDefinition>
        <CommandUIDefinition
          Location="Ribbon.Link.Link.Behavior.Controls._children">
          <DropDown 
                    Id="MyProject.RichEditorCustomisation.SetupMyPlugin"
                    Command="MyProject.Ribbon.PageComponent.DoSetupMyPlugin"
                    QueryCommand="MyProject.Ribbon.PageComponent.QuerySetupMyPlugin"
                    Sequence="31" Width="200px"
                    Alt="TODO"
                    TemplateAlias="dd1" >
            <Menu Id="MyProject.RichEditorCustomisation.SetupMyPlugin.Menu">
              <MenuSection Id="MyProject.RichEditorCustomisation.SetupMyPlugin.Menu.Section">
                <Controls Id="MyProject.RichEditorCustomisation.SetupMyPlugin.Menu.Section.Controls">
                  <Button  Id="MyProject.RichEditorCustomisation.SetupMyPlugin.DefaultButton"
                           Command="MyProject.Ribbon.PageComponent.SetupMyPlugin"
                           LabelText="Default" CommandValueId="0" 
                           />
                  <Button  Id="MyProject.RichEditorCustomisation.SetupMyPlugin.NewWindowButton"
                           Command="MyProject.Ribbon.PageComponent.SetupMyPlugin"
                           LabelText="Value 1" CommandValueId="1" 
                           />
                </Controls>
              </MenuSection>
            </Menu>
          </DropDown>
        </CommandUIDefinition>
      </CommandUIDefinitions>
    </CommandUIExtension>
  </CustomAction>
</Elements>

And here is my javascript file :

ExecuteOrDelayUntilScriptLoaded(
    function () {
        Type.registerNamespace("MyProject.RichEditorCustomisation");

        MyProject.RichEditorCustomisation.PageComponent = function () {
            MyProject.RichEditorCustomisation.PageComponent.initializeBase(this);
        };

        MyProject.RichEditorCustomisation.PageComponent.prototype = {
            commands: null,
            init: function () { },
            buildCommands: function () {
                if (SP.ScriptUtility.isNullOrUndefined(this.commands)) {
                    this.commands = [];
                    this.commands.push('MyProject.Ribbon.PageComponent.ToggleMyClass');
                    this.commands.push('MyProject.Ribbon.PageComponent.QueryToggleMyClass');
                    this.commands.push('MyProject.Ribbon.PageComponent.SetupMyPlugin');
                    this.commands.push('MyProject.Ribbon.PageComponent.DoSetupMyPlugin');
                    this.commands.push('MyProject.Ribbon.PageComponent.QuerySetupMyPlugin');
                }

                return this.commands;
            },
            getGlobalCommands: function () {
                return this.buildCommands();
            },
            canHandleCommand: function (commandId) {
                return true;
            },
            handleCommand: function (commandId, properties, sequence) {
                switch (commandId) {
                    case "MyProject.Ribbon.PageComponent.ToggleMyClass":
                        this.ToggleMyClass(properties[CUI.Controls.CheckBoxCommandProperties.On]);
                        break;
                    case "MyProject.Ribbon.PageComponent.QueryToggleMyClass":
                        properties[CUI.Controls.ToggleButtonCommandProperties.On] = this.isMyClassApplied();
                        break;
                    case "MyProject.Ribbon.PageComponent.DoSetupMyPlugin":
                        // Nothing to do ?!?!?
                        break;
                    case "MyProject.Ribbon.PageComponent.SetupMyPlugin":
                        setMyPluginValue(properties[CUI.Controls.DropDownCommandProperties.Value]);
                        break;
                    case "MyProject.Ribbon.PageComponent.QuerySetupMyPlugin":
                        properties[CUI.Controls.DropDownCommandProperties.Value] = getmyPluginValue();
                        break;
                }
                return true;
            },
            ToggleMyClass: function (hasMyClass) {
                var link = RTE.CanvasEvents.getNodeFromEvent('Link');
                if (!link) {
                    return;
                }
                RTE.SnapshotManager.takeSnapshot();
                if (hasMyClass) {
                    jQuery(link).addClass("myclass");
                }
                else {
                    jQuery(link).removeClass("myclass");
                }
                RTE.SnapshotManager.takeSnapshot();
            },
            isMyClassApplied: function () {
                var link = RTE.CanvasEvents.getNodeFromEvent('Link');
                return jQuery(link).hasClass("myclass");
            },
            setMyPluginValue: function (myPluginValue) {
                var link = RTE.CanvasEvents.getNodeFromEvent('Link');
                if (!link) {
                    return;
                }
                RTE.SnapshotManager.takeSnapshot();
                if (myPluginValue && myPluginValue != 0) {
                    jQuery(link).addClass("myplugin").data("mypluginvalue", myPluginValue);
                } else {
                    jQuery(link).removeClass("myplugin").removeData("myPluginValue");
                }
                RTE.SnapshotManager.takeSnapshot();
            },
            getmyPluginValue: function () {
                var link = RTE.CanvasEvents.getNodeFromEvent('Link');
                var $link = jQuery(link);
                var myPluginValue = $link.data("myPluginValue");
                if ($link.hasClass("myplugin") && myPluginValue > 0) return myPluginValue;
                else return 0;
            }
        }

        MyProject.RichEditorCustomisation.PageComponent.get_instance = function () {
            if (SP.ScriptUtility.isNullOrUndefined(MyProject.RichEditorCustomisation.PageComponent.singleton))
                MyProject.RichEditorCustomisation.PageComponent.singleton = new MyProject.RichEditorCustomisation.PageComponent();

            return MyProject.RichEditorCustomisation.PageComponent.singleton;
        }

        MyProject.RichEditorCustomisation.PageComponent.registerWithPageManager = function () {
            SP.Ribbon.PageManager.get_instance().addPageComponent(MyProject.RichEditorCustomisation.PageComponent.get_instance());
        }

        MyProject.RichEditorCustomisation.PageComponent.unregisterWithPageManager = function () {
            if (SP.ScriptUtility.isNullOrUndefined(MyProject.RichEditorCustomisation.PageComponent.singleton) == false)
                SP.Ribbon.PageManager.get_instance().removePageComponent(MyProject.RichEditorCustomisation.PageComponent.get_instance());
        }

        MyProject.RichEditorCustomisation.PageComponent.registerClass("MyProject.RichEditorCustomisation.PageComponent", CUI.Page.PageComponent);
        MyProject.RichEditorCustomisation.PageComponent.registerWithPageManager();
        NotifyScriptLoadedAndExecuteWaitingJobs("RichEditorCustomisation.js");
    }
, 'SP.Ribbon.js');

I can't see what's missing or wrong in my Xml declaration. I'd appreciate some help:)

PS: I've heard about the SharePoint Fluent Ribbon Api project. I keep an eye on it, but I don't want to introduce new dependency in my project yet.

share|improve this question

1 Answer 1

up vote 0 down vote accepted

What I had to change:

  1. Change the template of the dropdown list to o2:

    <DropDown 
        Id="MyProject.RichEditorCustomisation.SetupMyPlugin"
        Command="MyProject.Ribbon.PageComponent.DoSetupMyPlugin"
        QueryCommand="MyProject.Ribbon.PageComponent.QuerySetupMyPlugin"
        Sequence="31"
        Width="200px"
        Alt="TODO"
        TemplateAlias="o2" >
    
  2. I also specified a menuItemID for each element :

    <Button  Id="MyProject.RichEditorCustomisation.SetupMyPlugin.DefaultButton"
        Command="MyProject.Ribbon.PageComponent.SetupMyPlugin"
        MenuItemId="SetupMyPlugin_DefaultButton"
        LabelText="Default" CommandValueId="0" 
        />
    
  3. In the javascript file, I have to use the this keyword to call instance methods.

  4. I also have to use the CommandValueId property to get my value, and not the Value property/

    case "MyProject.Ribbon.PageComponent.SetupMyPlugin":
        this.setMyPluginValue(properties["CommandValueId"]);
        break;
    
  5. I also have to clear the browser cache each time I redeploy the solution.

    At this point, the drowdown appears.. However, the drop down list does not keep its value. Actually, everytime the QueryCommand is called, I return the correct value, but the dropdown does not reflect it.

  6. Unfortunately, The xhtml sanitizer strips all data-* attributes (see Publishing Rich HTML without any limitations in SharePoint 2010 for further backgroung).

    So I also have to rewrite my plugin (fortunately the plugin is written for this project) to support either data-* or pseudo css class <a class="myplugin mypluginvalue1"> construction.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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