アドイン Web で JavaScript からのホスト Web データを操作する

これは、SharePoint ホスト型の SharePoint アドインの開発の基本に関する記事のシリーズの 11 番目です。最初に、「SharePoint ホスト型の SharePoint アドインの作成を始める | 次の手順」にある「SharePoint アドイン」とこのシリーズの前の記事をよく読んで理解しておいてください。

注:

SharePoint ホスト型アドインに関するこのシリーズを続けて学習してきた方は、このトピックでも引き続き使用できる Visual Studio ソリューションをお持ちです。 また、SharePoint_SP-hosted_Add-Ins_Tutorials でリポジトリをダウンロードし、BeforeHostWebData.sln ファイルを開くこともできます。

既定の SharePoint の設計では、アドインに含まれる JavaScript がファーム上の他の SharePoint Web サイトのデータへアクセスすることを防ぎます。 これにより、悪意のあるアドインに含まれるスクリプトが機密性の高いデータへアクセスできないようにしています。 ところが、多くの場合、アドインはホスト Web にアクセスしたり、ホスト Web と同じサイト コレクション内の他の Web サイトにアクセスしたりする必要があります。

アドインでのこのシナリオの実現のための 2 つの部分があります。

  • アドインのアドイン マニフェスト ファイルでホスト Web へのアクセス許可を要求します。 アドインをインストールするユーザーにはこのアクセス許可を付与することを求めるメッセージが表示され、ユーザーがアクセス許可を付与しない場合はアドインをインストールできません。
  • SP.ClientContext オブジェクトを使用してホスト Web への JSOM コールを実行する代わりに、SP.AppContextSite オブジェクトを使用します。 このオブジェクトにより、アドインはアドイン Web 以外の Web サイトのコンテキスト オブジェクトを取得できますが、対象となるのは同じサイト コレクション内の Web サイトのみです。 (SharePoint Online サブスクリプション [またはオンプレミス SharePoint Web Application] 内のすべての Web サイトにアクセスする方法もありますが、そちらは高度な論題になります。)

この記事では JSOM を使用して、まだ開始されていないオリエンテーションを見つけて、それをホスト Web の予定表でスケジュールします。

ホスト Web 予定表の準備

ホスト Web (開発者向けテスト Web サイト) を開き、「新入社員オリエンテーションのスケジュール」という名前の予定表があること、およびその予定表に「Orient Cassie Hicks」という単一のイベントがあることを確認します。 ない場合には、次の手順を実行します。

  1. サイトのホーム ページで、[サイト コンテンツ]>[アドインの追加]>[予定表] の順に選択します。

  2. [予定表の追加] ダイアログの [名前] に、「新入社員オリエンテーションのスケジュール」と入力し、[作成] を選択します。

  3. 予定表が開いたら、いずれかの日付にカーソルを合わせて [追加] リンクが表示されるまで待機し、その後 [追加] をクリックします。

  4. [社員オリエンテーション スケジュール - 新しいアイテム] ダイアログで、[タイトル] に「Cassi Hicks に対するオリエンテーション」と入力します。 他のフィールドを既定のままにし、[保存] を選択します。

    予定表は、次のようになります。

    図 1. カスタムの予定表

    6 月 1 日付の「Orient Cassie Hicks」というアイテムがある「新入社員オリエンテーションのスケジュール」という名前の予定表

