在SharePoint中,如果手動觸發工作流,使用者需要選中item,然後點選workflow,導向另外一個頁面,才可以啟動工作流。
這裡我想添加一個Ribbon button,這個Ribbon button可以是一個下拉菜單,列出所有可以觸發的workflow,使用者隻需要選中一些item,然後直接點選下拉菜單中的一個就可以觸發工作流,不需要頁面轉向。
下面是具體實作方法的介紹:
第一步,我們添加一個Ribbon Button到Ribbon中。
我使用flyout anchor類型的Ribbon button,将其添加到Ribbon的Ribbon.ListItem.Workflow這個組中:

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的時候,顯示目前清單中所有可以觸發的工作流
向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的一個例子。
具體代碼可以在github上下載下傳:https://github.com/shrenky/sharepointprojects.git