天天看點

定制Ribbon觸發工作流

在SharePoint中,如果手動觸發工作流,使用者需要選中item,然後點選workflow,導向另外一個頁面,才可以啟動工作流。

這裡我想添加一個Ribbon button,這個Ribbon button可以是一個下拉菜單,列出所有可以觸發的workflow,使用者隻需要選中一些item,然後直接點選下拉菜單中的一個就可以觸發工作流,不需要頁面轉向。

下面是具體實作方法的介紹:

第一步,我們添加一個Ribbon Button到Ribbon中。

我使用flyout anchor類型的Ribbon button,将其添加到Ribbon的Ribbon.ListItem.Workflow這個組中:

定制Ribbon觸發工作流

Ribbon的配置代碼:

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">

  <Control
                  Id="AdditionalPageHead"
                  Sequence="200"
                  ControlClass="shrenky.projects.workflowtrigger.RibbonLoaderControl"
                  ControlAssembly="$SharePoint.Project.AssemblyFullName$">
  </Control>
  <CustomAction Id="shrenky.projects.workflowtrigger.Ribbon"
                Location="CommandUI.Ribbon"
                RegistrationId="100"
                RegistrationType="List"
                Sequence="120"
                Title="Tigger"
                Description="Trigger workflow">
    <CommandUIExtension>
      <CommandUIDefinitions>
        <CommandUIDefinition Location="Ribbon.ListItem.Workflow.Controls._children">
          <FlyoutAnchor Id="shrenky.projects.workflowtrigger.Anchor"
              Sequence="20"
              LabelText="Workflows"
              Image32by32="/_layouts/15/images/shrenky.projects.workflowtrigger/Trigger.jpg"
              PopulateDynamically="true"
              PopulateOnlyOnce="false"
              PopulateQueryCommand="shrenky.projects.workflowtrigger.PopulateMenus"
              ToolTipTitle="Dynamic dropdown"
              ToolTipDescription="Shows dropdown made of buttons defined in JavaScript"
              TemplateAlias="o1">
          </FlyoutAnchor>
        </CommandUIDefinition>
      </CommandUIDefinitions>
      <CommandUIHandlers>
        <CommandUIHandler Command="shrenky.projects.workflowtrigger.TriggerMenuClick" CommandAction="javascript:WorkflowTrigger.Ribbon.RibbonComponent.get_instance().TriggerWorkflow(arguments[2].MenuItemId, '');"  EnabledScript="true" />
        <CommandUIHandler Command="shrenky.projects.workflowtrigger.MessageMenuClick" CommandAction="javascript:alert('No workflow associated on current list.')"  EnabledScript="true" />
      </CommandUIHandlers>
    </CommandUIExtension>
  </CustomAction>
</Elements>
           

首先添加了一個delegate control,用來注冊一些腳本比如JQuery,SPServices和Ribbon需要使用的PageComponent等等。

然後定義了一個FlyoutAnchor按鈕,這個按鈕沒有配置下拉菜單,因為下拉菜單是通過PopulateQueryCommand這個屬性來動态生成的,當使用者點選這個按鈕時,會執行“shrenky.projects.workflowtrigger.PopulateMenus”這個方法,這個方法是定義在PageComponent中的,用來構造下拉菜單的xml。

除了FlayoutAnchor之外還定義了兩個CommandUIHandler,一個是"shrenky.projects.workflowtrigger.TriggerMenuClick",供使用者點選FlyoutAnchor動态生成的下拉菜單的時候調用,另外一個暫時沒有用處。

delegate control除了用來加載腳本之外,還有一個用途就是将目前list上的所有關聯的workflow的資訊(association id,Title,Description)等等輸出到頁面上供之後的操作使用。

第二步,當使用者點選Ribbon的時候,顯示目前清單中所有可以觸發的工作流

定制Ribbon觸發工作流

向Flyout Anchor動态添加下拉菜單是通過PageComponent(WorkflowTriggerPageComponent.js檔案)實作的,關于pagecomponent的介紹可以參考《SharePoint 2010 as development platform》這本書中的例子。具體定義如下:

