SharePoint Add-ins : Workflow の開発


SharePoint Add-ins 開発

こんにちは。

今回は、SharePoint Add-ins (SharePoint アドイン, 旧 App for SharePoint) のワークフロー開発について解説します。

 

New Workflow !

まず、今回から、Workflow の Platform (Engine) は、以下の通り新しくなっています。

  • WF 4 (Windows Workflow Foundation 4) がベースです。(WF 4 については、こちら を参照してください。)
    このため、XAML による表現力の向上 (Code を使わずに多くの処理を実現可能)、FlowChart などを使ったより直観的な表現、パフォーマンス向上、デザイン時のリッチなアクティビティ デザイナーの実現など、多くの点が改善されています。
  • SharePoint 2013 のワークフローでは、WF 4 に加えて、Workflow Manager 1.0 (及び、これと接続する Workflow Manager Client 1.0) と呼ばれる Middle-tier のエンジンが使用されています。
    このエンジンは、SharePoint とは異なるプロセスで実行され、SharePoint と接続して動作します。また、これまで CodePlex などで提供されていた HttpGet などの REST 関連のアクティビティも、この Workflow Manager のアクティビティとして統合されています。

補足 : SharePoint Server 2013 (On-Premise) を使用している場合は、SharePoint Server とは別に、Workflow Manager のセットアップをおこなってください。(既定では、サービス アプリケーションとして Workflow Service Application がインストールされていますが、Workflow Manager と接続されていません。) このインストール方法等については、MVP の山崎愛さんが下記で紹介してくれています。
http://shanqiai.weblogs.jp/sharepoint_technical_note/2013/02/sp2013-workflow-manager-10-%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB.html

補足 : また、On-Premise 用の開発などで Code の Custom Activity を作成した場合には、従来の SharePoint への登録だけでなく、Workflow Manager へも登録が必要なので注意してください。(SharePoint Online では、Code を使った Custom Activity の配置は不可能です。)

補足 : Microsoft Office Developer Tools for Visual Studio 2012 を導入すると、開発環境にも Workflow Manager がインストールされます。(Debug については、後述します。)

また、SharePoint では、Workflow Service Manager と呼ばれる中間層を通して、ワークフローの発行・変更などの管理 (Deployment Service)、インスタンスの管理 (Instance Service)、リモートの呼び出し (Messaging Service)、過去の SharePoint 2010 ベースのワークフローの呼び出し (Interop Service) などの処理が可能です。

SharePoint 2010 までのワークフローでは、Task (Workflow Task) を中心とした外部連携をしました。例えば、ワークフローでカスタム UI を提供する場合は、Custom Content Type を使って Task Form と関連付けたり、外部システム連携も Workflow Task を媒介として Web Service で連携をおこないました。(「SharePoint : Workflow 用 Web Service を使ってシステム連携をおこなう」を参照。)
SharePoint 2013 からは、こうした、ある意味「不自然な連携」は不要です。Task は、あくまでも Task 割り当ての目的だけで使えばよく、外部システムと連携したい場合は Web API を呼び出したり、UI 系のカスタム機能を構築したい場合は JavaScript Object Model (JSOM) を使ってワークフロー関連の UI を構築するなど、自然な連携が可能です。
以降では、まずは、これまで通りの Task を使った処理を構築し、つぎに、こうした新しい Workflow 開発を簡単に確認してみたいと思います。

なお、「MSDN : SharePoint 2013 workflow fundamentals」の説明を読むと、Azure Service Bus を介して Windows Azure 上の Workflow を呼び出せると書いていますが、「MSDN : What is Workflow Manager 1.0」には下記の通り記載されています。すなわち、現状 (2013 年 03 月時点) は、作成した Workflow は SharePoint にホストして使用してください。(将来、環境が整ったら、ドキュメントなども出てくることでしょう。)

Over time, we intend to provide both a Windows Azure service capability, as well as a user-installed service capability, thereby providing user flexibility and symmetry across on-premises and Azure offerings. Initially, the capability is being made available publically as a user-installed service (for on-premises installation or installation on Azure Virtual Machines).

(2013/09 削除 : 現在、予定はありません。。。)

特に、上述の Workflow Manager では、簡単な構成によってマルチ テナントによる実行が可能で (さらに、SharePoint からは、個別のテナントに接続可能)、こうした仕組みと組み合わせることで、将来的に、Azure 上にホストされたワークフローとの柔軟な連携も可能となるでしょう。