JavaScript とそれを呼び出すボタンの作成

  1. ソリューション エクスプローラー[スクリプト] ノードで Add-in.js ファイルを開きます。

  2. completedItems の宣言の下に、次の宣言を追加します。

    var notStartedItems;
    var calendarList;
    var scheduledItems;
    
    • notStartedItems は、[オリエンテーションのステージ][未開始][シアトルの新入社員] リスト上の項目を参照します。
    • calendarList は、ホスト Web 上に作成した予定表を参照します。
    • scheduledItems は、予定表のアイテムのコレクションを参照します。
  3. SharePoint アドインを実行すると、SharePoint は、開始ページを呼び出し、いくつかのクエリ パラメーターを開始ページの URL に追加します。 その 1 つが SPHostUrl で、これはもちろんホスト Web の URL です。 アドインはホスト Web データへの呼び出しを行うためにこの情報が必要なので、Add-in.js ファイルの先頭で、scheduledItems の変数宣言のすぐ下に、次の行を追加します。

    var hostWebURL = decodeURIComponent(getQueryStringParameter("SPHostUrl"));
    

    このコードについては、次の点に注意してください。

    • getQueryStringParameter は、次の手順で作成するユーティリティ関数です。
    • decodeUriComponent は標準の JavaScript 関数で、SharePoint がクエリ パラメーターに対して実行する URI エンコード処理を元に戻します。たとえば、エンコードされたスラッシュ "%2F" は "/" に戻されます。
  4. このファイルの下部に次のコードを追加します。 この関数は、クエリ パラメーターの読み取りに使用できます。

    // Utility functions
    
    function getQueryStringParameter(paramToRetrieve) {
        var params = document.URL.split("?")[1].split("&");
        var strParams = "";
        for (var i = 0; i < params.length; i = i + 1) {
            var singleParam = params[i].split("=");
            if (singleParam[0] == paramToRetrieve) {
                return singleParam[1];
          }
        }
    }
    
  5. Add-in.js ファイルの失敗コールバック セクションより上のいずれかの場所に次の関数を追加します。

    function ensureOrientationScheduling() {
    
      var camlQuery = new SP.CamlQuery();
      camlQuery.set_viewXml(
          '<View><Query><Where><Eq>' +
              '<FieldRef Name=\'OrientationStage\'/><Value Type=\'Choice\'>Not started</Value>' +
          '</Eq></Where></Query></View>');
      notStartedItems = employeeList.getItems(camlQuery);
    
      clientContext.load(notStartedItems);
      clientContext.executeQueryAsync(getScheduledOrientations, onGetNotStartedItemsFail);
      return false;
    }
    

    このコードについては、次の点に注意してください。

    • これは完了項目を取得するリスト クエリ メソッドとほぼ同じですが、完了した項目の代わりに未開始の項目を取得します。 スクリプトはオリエンテーションが未開始ステージを過ぎていれば既にスケジュールされているという簡易な前提に基づくため、関心があるのは未開始の項目のみです。
    • 後のステップで、executeQueryAsync 呼び出しに 2 つのコールバック メソッドを作成します。
  6. Add-in.js ファイルで、直前の関数のすぐ下に、次の関数を追加します。 hostWebContext オブジェクトを使用してクエリされるリストを特定することに注意してください。

    function getScheduledOrientations() {
    
      var hostWebContext = new SP.AppContextSite(clientContext, hostWebURL);
      calendarList = hostWebContext.get_web().get_lists().getByTitle('Employee Orientation Schedule');
    
      var camlQuery = new SP.CamlQuery();
      scheduledItems = calendarList.getItems(camlQuery);
    
      clientContext.load(scheduledItems);
      clientContext.executeQueryAsync(scheduleAsNeeded, onGetScheduledItemsFail);
    }
    

    注:

    なお、CAML クエリにクエリ マークアップは追加されません。 クエリ オブジェクト内に実際のクエリを持たない効果は、確実にすべてのリスト アイテムが取得されることです。 リストが非常に大きい場合、サーバーへの要求が許容範囲を超える長時間の実行になる可能性があります。 その場合は、目標を実現するその他のなんらかの方法を見つけます。 しかし、このサンプルの状況ではリストが非常に小さく (予定表リストは小さい場合がほとんどです)、クライアント上で繰り返し実行できるようにリスト全体を取得することは、実際はサーバーへの呼び出し、つまり executeQueryAsync の呼び出しの数を最小限に抑えるのに役立ちます。

  7. ファイルに次の関数を追加します。

    function scheduleAsNeeded() {
    
      var unscheduledItems = false;
      var dayOfMonth = '10';
    
      var listItemEnumerator = notStartedItems.getEnumerator();
    
      while (listItemEnumerator.moveNext()) {
          var alreadyScheduled = false;
          var notStartedItem = listItemEnumerator.get_current();
    
          var calendarEventEnumerator = scheduledItems.getEnumerator();
          while (calendarEventEnumerator.moveNext()) {
              var scheduledEvent = calendarEventEnumerator.get_current();
    
                // The SP.ListItem.get_item('field_name ') method gets the value of the specified field.
              if (scheduledEvent.get_item('Title').indexOf(notStartedItem.get_item('Title')) > -1) {
                  alreadyScheduled = true;
                  break;
              }
          }
          if (alreadyScheduled === false) {
    
                // SP.ListItemCreationInformation holds the information the SharePoint server needs to
                // create a list item
              var calendarItem = new SP.ListItemCreationInformation();
    
                // The some_list .additem method tells the server which list to add
                // the item to.
              var itemToCreate = calendarList.addItem(calendarItem);
    
                // The some_item .set_item method sets the value of the specified field.
              itemToCreate.set_item('Title', 'Orient ' + notStartedItem.get_item('Title'));
    
                // The EventDate and EndDate are the start and stop times of an event.
              itemToCreate.set_item('EventDate', '2015-06-' + dayOfMonth + 'T21:00:00Z');
              itemToCreate.set_item('EndDate', '2015-06-' + dayOfMonth + 'T23:00:00Z');
              dayOfMonth++;
    
                // The update method tells the server to commit the changes to the SharePoint database.
              itemToCreate.update();
              unscheduledItems = true;
          }
      }
      if (unscheduledItems) {
          calendarList.update();
          clientContext.executeQueryAsync(onScheduleItemsSuccess, onScheduleItemsFail);
      }
    }
    

    このコードについては、次の点に注意してください。

    • このメソッドは、[シアトルの新入社員] リストの [未開始] アイテムのタイトル (その社員の名前) が、[新入社員オリエンテーションのスケジュール] 予定表のイベント タイトルに含まれているかどうかをチェックします。 簡略化のため、予定表内のすべてのエントリはイベント タイトルに社員の氏名を指定して作成されるものと想定しています。

    • 予定表に既に記載されているどのイベントも [未開始] アイテムに合致しない場合、スクリプトは [未開始] アイテムの予定表アイテムを作成します。

    • JSOM は、SharePoint サーバーに送信するペイロードのサイズを最小化するために、SPListItem オブジェクトではなく軽量の ListItemCreationInformation オブジェクトを使用します。

    • 新しい予定表イベントの 2 つの DateTime フィールドは、この記事が作成された月、つまり 2015-06 に日付設定されています。 アイテムを見つけるためにカレンダーを過去にスクロールしなくてもすむように、これらの日付を現在の月と年の日付に変更します。

    • スケジュールされていない [未開始] アイテムが検出されると、最初のアイテムが月の 10 日にスケジュールされます。 スケジュールされていないアイテムがさらにある場合はそれぞれ 10 日以降にスケジュールされます。 簡略化のため、その数はそれほど多くなく、"32" などのあり得ない日付になることはないと想定しています。

    • このコードの大部分は標準の JavaScript です。 SharePoint JSON を使用する行には、コメントが付いています。

  8. 次の成功ハンドラーを追加します。これは、以前にスケジュールされていなかったアイテムが予定表に追加されると呼び出されます。

    function onScheduleItemsSuccess() {
      alert('There was one or more unscheduled orientations and they have been added to the '
                + 'Employee Orientation Schedule calendar.');
    }
    
  9. ファイルの失敗コールバック セクションに、次のエラー関数を追加します。

      function onGetNotStartedItemsFail(sender, args) {
        alert('Unable to get the not-started items. Error:'
            + args.get_message() + '\n' + args.get_stackTrace());
    }
    
    function onGetScheduledItemsFail(sender, args) {
        alert('Unable to get scheduled items from host web. Error:'
            + args.get_message() + '\n' + args.get_stackTrace());
    }
    
    function onScheduleItemsFail(sender, args) {
        alert('Unable to schedule items on host web calendar. Error:'
            + args.get_message() + '\n' + args.get_stackTrace());
    }
    
  10. default.aspx ファイルを開き、ID PlaceHolderMain を持つ asp:Content 要素を検索します。

  11. purgeCompletedItems ボタンの直下に、次のマークアップを追加します。

    <p><asp:Button runat="server" OnClientClick="return ensureOrientationScheduling()"
                   ID="ensureorientationschedulingbutton"
                   Text="Ensure all items are on the Calendar" /></p>
    
  12. プロジェクトを Visual Studio で再構築します。

  13. アドインのテスト中に、リスト アイテムの [オリエンテーションのステージ][未開始] に手動で設定する必要を最小限に抑えるため、リスト インスタンス NewEmployeesInSeattle の elements.xml ファイル (リスト テンプレート NewEmployeeOrientation の elements.xml ではない) を開き、最低 3 つの Row 要素 (その中に Cassie Hicks の行を含める) の [オリエンテーションのステージ] 値が [未開始] 値になるようにします。 これは既定値なので、3 つ (あるいはそれ以上) の行に OrientationStageField 要素を含めないことが最も簡単な方法です。

    Rows 要素は、次の例のようになります。

    <Rows>
      <Row>
        <Field Name="Title">Tom Higginbotham</Field>
        <Field Name="Division">Manufacturing</Field>
        <Field Name="OrientationStage">Completed</Field>
      </Row>
      <Row>
        <Field Name="Title">Satomi Hayakawa</Field>
      </Row>
      <Row>
        <Field Name="Title">Cassi Hicks</Field>
      </Row>
      <Row>
        <Field Name="Title">Lertchai Treetawatchaiwong</Field>
      </Row>
    </Rows>
    