Type.registerNamespace('WorkflowTrigger.Ribbon');

WorkflowTrigger.Ribbon.RibbonComponent = function () {
    WorkflowTrigger.Ribbon.RibbonComponent.initializeBase(this);
}

WorkflowTrigger.Ribbon.RibbonComponent.get_instance = function () {
    if (!WorkflowTrigger.Ribbon.RibbonComponent.s_instance) {
        WorkflowTrigger.Ribbon.RibbonComponent.s_instance = new WorkflowTrigger.Ribbon.RibbonComponent();
    }
    return WorkflowTrigger.Ribbon.RibbonComponent.s_instance;
}

WorkflowTrigger.Ribbon.RibbonComponent.prototype = {
    focusedCommands: null,
    globalCommands: null,
    registerWithPageManager: function () {
        SP.Ribbon.PageManager.get_instance().addPageComponent(this);
        SP.Ribbon.PageManager.get_instance().get_focusManager().requestFocusForComponent(this);
    },

    unregisterWithPageManager: function () {
        SP.Ribbon.PageManager.get_instance().removePageComponent(this);
    },

    init: function () { },

    getFocusedCommands: function () {
        return ['shrenky.projects.workflowtrigger.PopulateMenus'];
    },

    getGlobalCommands: function () {
        return ['shrenky.projects.workflowtrigger.PopulateMenus'];
    },

    canHandleCommand: function (commandId) {
        if (commandId === 'shrenky.projects.workflowtrigger.PopulateMenus') {
            return true;
        }
        else { return false; }
    },

    handleCommand: function (commandId, properties, sequence) {
        if (commandId === 'shrenky.projects.workflowtrigger.PopulateMenus') {
            properties.PopulationXML = this.GetDynamicMenuXml();
        }
        else {
            return handleCommand(commandId, properties, sequence);
        }
    },

    GetDynamicMenuXml: function () {
        ... ...
    },

    selectedItems: [],
    workflowAssociationId: null,

    TriggerWorkflow : function(workflowAssoId, param)
    {
        ... ...
    },

    TriggerWorkflowExec: function ()
    {
        ... ...
    },

    TriggerWorkflowFailed: function ()
    {
        ... ...
    }
}

WorkflowTrigger.Ribbon.RibbonComponent.registerClass('WorkflowTrigger.Ribbon.RibbonComponent', CUI.Page.PageComponent);
WorkflowTrigger.Ribbon.RibbonComponent.get_instance().registerWithPageManager();
NotifyScriptLoadedAndExecuteWaitingJobs("WorkflowTriggerPageComponent.js");
           

動态生成下拉菜單是通過以下代碼來實作的:

if (commandId === 'shrenky.projects.workflowtrigger.PopulateMenus') {
            properties.PopulationXML = this.GetDynamicMenuXml();
        }
           

GetDynamicMenuXm這個方法負責産生下拉菜單的xml,在産生xml的時候,需要用到delegate control事先輸出到頁面上的workflow的資訊(即workflowtrigger.data.WorkflowData這個變量,一個json對象,包含了workflow的具體資訊例如id,Title等)

