Uncategorized

SharePoint Add-ins : UI Custom Action の開発 (Ribbon のカスタマイズ)

最新の SharePoint 開発については SharePoint Framework を参照してください。(この開発手法は、古い情報です。)

SharePoint Add-ins 開発

こんにちは。

今回は、カスタムのメニュー項目(ECB) やリボンをプログラミングするための UI Custom Action 開発について解説します。

 

SharePoint Add-ins における UI Custom Action

SharePoint 2010 開発: カスタムの SharePoint リボンの作成」で紹介したように、SharePoint では、従来より、UI Custom Action と呼ばれるフィーチャーを実装して、リボン (Ribbon) カスタマイズがおこなえます。また、リボンだけでなく、カスタムの ECB (Edit Control Block) メニュー (下図) も、この方法で実装できます。

新しくなった SharePoint Add-ins (SharePoint アドイン, 旧 App for SharePoint) の UI Custom Action は、従来の Custom Action 開発とベースの概念は似ていますが (例えば、要素マニフェストに定義を記述する点など)、異なる点もいくつかあるので注意が必要です。
そこで、今回は、実際の構築手順を解説しながら、そうしたポイントを見て行きます。

 

UI Custom Action の構築

では、さっそく、カスタムのリボン (Ribbon) ボタンを構築してみましょう。

後述しますが、SharePoint Add-ins におけるリボン選択時の動作 (Custom Action) は、基本的に、Page の表示 (遷移) で実装します。この Page は、SharePoint-hosted (App web 上の Page)、Provider-hosted のどのホスト方法でも実装可能です。今回のサンプルでは、Provider-hosted を使用します。

まず、Visual Studio を起動し、[Apps for SharePoint 2013] (SharePoint Add-ins) のプロジェクトを新規作成します。今回は、上述の通り、Provider-hosted (プロバイダー向けホスト型) のアプリケーションを構築します。

つぎに、Visual Studio のソリューション エクスプローラーで、作成された Add-ins のプロジェクトをマウスで右クリックして、[追加] – [新しい項目] を選択します。表示される画面で、[リボンのカスタム アクション] (下図) を選択します。

後述しますが、Custom Action は、Host web と App web のどちらにもホストできます。

補足 : ただし、App web にホストした場合、後述する StandardTokens や認証 token は渡されません。(/_layouts/15/appredirect.aspx から redirect されないためです。)
App web の Custom Action では、同一サイト内の Page に飛ばしてください。

今回は、ユーザーが使用している Host web の「ドキュメント ライブラリー」(Document Library) にカスタムの Ribbon ボタンを配置しましょう。このため、次に表示される下記の画面 (ウィザード) で、[ホスト Web] (Host web) を選択し、動作の対象として [ドキュメント ライブラリー] (Document Library) を選択して、次に進みます。(App web に配置する場合など、個別のリスト インスタンスに設定することもできます。)

SharePoint Add-ins におけるリボン選択時 (クリック時) の動作 (Action) は、基本的に、Page の表示 (遷移) で実装します。次に表示されるウィザードで、下図の通り ~remoteAppUrl/Pages/Default.aspx を指定し、Provider-hosted の Page (Default.aspx) を表示するようにします。

従来の SharePoint Farm SolutionSharePoint Sandboxed Solution のように、JavaScript (JSOM) を使った Custom Action の動作 (Action) の実装はできないようです。(SharePoint Add-ins では、エラーになります。SharePoint 2010 時代の JavaScript を使った Custom Action の実装については、「SharePoint 2010 開発: カスタムの SharePoint リボンの作成」を参照してください。)
ただし、Page の表示を、ブラウザーの Page 遷移 (全画面の表示) でなく、SharePoint の Dialog Framework で表示することも可能です。このため、作り方次第では、JavaScript (JSOM) で実装した場合と類似の Experience (動作) を提供できます。この手法については、このあとで補足し、まずは、上記のページ (Pages/Default.aspx) を全画面で表示するようにしましょう。

なお、上図の [コントロールの場所] は、リボンのどの場所にボタンを配置するか (どのタブ、どのグループ) を指定します。今回は、下図の [管理] (Manage) グループの中に配置します。

ウィザードを完了すると、下記の Elements.xml (要素マニフェスト) が作成されます。

<?xml version="1.0" encoding="utf-8"?><Elements xmlns="http://schemas.microsoft.com/sharepoint/">  <CustomAction Id="7b6d4ac0-6f2c-463d-8d9e-c282fb494645.RibbonCustomAction1"RegistrationType="List"RegistrationId="101"Location="CommandUI.Ribbon"Sequence="10001"title=''><CommandUIExtension>  <CommandUIDefinitions><CommandUIDefinition Location="Ribbon.Documents.Manage.Controls._children">  <Button Id="Ribbon.Documents.Manage.RibbonCustomAction1Button"Sequence="100"Command="Invoke_RibbonCustomAction1ButtonRequest"LabelText="Apps Test Button"TemplateAlias="o1"Image32by32="_layouts/15/images/placeholder32x32.png"Image16by16="_layouts/15/images/placeholder16x16.png" /></CommandUIDefinition>  </CommandUIDefinitions>  <CommandUIHandlers><CommandUIHandler Command="Invoke_RibbonCustomAction1ButtonRequest"  CommandAction="~remoteAppUrl/Pages/Default.aspx?{StandardTokens}"/>  </CommandUIHandlers></CommandUIExtension >  </CustomAction></Elements>