アドインが必要とするホスト Web へのアクセス許可を指定します。

アドインにはそれ自体のアドイン Web に対するフル コントロールのアクセス許可が自動的に設定されるため、ここまでの部分では必要なアクセス許可を指定する必要がありませんでした。 しかし、ホスト Web のデータと対話するには特にアクセス許可を要求しなければなりません。 新入社員オリエンテーション アドインには、ホスト Web の予定表にアイテムを追加するためのアクセス許可が必要です。

  1. ソリューション エクスプローラーで appmanifest.xml ファイルを開きます。

  2. マニフェスト デザイナーで [アクセス許可] タブを開きます。

  3. [範囲] 列の一番上の行にあるドロップダウンで、[リスト] を選択します。

  4. [アクセス許可] 列で [管理] を選択します。

  5. [プロパティ] 列が空白のままになっている場合、アドインはホスト Web 上のすべてのリストに対する書き込みアクセス許可を要求します。 アドインには必要なアクセス許可のみに制限することをお勧めします。 アドイン マニフェストで特定のリスト インスタンスにアクセス許可を制限する方法はありませんが、特定の基本リスト テンプレートをもとに作成されるインスタンス リストにアドインを制限することは可能です。 予定表の基本リスト テンプレートは、数値 ID が 106 のイベントです。

    同じ行の [プロパティ] セルを選択して、セル内に [編集] ボタンを表示します。 アクセス許可リストは、次のようになります。

    図 2. [編集] ボタンが表示されている [アクセス許可] リスト

    Visual Studio アドイン マニフェスト デザイナーの [アクセス許可] タブに記されているアクセス許可の一覧。[プロパティ] 列のセルに [編集] ボタンが示されています。

  6. [編集] を選択して、[プロパティ] ダイアログを開きます。

  7. [名前]BaseTemplateId に設定し、[値]106 に設定します。 ダイアログは、次のようになります。

    図 3. アクセス許可リストの [プロパティ] ダイアログ

    Visual Studio におけるアクセス許可一覧のための [プロパティ] ダイアログ。プロパティ名は「Base List ID」に、値は「106」に設定されています。

  8. [OK] を選択します。 [アクセス許可] タブは、次のようになります。

    図 4. Visual Studio のアドイン マニフェスト デザイナーの [アクセス許可] タブ

    Visual Studio のアドイン マニフェスト デザイナーの [アクセス許可] タブ。[管理] アクセス許可があり、ベース タイプが 106 のリストを表示するアドインが示されています。

