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


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="&apos;RibbonCustomAction1&apos; 動作の呼び出し">
    <CommandUIExtension>
      <CommandUIDefinitions>
        <CommandUIDefinition Location="Ribbon.Documents.Manage.Controls._children">
          <Button Id="Ribbon.Documents.Manage.RibbonCustomAction1Button"
            Alt="Apps Test Button"
            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="&apos;RibbonCustomAction1&apos; 動作の呼び出し"
    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/">
  <CustomAction
    Id="0f00ab52-aafb-4d0f-b312-04dbf0829c13.RibbonCustomAction1"
    RegistrationType="List"
    RegistrationId="10000"
    Location="CommandUI.Ribbon"
    Sequence="10001"
    Title="&apos;RibbonCustomAction1&apos; 動作の呼び出し">
    <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">
          <Group
            Id="Ribbon.ListItem.MyCustomGroup"
            Sequence="1"
            Description="This is group test."
            Title="My Test Group"
            Template="Ribbon.Templates.TestGroupTemplate">
            <Controls Id="Ribbon.ListItem.MyCustomGroup.Controls">
              <Button
                Id="Ribbon.ListItem.MyCustomGroup.New"
                Alt="This is button test."
                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="OneLarge" LayoutTitle="OneLarge">
              <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 アドイン) に名称変更

 

Comments (0)

Skip to main content