上記で、RegistrationId (上記の太字を参照) の 101 は、対象がドキュメント ライブラリー (Document Library) であることを意味しています。100 が Generic List、101 が Document library、104 が Announcements List など、Id が決まっています。

また、前述で設定したボタンの配置場所 (今回は、[ファイル] タブの [管理] グループ) は、上記の Location (太字を参照) に反映されています。

また、ボタンに使用するアイコンとして、今回は、既存のアイコン (placeholder16x16.png、placeholder32x32.png) を使用していますが、もちろん、独自のアイコンを Web 上にアップして参照できます。(リボン コントロールのサイズの動的変更にあわせて、2 種類のアイコンを用意しておきます。)

また、ボタンを押した際に表示されるページが上記の CommandAction に設定されています (太字を参照)。このページ (Default.aspx) には、「.NET CSOM を使ったプログラミングと認証 (Authentication)」で紹介したように、StandardToken (必要な情報を設定したクエリー文字列) や認証用の Token (HTTP の POST データ) が渡されるので、Page (Default.aspx) から SharePoint に接続して、SharePoint に対する検索や更新などの処理が可能です。
今回は、リスト アイテムの Id を取得して、そのファイル名を表示する簡単なページを構築するため、下記の通り CommandAction を変更しておきます。(リストとリスト アイテムの情報をページに渡します。)

. . .<CommandUIHandler Command="Invoke_RibbonCustomAction1ButtonRequest"  CommandAction="~remoteAppUrl/Pages/Default.aspx?{StandardTokens}&amp;list={SelectedListId}&amp;item={SelectedItemId}"/>. . .

なお、ここで使用されている {StandardTokens}、{SelectedListId}、{SelectedItemId} はトークン (token) と呼ばれるもので、実行時に適切な値に変換されます。この他に、リストの Url を示す {ListUrlDir} や、ユーザーが見ている元のページの URL を示す {Source} などのトークンが使用できます。特に、画面を遷移して、また元のページに戻ってくるような処理を実装する場合は、{Source} は必要不可欠です。

なお、作成された Elements.xml の [配置タイプ] (Deployment Type) プロパティを Visual Studio で確認すると、「AppPackage」となっています。(下図を参照)

この場合、このカスタム アクションは .wsp (App web に配置されるパッケージ) に含まれず、Manifest Feature として展開されることを意味しています。(もちろん、作成される .app パッケージに、この Elements.xml は含まれます。ただし、.wsp パッケージには含まれません。)
一方、App web に配置する場合には「ElementManifest」となり、カスタム アクション (Elements.xml) は .wsp ファイル (App web に配置されるパッケージ) の中に含まれます。

つぎに、遷移先のページ (Default.aspx) をプログラミングしましょう。
今回は、上述の通り、選択されたリスト アイテムの名前 (ファイル名) を表示するプログラムなので、下記の通り実装します。

. . .protected void Page_Load(object sender, EventArgs e){  var token = TokenHelper.GetContextTokenFromRequest(Page.Request);  var hostWeb = Page.Request["SPHostUrl"];  var listId = Page.Request["list"];  var itemId = Page.Request["item"];  using (var ctx = TokenHelper.GetClientContextWithContextToken(hostWeb,token,Request.Url.Authority))  {var list = ctx.Web.Lists.GetById(new Guid(listId));var item = list.GetItemById(int.Parse(itemId));ctx.Load(item);ctx.ExecuteQuery();Response.Write(item["FileLeafRef"]);  }}. . .

補足 : なお、AppManifest.xml の StartPage として、この Default.aspx が使われていると思いますので、本来は、ListId や ItemId が null でもちゃんと動くようにプログラミングしておきましょう。(今回は、面倒なのでサボりました。。。)

さいごに、忘れずに、Permission の設定をおこなってください。(今回は、この解説は省略します。)

 

動作の確認

F5 でデバッグ実行をおこなうなどして配置後、ドキュメント ライブラリー (どのドキュメント ライブラリーでも構いません) を開いて、リボンの [ファイル] タブを選択してみてください。
下図の通り、[管理] (Manage) グループにカスタムのボタンが表示されているのがわかります。

ドキュメント (アイテム) を上図の通り選択 (チェック) してこのボタンを押すと、Remote App (Remote Web) の Default.aspx のページが表示され、下図の通り選択したアイテムのファイル名が表示されます。(SharePoint と連携して動作しています。)

補足 : EnableScript 属性を使って、アイテムが選択されたときだけ、このボタンを有効にできます。ここでは解説を省略しますが、詳細は「SharePoint 2010 開発: カスタムの SharePoint リボンの作成」を参照してください。

 

Dialog の Custom Action