アドインを実行してテストする

  1. ホスト Web 予定表がこの記事で前述したように準備してあることを確認してください。 イベントが 1 つあり、「Cassi Hicks に対するオリエンテーション」という名前である必要があります。

  2. デバッグするときに Visual Studio が使用するブラウザーのポップアップ ウィンドウを有効にします。

  3. F5 キーを使用して、アドインを展開して実行します。 Visual Studio が、テスト用 SharePoint サイトにアドインを一時的にインストールして、すぐにアドインを実行します。

  4. アドインに必要なアクセス許可を付与できる、アクセス許可同意フォームが開きます。 ページにはホスト Web 上のすべての予定表の中から選択できるドロップダウン リストがあります。 [社員オリエンテーション スケジュール] を選択し、[信頼する] を選択します。

    図 5. SharePoint アドインの同意確認プロンプト

    アドインが必要とするアクセス許可の概要が示された SharePoint アドイン同意確認プロンプトと、[信頼する] または [キャンセル] のボタン。

  5. アドインのが開始ページが完全に読み込まれたら、[アイテムがスケジュール設定されていることを確認する] ボタンを選択します。

    図 6. 新しいボタンが表示されている [新入社員オリエンテーション] ホームページ

    [アイテムがスケジュール設定されていることを確認する] というラベルの新しいボタンが追加された [新入社員オリエンテーション] ホームページ。

  6. いずれかの失敗コールバック関数が実行されると、コールバック関数によって作成されたエラー メッセージ通知が表示されます。 それ以外の場合には最終的な成功コールバックによって作成された成功メッセージ「スケジュールされていない 1 つ以上のオリエンテーションがあり、それらは [新入社員オリエンテーションのスケジュール] 予定表に追加されました」が表示されます。

  7. ホスト Web 上の [社員オリエンテーション スケジュール] 予定表に移動します。 たとえば、開発者向けサイトのホーム ページへの階層リンクを選択し、[サイト コンテンツ] を選択します。 [社員オリエンテーション スケジュール] タイル ([社員オリエンテーション] タイルではありません) を選択します。

    予定表は次のようになります。 このスクリプトでは、Cassi Hicks のイベントが既に存在することが検出されたため、2 つ目のイベントは作成されませんでした。 その他の 2 人の従業員のオリエンテーションが [未開始 ] 状態のイベントを作成しました。 また、オリエンテーションが未 開始 状態を過ぎた従業員のイベントも作成されませんでした。

    図 7. 2 つの新しいイベントが追加された後の予定表

    対象月の 10 日と 11 日に 2 人の社員のオリエンテーションを行うための新しいイベントが追加された [新入社員オリエンテーション] 予定表

  8. [アイテムがスケジュール設定されていることを確認する] をもう一度選択する前に、予定表から 2 つの新しいイベントを確実に削除してください。

  9. デバッグ セッションを終了するには、ブラウザー ウィンドウを閉じるか、Visual Studio でデバッグを停止します。 F5 キーを選択するたびに、Visual Studio は以前のバージョンのアドインを取り消し、最新のバージョンをインストールします。

  10. このアドインおよび他の記事の Visual Studio ソリューションを操作し、それが終了したら前回のアドインを取り消すとよいでしょう。 ソリューション エクスプローラーでプロジェクトを右クリックして、[取り消し] を選択します。

次の手順

SharePoint ホスト型 SharePoint アドインの次の詳細作業に移動します。