ただし、現時点でも、この後、見ていくように、外部とのメッセージングが容易なため、必要な処理を Azure など外に出して簡単に連携できます。特に、Office 365 では、Custom の Code Activity をホストできないため、コード レベルの処理が必要な場合には、Web API などの形で処理を分離して Azure 上に実装する意義は充分にあります。

 

構築前の事前知識 (準備など)

Workflow を追加するには、[Apps for SharePoint 2013] (SharePoint Add-ins) のプロジェクトを新規作成して、いつものように [追加] - [新しい項目] でワークフローを追加できます。(Visual Studio のソリューション エクスプローラーで、プロジェクトをマウスで右クリックして、[追加] - [新しい項目] メニューを選択します。)

追加をおこなうと Workflow の Desiger が表示されますが、上述の通り、今回から、WF 4 の Desiger が表示されます。このデザイナーの基本的な使い方は、以前紹介した「WF 4 (Windows Workflow Foundation 4) シリーズ」に記載していますので、はじめて WF 4 を使用する方は、あらかじめ参照しておいてください。(ここでは、WF 4 の基本的な操作などの解説は省略します。)

また、使用できるアクティビティは、WF 4 の標準アクティビティと、Workflow Manager と共に追加されているアクティビティ (HttpSend など)、さらに、SharePoint 用のアクティビティの、大きく 3 種類のアクティビティがあります。
注意していただきたいのが、ツールボックス (Toolbox) には、表示されていないアクティビティもあるという点です。例えば、下図の通り、メッセージング関連のアクティビティとして HttpSend のみが表示されていますが、実際には、HttpGet、HttpPost、HttpDelete なども内部で存在し、使用できます。(HttpSend でこれらの処理の代用が可能なので、大きな問題とはなりませんが。)

こうした隠れたアクティビティを使用するには、ツールボックス上をマウスで右クリックし、[アイテムの選択...] を選択してください。(Workflow Manager のアクティビティや SharePoint のアクティビティは、表示される下図の画面で [参照] ボタンを押して、Microsoft.Activities.dll、Microsoft.SharePoint.DesignTime.Activities.dll を追加します。)

補足 : Microsoft.Activities.dll、Microsoft.SharePoint.DesignTime.Activities.dll は、それぞれ下記にあります。
%programfiles%\Reference Assemblies\Microsoft\Workflow Manager\1.0\Microsoft.Activities.dll
%programfiles(x86)%\Microsoft Visual Studio 11.0\Common7\IDE\PublicAssemblies\Microsoft.SharePoint.DesignTime.Activities.dll

実際、このあとでも、こうしたツールボックスにないアクティビティをいくつか使用すると思います。

 

構築手順 - 基礎

では、実際の構築をおこなってみましょう。
今回から、Microsoft Office Developer Tools for Visual Studio 2012 の RTM 版を使用します。

まず、Visual Studio 2012 を起動して、[Apps for SharePoint 2013] (SharePoint Add-ins) のプロジェクトを新規作成します。今回は、SharePoint-hosted (SharePoint ホスト型) のプロジェクトを新規作成します。

Add-ins の Workflow では、リストに紐づいた「リスト ワークフロー」と、リストに紐づかない「サイト ワークフロー」が作成できますが、ここではリスト ワークフローを使用します。
このため、まず準備として、あらかじめ カスタム リストを作成しておいてください。(カスタム リストの作成方法については、「SharePoint Add-ins : List の開発と CSR」を参照してください。今回は、「List1」の名前のリストを作成したと仮定します。)

つぎに、Visual Studio のソリューション エクスプローラーで、プロジェクトをマウスで右クリックして、[追加] - [新しい項目] を選択します。
表示される画面で、[ワークフロー] (Workflow) を選択してワークフローを追加します。

ウィザードが表示されます。
まず、[リスト ワークフロー] か [サイト ワークフロー] か選択する画面が表示されるので、今回は [リスト ワークフロー] を選択します。

補足 : なお、[サイト ワークフロー] (Site Workflow) を選択した場合には、App web の /_layouts/15/workflow.aspx の画面からサイト ワークフローのインスタンスを追加 (ワークフローを実行) できます。(Add-ins で提供する画面などに、このページへのリンクなどを設定しておくと良いでしょう。)