上記のサンプルでは Page 全体を遷移 (表示) しましたが、上述の通り、SharePoint の Dialog Framework で表示することも可能です。(この方が、組み込まれた機能らしい動きになりますね。)

Dialog Framework を使ってページを表示するには、Elements.xml に下記の通り追記するだけです。(なお、なぜかマークアップのエラーが出ますが、まあ、細かいことは気にせず先に進みましょう。)

<?xml version="1.0" encoding="utf-8"?><Elements xmlns="http://schemas.microsoft.com/sharepoint/">  <CustomAction Id="7b6d4ac0-6f2c-463d-8d9e-c282fb494645.RibbonCustomAction1"RegistrationType="List"RegistrationId="101"Location="CommandUI.Ribbon"Sequence="10001"title=''HostWebDialog="true"HostWebDialogWidth="250"HostWebDialogHeight="100">. . .

Dialog を閉じる際、従来は SP.UI.ModalDialog.close や SP.UI.ModalDialog.commonModalDialogClose などを使用しました。しかし、SharePoint Add-ins の Dialog Framework は ifarme で表示されます。このため、今回は、親ウィンドウに、CloseCustomActionDialogRefresh か CloseCustomActionDialogNoRefresh をフレーム間通信 (postMessage) を使って呼び出します。(前者は Close と同時に Page を Refresh し、後者は Page を Refresh しません。)

例えば、上記の Default.aspx に下記のようにボタンを配置しておくと良いでしょう。

. . .<body>  <form id="form1" runat="server">  <div><input type="button"  value="Close"  onclick="javascript: window.parent.postMessage('CloseCustomActionDialogRefresh', '*');" /></div>  </form></body>. . .

この Add-ins を実行すると、下図の通り、Dialog で表示されるようになります。
また、[Close] ボタンを押すと、この Dialog は閉じて、背景にある一覧が更新 (再取得) されます。

補足 : 現在 (2013 年 02 月)、この HostWebDialog を使用した動作 (Action) は、Debug 実行でうまく動かないようなので注意してください。Debugger と SharePoint が既定で持っている JavaScript ライブラリーの相性が良くないみたいです。

 

なお、「MSDN : Apps for SharePoint compared with SharePoint solutions」に依ると、SharePoint Add-ins では、custom action groups と custom action hiding は不可能と書かれているので注意してください。

2013/02/22 訂正 : App web のカスタム リストで動作確認したところ、Hide も Group も可能でした。(下記)

<?xml version="1.0" encoding="utf-8"?><Elements xmlns="http://schemas.microsoft.com/sharepoint/">  <CustomActionId="0f00ab52-aafb-4d0f-b312-04dbf0829c13.RibbonCustomAction1"RegistrationType="List"RegistrationId="10000"Location="CommandUI.Ribbon"Sequence="10001"title=''><CommandUIExtension>  <CommandUIDefinitions><!-- hide all --><CommandUIDefinition Location="Ribbon.ListItem.New" /><CommandUIDefinition Location="Ribbon.ListItem.Manage" /><CommandUIDefinition Location="Ribbon.ListItem.Actions" /><CommandUIDefinition Location="Ribbon.ListItem.Share" /><CommandUIDefinition Location="Ribbon.ListItem.Workflow" /><!-- new group and button --><CommandUIDefinition Location="Ribbon.ListItem.Groups._children">  <GroupId="Ribbon.ListItem.MyCustomGroup"Sequence="1"Description="This is group test."title=''Template="Ribbon.Templates.TestGroupTemplate"><Controls Id="Ribbon.ListItem.MyCustomGroup.Controls">  <ButtonId="Ribbon.ListItem.MyCustomGroup.New"Sequence="20"Image32by32="_layouts/15/images/placeholder32x32.png"Image16by16="_layouts/15/images/placeholder16x16.png"Command="Invoke_RibbonCustomAction1ButtonRequest"LabelText="Test Button"TemplateAlias="Area1"/></Controls>  </Group></CommandUIDefinition><CommandUIDefinition Location="Ribbon.Templates._children" >  <GroupTemplate Id="Ribbon.Templates.TestGroupTemplate"><Layout title='' Layouttitle=''>  <Section Alignment="Top" Type="OneRow"><Row>  <ControlRef DisplayMode="Large" TemplateAlias="Area1" /></Row>  </Section></Layout>  </GroupTemplate></CommandUIDefinition><CommandUIDefinition Location="Ribbon.ListItem.Scaling._children">  <MaxSize Id="Ribbon.ListItem.MyCustomGroup.Scaling.MaxSize"Sequence="35"GroupId="Ribbon.ListItem.MyCustomGroup"Size="OneLarge"/></CommandUIDefinition>  </CommandUIDefinitions>  <CommandUIHandlers><CommandUIHandler Command="Invoke_RibbonCustomAction1ButtonRequest"  CommandAction="~site/Pages/Default.aspx"/>  </CommandUIHandlers></CommandUIExtension >  </CustomAction></Elements>

 

※ 変更履歴 :

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

 

Categories: Uncategorized

Tagged as: ,

10 replies»

Leave a Reply