GetDynamicMenuXml: function () {
        var counter = 0;
        var data = workflowtrigger.data.WorkflowData;
        var xml = '<Menu Id = "shrenky.projects.workflowtrigger.Anchor.Menu">'
        + '<MenuSection Id="shrenky.projects.workflowtrigger.Anchor.Menu.MenuSection1" >'
        + '<Controls Id="shrenky.projects.workflowtrigger.Anchor.Menu.MenuSection1.Controls">';
        var len = data.length;
        for (var i = 0;  i < len; i++){
            counter = counter + 1;
            var current = data[i];
            var workflowAssociationId = current.WorkflowAssociationId;
            var workflowName = current.WorkflowTitle;
            var workflowDesc = current.WorkflowDescription;
            var buttonXml = String.format(
                    '<Button Id= "shrenky.projects.workflowtrigger.Anchor.Menu.MenuSection1.Menu{0}" '
                    + 'Command="shrenky.projects.workflowtrigger.TriggerMenuClick" '
                    + 'MenuItemId="{1}" '
                    + 'LabelText="{2}" '
                    + 'ToolTipTitle="{2}" '
                    + 'ToolTipDescription="{3}" TemplateAlias="o1"/>', counter, workflowAssociationId, workflowName, workflowDesc);
            xml += buttonXml;
        }
        if (counter === 0) {
            var msgXml = String.format(
                    '<Button Id= "shrenky.projects.workflowtrigger.Anchor.Menu.MenuSection1.Menu{0}" '
                    + 'Command="shrenky.projects.workflowtrigger.MessageMenuClick" '
                    + 'MenuItemId="1" '
                    + 'LabelText="{0}" '
                    + 'ToolTipTitle="{0}" '
                    + 'ToolTipDescription="{0}" TemplateAlias="o1"/>', 'No workflow associated on current list');
            xml += buttonXml;
        }
        xml += '</Controls>' + '</MenuSection>' + '</Menu>';
        return xml;
    },
           

在構造xml的時候,我們把下拉菜單的Command屬性賦予了“shrenky.projects.workflowtrigger.TriggerMenuClick”這個方法,就個方法就是之前定義的CommandUIHandler其中之一。當使用者點選下拉菜單時,觸發“shrenky.projects.workflowtrigger.TriggerMenuClick”這個方法,這個方法實際上是調用了另外一個方法TriggerWorkflow:

selectedItems: [],
    workflowAssociationId: null,

    TriggerWorkflow : function(workflowAssoId, param)
    {
        this.workflowAssociationId = workflowAssoId;
        var ctx = SP.ClientContext.get_current();
        var web = ctx.get_web();
        var lists = web.get_lists();
        var listId = SP.ListOperation.Selection.getSelectedList();
        var list = lists.getById(listId);
        var items = SP.ListOperation.Selection.getSelectedItems();
        this.selectedItems = [];
        if (items.length > 0) {
            for (var i in items) {
                var id = items[i].id;
                var item = list.getItemById(id);
                this.selectedItems.push(item);
                ctx.load(item);
            }
            ctx.executeQueryAsync(Function.createDelegate(this, this.TriggerWorkflowExec), Function.createDelegate(this, this.TriggerWorkflowFailed));
        }
        else {
            alert("Please select item");
        }
    },
           

這個方法首先擷取使用者選擇的所有item,将這些item儲存在selectedItems這個變量中,然後使用executeQueryAsync方法擷取這些item的資訊,供最後在這些item上觸發工作流使用。如果這些item的資訊擷取成功,則會調用回調函數TriggerWorkflowExec,正式觸發工作流了:

TriggerWorkflowExec: function ()
    {
        for (var i in this.selectedItems) {
            var item = this.selectedItems[i];
            var itemId = item.get_item("ID");
            var itemTitle = item.get_item("Title");
            var itemFileRef = item.get_item("FileRef");
            var webUrl = _spPageContextInfo.webAbsoluteUrl;
            var url = webUrl + itemFileRef;
            var id = this.workflowAssociationId;
            !function outer(id, url, itemTitle) {
                $().SPServices({
                    debug: true,
                    operation: "StartWorkflow",
                    async: true,
                    item: url,
                    templateId: id,
                    workflowParameters: "<Data/>",
                    completefunc: function () { SP.UI.Notify.addNotification("Start workflow on " + itemTitle + " Successfully", true); }
                });
            }(id, url, itemTitle);
        }
    },
           

這裡周遊使用者選擇的items,使用SPServices觸發工作流,使用SPServices觸發工作流的方法參見: 點選打開連結。

第三步, 選中需要啟動的工作流然後使用SPServices來觸發工作流,工作流正常啟動之後會有消息通知(這裡“Thinkpad”是item的title):

定制Ribbon觸發工作流

目前這個工具隻能運作在清單上,還很不完善,但是是一個很好的自定義ribbon的一個例子。

具體代碼可以在github上下載下傳:https://github.com/shrenky/sharepointprojects.git

繼續閱讀