つぎに、ワークフローを関連付けるリストを選択する画面が表示されるので、上記で作成したリスト (List1) を選択します。
また、今回、後述の通り、Task を使った処理を構築するため、関連付ける Task List として [新規作成] (New) を選択し (下図の赤枠を参照)、ワークフローの作成と同時に Task List も作成します。(これにより、「WorkflowTaskList」という名前のリスト インスタンスが作成されます。)
なお、あらかじめ、「タスク」(Task) のリスト インスタンスを新規追加して、これを Task List として設定しても構いません。

つぎに、ワークフローの開始方法を確認する画面が表示されます。
今回、リスト アイテムが新規作成された際にワークフローを自動開始するため、[項目が作成されたときにワークフローを自動的に開始] にチェックを付けておきます。(以降のサンプルでも、同様に自動開始するようにしておきましょう。)

以上でウィザードの設定が完了です。(ワークフローが新規追加されます。) なお、このウィザードで設定した内容は、作成された Workflow のプロパティ ウィンドウを使って、あとから変更 (設定) できます。

では、ワークフローを構築していきましょう !
まずは、SharePoint 2010 時代のように、リスト アイテムが作成されたら、Task をアサインするようなワークフローを構築してみましょう。(SharePoint 2013 で、どのように変わったか実感してください。)

今回、デモ (サンプル) として、リスト アイテムの作成者 (Author) に Task をアサインしますので、まずは、リスト アイテムの作成者を取得します。

まず、作成された Workflow1.xaml で [Sequence] アクティビティを選択し、Designer の左下の [変数] (Variables) をクリックします。そして、「authorId」という名前の Int32 型の変数を追加します。

つぎに、[LookupSPListItemInt32Property] アクティビティを Drag & Drop して [Sequence] アクティビティの内部に挿入します。

挿入した [LookupSPListItemInt32Property] アクティビティのプロパティ ウィンドウを表示し、下記の通りプロパティを設定します。
これにより、現在のリスト アイテムの登録者の Id 情報が、変数 authorId に挿入されます。

ListId : 現在のリスト (Current List)

ItemId : 現在の項目 (Current Item)

PropertyName : 登録者 (authorId)

Result : authorId (上記で作成した変数)

以降も、同様に、「変数作成」、「アクティビティ挿入」、「プロパティ設定」を繰り返していきます。Code の記述はいっさいおこないません。(「C# 式」と呼ばれる定義式の挿入はおこなえますが。)

つぎに、上記で取得した authorId からユーザー名を取得するため、[Sequence] アクティビティに下記の変数を追加します。なお、DynamicValue 型は、デザイン時に「型」が決まっていない値を受け取ることができる特別な型だと思ってください。

userValue : DynamicValue 型

userName : String 型

[LookupSPUser] アクティビティを挿入して、下記の通りプロパティを設定します。これにより、ユーザーに関するさまざまな情報が userValue に設定されます。

PrincipalId : authorId (上記の変数)

Result : userValue (上記の変数)

[GetDynamicValueProperties] アクティビティを挿入して、下記の通りプロパティを設定します。これにより、userValue から LoginName のプロパティだけを抽出して、変数 userName に設定します。

Source : userValue

Properties - エンティティの種類 : ユーザー (User)

Properties - 辞書 : LoginName userName

以上で、リスト アイテムの登録者のユーザー名 (i:0#.w|contoso\demouser1 形式の文字列) が userName に設定されます。

では、このユーザーに、Task の割り当てをおこないましょう。
今回は、標準のタスクを割り当て、タスクの承認・却下の情報 (Int 型の情報) を受け取ります。(Approved が 0、Rejected が 1 です。)
まず、この結果を設定する変数として、以下の変数を追加します。

taskResult : Int32 型

[SingleTask] アクティビティを挿入してプロパティを設定します。
実は、[SingleTask] アクティビティを挿入すると、下図のように、ほとんどのプロパティは設定済みの状態になっているはずです。そこで、必要に応じて、プロパティを変更します。

まず、[AssignTo] プロパティが空になっていると思いますので (ここは必須です)、上記の userName 変数を設定します。
また、[Outcome] プロパティに、上記で作成した変数 taskResult を設定します。

AssignTo : userName

Outcome : taskResult

なお、今回は SingleTask で単一タスクのみを割り当てますが、CompositeTask アクティビティを使って複数名にタスクを割り当てることも可能です。(Parallel と Serial の双方が選択できます。)

また、ここでは、タスクが完了するまで待って、「タスクの結果」(TaskOutcome) を取得していますが、Workflow Task の Custom Content Type を構築して、独自なプロパティを結果として取得することもできます。
また、出力結果として、TaskList の Item Id (TaskItemId) も取得できるので、あとからタスクのプロパティ (結果) を自由に参照して、結果に応じた柔軟な処理も構築できます。

さいごに、taskResult の内容を判定して、Workflow Status を設定します。
[If] アクティビティを挿入して、taskResult の結果に応じて、[SetWorkflowStatus] アクティビティでステータスを設定します。(すみません、そろそろ面倒になってきたので、詳細は省略します。下図のような感じです。)

以上で完了です。(さいごに、Permission の設定も忘れずに実施しておきましょう。)

ここでは、Primitive なアクティビティをそのまま使用しましたが、Custom Activity を (XAML で) 作成し、これらの Activity を 1 まとまりの処理として組み合わせることができます。(実際の開発では、こうした設計もちゃんとおこなっておきましょう。)

 

Debug と動作確認

2013/10 追記 : Visual Studio 2013 では、Office 365 においても、Azure Service Bus を使用したワークフローのデバッグが可能になりました。

あらかじめ、Azure Service Bus の名前空間を新規作成して接続文字列を取得しておきます。プロジェクトのプロパティ ウィンドウの [SharePoint] タブを選択し、下図の通り、[ワークフロー デバッグを有効にする] (Enable Workflow debugging) と [Windows Azure サービス バスによるデバッグを有効にする] (Enable debugging via Windows Azure Service Bus) をチェックして、[Windows Azure Service Bus の接続文字列] (Windows Azure Service Bus connecting string) にコピーした接続文字列を入力します。(あとは、ワークフローのアクティビティに F9 でブレークポイントを設定して、デバッグ実行をおこなってみてください。)

Azure Service Bus を介して、SharePoint Online (Office 365) から、localhost で実行されているワークフローを呼び出します。

以前、「SharePoint Add-ins : Remote Event Receiver の開発と Debug」でぼやいたように、Workflow や Event Receiver 開発において、デバッグは悩みの種です。

上述の通り、実行に際しては Workflow Manager が必要ですが、実は、On-Premises (SharePoint Server 2013) であれば、開発環境にインストールされた Microsoft.Workflow.TestServiceHost.exe を起動してワークフローをホストし、デバッグ (トラックなど) をおこなうことができるようになっています。
しかし、Office 365 (SharePoint Online) を使用している場合は、無論、こうした逆向きの (localhost への) 呼び出しはできません。

申し訳ありませんが、Office 365 をお使いの方は、プロジェクトのプロパティ画面を開き、[SharePoint] タブを選択して、下図の [ワークフロー デバッグを有効にする] のチェックを外しておいてください。

なお、デバッグをおこないたい場合は、今のところ、「オンプレミスを使って動作確認してください」という答えになります。(どなたか、良い方法を思いついた方は、アイデアください!)

では、動作を確認してみましょう。

F5 でデバッグ実行をおこない、App web の /Lists/List1 (~appWebUrl/Lists/List1) を表示します。
リスト (List1) が表示されるので、リスト アイテムを追加 (新規作成) します。追加後、/Lists/WorkflowTaskList を表示すると、ワークフロー タスクが作成されているのが確認できます。(下図)

このタスクをクリックしてタスクの編集画面を表示し、承認や却下をおこなうと、下図の通り、Workflow Status が設定されているのが確認できます。

補足 : .xaml ファイルは、配置タイプが「AppPackage」となっています。(「モジュール」など、App web のフィーチャーとして作成されるのではありません。) この .xaml ファイルは、App web の wfsvc フォルダーに配置されます。

 

Web API (REST サービス) との連携

つぎに、前述の通り、SharePoint 2013 らしい外部連携のシナリオを確認してみましょう。
まず、Workflow Manager で追加された REST API (Web API) 関連のアクティビティ (メッセージング アクティビティ) を使ったサンプルを構築します。

補足 : SharePoint 2010 のサンドボックス ソリューションでは外部ネットワークの接続は制限されていましたが、ここで紹介する Workflow (SharePoint Add-ins の Workflow) は Office 365 でも使用可能です。

Web API の作成手順から書いていると時間がかかるので (もう 1 時間以上、ブログを書き続けています。。。とほほ)、今回は、リクルート株式会社の ATND API を使用して外部サービス連携します。もちろん、実際の開発 (プログラミング) では、開発者の皆さんが作成した独自な Web API (SharePoint の外の API) と連携して動作させることも可能です。

今回のサンプルでは、リストに設定された eventid の内容を取得して、その owner の情報を ATND API から取得するワークフロー サンプルを構築してみましょう。

まず、準備として、あらかじめ、下図の eventid (文字列型)、owner (文字列型) の列 (フィールド) を持つカスタム リストを作成します。(カスタム リストの作成方法については、「SharePoint Add-ins : List の開発と CSR」を参照してください。)

上記と同様に、このリストに紐づいたリスト ワークフローを作成 (追加) します。(今回は、タスク リストは不要です。)

つぎに、ワークフローを構築します。
まず、上記同様、リスト アイテムから eventid の列 (フィールド) の値を取得するため、[Sequence] アクティビティに変数 eventId (String 型) を作成し、[LookupSPListItemStringProperty] アクティビティを使って、この変数に eventid 列の値を設定します。(詳細の手順は上記と同様ですので、省略します。PropertyName には、"eventid" を設定します。)

つぎに、Web API を呼び出して、結果の Json 文字列を DynamicValue 型の変数に設定します。
変数 eventValue (DynamicValue 型) を作成し、つぎに [HttpSend] アクティビティを挿入して、[HttpSend] アクティビティのプロパティを下記の通り設定します。

Method : GET

Uri : "http://api.atnd.org/events/?event_id=" + eventId + "&format=json"

ResponseContent : eventValue

ATND API のリファレンス」に記載されているように、結果は、下記のフォーマットの Json 文字列として返ってきます。(今回、結果は 1 件なので、イベント データは、要素が 1 つの配列として返ってきます。)
今回は、この中の owner_nickname (エンコードされています) を取得して、リスト アイテムの owner 列 (フィールド) に設定します。

{
  "results_returned":1,
  "results_available":1,
  "events":
  [
    {
      "event_url":"http://atnd.org/events/27457",
      "title":"Office365 LT\u5927\u4f1a \u7b2c\u4e00\u56de",
      "started_at":"2012-05-18T18:30:00+09:00",
      "ended_at":"2012-05-18T21:00:00+09:00",
      "updated_at":"2012-05-18T08:21:47+09:00",
      "deleted_at":null,
      "event_id":27457,
      "url":null,
      "owner_nickname":"\u76ee\u4ee3\u660c\u5e78",
      . . .
    }
  ],
  "results_start":1
}

そこで、変数 ownerNickname (String 型) を作成し、[GetDynamicValueProperty<T>] アクティビティを挿入して、下記の通りプロパティを設定します。(Generic の T として「String」を選択してください。)
なお、今回、データが抽出できなかったときのことはまったく考慮してませんので、実際の開発では、返ってくる件数の情報を取得して、きめ細かな処理を作成してください。(今回は、さぼります。)

Source : eventValue

PropertyName : "events(0)/owner_nickname"

Result : ownerNickname

さいごに、[SetFiled] アクティビティを挿入して、下記の通りプロパティを設定します。

FieldName : owner

FieldValue : ownerNickname

以上で完了です。

デバッグ実行をおこない、上記で作成したリストを表示してアイテムを追加します。
この際、eventid に「27457」と設定します。(http://api.atnd.org/events/?event_id=27457&format=json で、実際に値が取得できることを確認してみてください。)

しばらくすると、下図の通り、owner 列に目代さんの名前が設定されます。(目代さん、サンプルに使ってすみません !)

なお、Workflow Manager が提供するメッセージング アクティビティ (HttpSend、HttpGet、など) では、プロパティを使って HTTP ヘッダーの設定も可能なため、簡単な認証処理などもアクティビティで処理できます。
また、メッセージング アクティビティには SecurityToken プロパティも存在しています。このプロパティを GetS2SSecurityToken アクティビティと組み合わせて使うことで、server-to-server 認証をおこなって SharePoint に接続することもできるようです。(S2S 認証については、「.NET CSOM を使ったプログラミングと認証」を参照してください。) もちろん、SharePoint に接続するだけなら、そのためのアクティビティは既に存在するので、一般には、こうした処理をしなくても SharePoint 連携が可能です。

 

Custom UI と Object Model (JSOM) との連携

SharePoint 2013 の Object Model (または REST API) を使うと、Workflow Service Manager を使用してワークフロー関連のさまざまな処理を実行できます。このため、例えば、JSOM (JavaScript OM) を使って、カスタム UI (ページ) も簡単に構築できます。

今回は、カスタム リストにリボン ボタンを配置し、このボタンを押して承認行為をおこなう簡単な UI (画面) を構築してみましょう。

今回も、上記同様、カスタム リストを作成します。(作成手順については、省略します。)
さらに、上記同様、このリストに紐づいたリスト ワークフローを作成 (追加) します。(今回も、タスク リストは不要です。)

では、ワークフローを構築します。
まず、リボンから渡される結果を格納するための変数 res (String 型) を作成します。

つぎに、[WaitForCustomEvent] アクティビティを追加し、下記の通りプロパティを設定します。
このアクティビティでは、カスタムの画面から JSOM を通して送られてくるカスタムのイベントを待機し、イベントが送信されると、その結果を変数 res に設定します。

EventName : "MyTestEvent"

Result : res (上記の変数)

[SetWorkflowStatus] アクティビティを挿入し、下記のプロパティを設定します。
ここでは、上記で取得した res を Workflow Status として そのまま設定しています。

Status : res

つぎに、上記で追加したリスト (リスト アイテム) にリボンの Custom Action を追加します。追加方法は、「SharePoint Add-ins : UI Custom Action の開発」を参照してください。

今回、リスト アイテムが 1 つだけ選択されている場合のみボタンを有効にし、ボタンを押すと /Pages/MyWorkflowAction.aspx に遷移するようにします。このため、Elements.xml に、下記の通り記述します。

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <CustomAction Id="..."
    RegistrationType="List"
    RegistrationId="10002"
    Location="CommandUI.Ribbon"
    Sequence="10001"
    Title="...">
    <CommandUIExtension>
      <CommandUIDefinitions>
        <CommandUIDefinition Location="Ribbon.ListItem.Actions.Controls._children">
          <Button Id="Ribbon.ListItem.Actions.RibbonCustomAction1Button"
            Alt="RibbonCustomAction1 の要求"
            Sequence="100"
            Command="Invoke_RibbonCustomAction1"
            LabelText="RibbonCustomAction1 の要求"
            TemplateAlias="o1"
            Image32by32="_layouts/15/images/placeholder32x32.png"
            Image16by16="_layouts/15/images/placeholder16x16.png" />
        </CommandUIDefinition>
      </CommandUIDefinitions>
      <CommandUIHandlers>
        <CommandUIHandler Command="Invoke_RibbonCustomAction1"
          CommandAction="~site/Pages/MyWorkflowAction.aspx?source={Source}&amp;list={SelectedListId}&amp;item={SelectedItemId}"
          EnabledScript="javascript:function OneItemSelected()
            {
              var items = SP.ListOperation.Selection.getSelectedItems();
              var ci = CountDictionary(items);
              return (ci == 1);
            }
            OneItemSelected();"/>
      </CommandUIHandlers>
    </CommandUIExtension >
  </CustomAction>
</Elements>

さいごに、MyWorkflowAction.aspx のページを (Pages フォルダーに) 追加し、下記の通り JavaScript のコードを記述します。(非同期チェーンでネストしまくってますので、実際のプログラミングでは関数を分けるなどしてください。すみません、どんどんいい加減になってきてます。。。)

この処理では、ボタンを押すと、Workflow Service Manager の Workflow Instance Service を使って、このリスト アイテムに設定されている Workflow Instance の一覧を取得し、すべての Workflow Instance にカスタムのイベントを投げます。(そして、Workflow 側で、このイベントを受信して、処理が進むはずです。)

. . .
<asp:Content ContentPlaceHolderId="PlaceHolderAdditionalPageHead" runat="server">
  <script type="text/javascript" src=" /_layouts/15/sp.core.js"></script>
  <script type="text/javascript" src=" /_layouts/15/sp.runtime.js"></script>
  <script type="text/javascript" src=" /_layouts/15/sp.init.js"></script>
  <script type="text/javascript" src=" /_layouts/15/sp.js"></script>
  <script type="text/javascript" src=" /_layouts/15/sp.workflowservices.js"></script>
  <script type="text/javascript" src="../Scripts/jquery-1.7.1.min.js"></script>
  <script>
    $(document).ready(function () {
      $('#btnApprove').click(
        { 'payload': 'approved' },
        pubEvent);
      $('#btnReject').click(
        { 'payload': 'rejected' },
        pubEvent);
    });

    function pubEvent(e) {
      var params = getQueryParams();
      var ctx = SP.ClientContext.get_current();
      var man =
        SP.WorkflowServices.WorkflowServicesManager.newObject(
          ctx, ctx.get_web());
      ctx.load(man);
      ctx.executeQueryAsync(function () { // load WorkflowServicesManager
        var isv = man.getWorkflowInstanceService();
        ctx.load(isv);
        ctx.executeQueryAsync(function () { // load WorkflowInstanceService
          var ins = isv.enumerateInstancesForListItem(
            params['list'],
            params['item']);
          ctx.load(ins);
          ctx.executeQueryAsync(function () { // load Workflow instances
            var enu = ins.getEnumerator();
            while (enu.moveNext()) {
              isv.publishCustomEvent(
                enu.get_current(),
                'MyTestEvent',
                e.data.payload);
            }
            ctx.executeQueryAsync(function () { // publishCustomEvent
              location.href = params['source'];
            }, failf); // publishCustomEvent
          }, failf); // load Workflow instances
        }, failf); // load WorkflowInstanceService
      }, failf); // load WorkflowServicesManager
    }

    function failf(sender, arg) {
      alert('failed: ' + arg.get_message());
    }

    function getQueryParams() {
      var params = [];
      var arrays = location.search.substr(1).split('&');
      for (var i = 0; i < arrays.length; i++) {
        var elems = arrays[i].split('=');
        var key = decodeURIComponent(elems[0]);
        var val = decodeURIComponent(elems[1]);
        params.push(key);
        params[key] = val;
      }
      return params;
    }
  </script>
</asp:Content>

<asp:Content ContentPlaceHolderId="PlaceHolderMain" runat="server">
  Please select !
  <br />
  <input id="btnApprove"
    type="button"
    value="Approve" />
  <input id="btnReject"
    type="button"
    value="Reject" />
</asp:Content>

以上で完了です。

デバッグ実行をおこない、App web の上記リストを表示します。
リスト アイテムを新規作成します。(Workflow が開始され、上記の WaitForCustomEvent で待機状態になります。)
リスト アイテムを選択すると、下図の通り、リボンのボタンが押せるようになるので、このボタンをクリックします。

ボタンを押すと、下図の画面が表示されます。

上図の画面で [Reject] ボタンを押すと、下図の通り、リスト アイテムの Workflow Status が「rejected」に設定されます。

ここでは、インスタンスにイベントを渡す処理を作成しましたが、Object Model (CSOM, JSOM) を使って、リボンのコマンドや ECB メニューで Workflow の開始も制御できるでしょう。(こうした On-Demand な実行や制御は、Remote Event Receiver では不可能です。) また、SharePoint Designer がおこなっているような発行などの機能も実装できるらしいので、以前、Web Service (SOAP) を使って苦労して実装した「SharePoint の Custom Workflow Editor」なども、今回から、この Object Model を使ってもっと簡単に実装できるかもしれませんね。(すみません、試してませんので、どこまでできるか分かりませんが。)
また、ここではページ上から JSOM を使いましたが、REST API を直接呼び出すことも可能なため、リモートで実行されているサービスなどから Workflow を制御することもできるでしょう。(「SharePoint Add-ins 概要」で解説したように、新しい REST API を使うと、Object Model で提供されている多くの処理を実行できます。)

 

以上、ざっとですが、SharePoint Add-ins の Workflow 開発における特徴的な側面をみてきました。

なお、Workflow Manager は、単体でインストールして使うこともできます。
この Workflow Manager については、以前、SharePoint User Group で登壇されていた "てすとぶろぐ" さんが詳しく書いてくれていますので、勝手にリンクしてしまいますが、是非、下記も確認してみてください。

もっといろいろ書こうかと思いましたが、そろそろ疲れてきたので、このシリーズもいったんお開きにしようかと思います。(いろいろと試してみてください)

 

※ 変更履歴 :

2015/05/05  App for SharePoint (SharePoint 用アプリ) を SharePoint Add-ins (SharePoint アドイン) に名称変更

 

Comments (0)

Skip to main content