チュートリアル: メッセージ作成 Outlook アドインのビルド

このチュートリアルでは、コンテンツをメッセージの本文に挿入するためにメッセージ作成モードで使用可能な Outlook アドインをビルドする方法について説明します。

このチュートリアルの内容:

  • Outlook アドイン プロジェクトを作成する
  • メッセージ作成ウィンドウに表示されるボタンを定義する
  • ユーザーから情報を収集し、外部サービスからデータを取得する最初の実行エクスペリエンスを実装する
  • 関数を呼び出す、UI のないボタンを実装する
  • メッセージの本文にコンテンツを挿入する作業ウィンドウを実装する

ヒント

このチュートリアルの完成したバージョンが必要な場合は、 GitHub の Office アドイン サンプル リポジトリに進んでください

前提条件

  • Node.js (最新 LTS バージョン)。 Node.js サイトにアクセスして、オペレーティング システムに適したバージョンをダウンロードしてインストールします。

  • 最新バージョンの Yeoman と Office アドイン用の Yeoman ジェネレーター。これらのツールをグローバルにインストールするには、コマンド プロンプトから次のコマンドを実行します。

    npm install -g yo generator-office
    

    注:

    Yeomanのジェネレーターを過去に取付けている場合でも、npmからのパッケージを最新のバージョンにすることをお勧めします。

  • Microsoft 365 サブスクリプションに接続されている Office (Office for the web を含む)。

    注:

    まだ Office をお持ちでない場合は、Microsoft 365 開発者プログラムを通じてMicrosoft 365 E5開発者サブスクリプションを受ける資格があります。詳細については、FAQ を参照してください。 または、 1 か月間の無料試用版にサインアップ するか、 Microsoft 365 プランを購入することもできます。

セットアップ

このチュートリアルで作成するアドインは、ユーザーの GitHub アカウントから Gist を読み込み、選択した Gist をメッセージの本文に追加します。 ビルドするアドインのテストに使用可能な 2 つの新しい Gist を作成するには、次の手順を実行します。

  1. GitHub にログインします。

  2. 新しい Gist を作成します。

    • [Gist description...] フィールドに、「Hello World Markdown」と入力します。

    • [Filename including extension...] フィールドに、「test.md」と入力します。

    • 複数行のテキストボックスに、次の Markdown を追加します。

      # Hello World
      
      This is content converted from Markdown!
      
      Here's a JSON sample:
      
        ```json
        {
          "foo": "bar"
        }
        ```
      
    • [Create Public Gist] ボタンを選択します。

  3. もう 1 つの新しい Gist を作成します。

    • [Gist description...] フィールドに、「Hello World Html」と入力します。

    • [Filename including extension...] フィールドに、「test.html」と入力します。

    • 複数行のテキストボックスに、次の Markdown を追加します。

      <html>
        <head>
          <style>
          h1 {
            font-family: Calibri;
          }
          </style>
        </head>
        <body>
          <h1>Hello World!</h1>
          <p>This is a test</p>
        </body>
      </html>
      
    • [Create Public Gist] ボタンを選択します。

Outlook アドイン プロジェクトを作成する

  1. 次のコマンドを実行し、Yeoman ジェネレーターを使用してアドイン プロジェクトを作成します。 プロジェクトを含むフォルダーが現在のディレクトリに追加されます。

    yo office
    

    注:

    yo officeコマンドを実行すると、Yeoman のデータ収集ポリシーと Office アドイン CLI ツールに関するプロンプトが表示される場合があります。 提供された情報を使用して、必要に応じてプロンプトに応答します。

    プロンプトが表示されたら、以下の情報を入力してアドイン プロジェクトを作成します。

    • Choose a project type: (プロジェクトの種類を選択) - Office Add-in Task Pane project

    • Choose a script type: (スクリプトの種類を選択) - JavaScript

    • What would you want to name your add-in? (アドインの名前を何にしますか) - Git the gist

    • Which Office client application would you like to support?: (どの Office クライアント アプリケーションをサポートしますか) - Outlook

    コマンド ライン インターフェイスに表示された Yeoman ジェネレーターのプロンプトと回答。

    ウィザードを完了すると、ジェネレーターによってプロジェクトが作成されて、サポートしているノード コンポーネントがインストールされます。

    注:

    バージョン 20.0.0 以降 Node.js 使用している場合は、ジェネレーターがインストールを実行するときに、サポートされていないエンジンがあることを示す警告が表示されることがあります。 この問題の修正に取り組んでいます。 その間、警告は生成するジェネレーターまたはプロジェクトに影響しないため、無視できます。

    ヒント

    アドイン プロジェクトの作成後に Yeoman ジェネレーターが提供する次の手順ガイダンスは無視できます。 この記事中の詳しい手順は、このチュートリアルを完了するために必要なすべてのガイダンスを提供します。

  2. プロジェクトのルート ディレクトリに移動します。

    cd "Git the gist"
    
  3. このアドインでは、次のライブラリを使用します。

    • MarkdownをHTMLに変換する Showdown ライブラリ。
    • 相対URLを構築するためのURI.jsライブラリ。
    • DOM の 操作を簡略化するための jQuery ライブラリ。

    これらのツールをプロジェクトにインストールするには、プロジェクトのルート ディレクトリで次のコマンドを実行します。

    npm install showdown urijs jquery --save
    
  4. VS Code またはお好みのコード エディターでプロジェクトを開きます。

    ヒント

    Windows では、コマンド ラインからプロジェクトのルート ディレクトリに移動し、code . を入力して VS Code でそのフォルダーを開くことができます。 Mac では、VS Code でプロジェクト フォルダーを開くためにそのコマンドを使用する前に、code コマンドをパスに追加する必要があります。

マニフェストを更新する

アドインのマニフェストは、Outlook での表示方法を制御します。 またマニフェストは、アドインをアドイン一覧に表示する方法とリボンに表示するボタンを定義し、アドインによって使用される HTML ファイルと JavaScript ファイルの URL を設定します。

基本的な情報を指定する

manifest.xml ファイルで次の更新を行い、アドインに関する基本情報を指定します。

  1. ProviderName> 要素を<見つけて、既定値を会社名に置き換えます。

    <ProviderName>Contoso</ProviderName>
    
  2. Description> 要素を<見つけ、既定値をアドインの説明に置き換えて、ファイルを保存します。

    <Description DefaultValue="Allows users to access their GitHub gists."/>
    

生成されたアドインをテストする

この先に進める前に、ジェネレーターによって生成されたアドインをテストして、プロジェクトが正しく設定されていることを確認します。

注:

Office アドインでは、開発中でも HTTP ではなく HTTPS を使用する必要があります。 次のいずれかのコマンドを実行した後に証明書のインストールを求められた場合は、Yeoman ジェネレーターが提供する証明書をインストールするプロンプトに同意します。 変更を行うには、管理者としてコマンド プロンプトまたはターミナルを実行する必要がある場合もあります。

  1. プロジェクトのルート ディレクトリから次のコマンドを実行します。 このコマンドを実行すると、ローカル Web サーバーが起動し、アドインがサイドロードされます。

    npm start
    

    注:

    アドインが自動的にサイドロードされなかった場合は、「 Outlook アドインをサイドロードしてテストする 」の手順に従って、Outlook でアドインを手動でサイドロードします。

  2. Outlookで既存のメッセージを開き、タスクパネルを表示ボタンを選択します。

  3. WebView Stop On Load ダイアログ ボックスでプロンプトが表示されたら、[OK] を選択します。

    注:

    [キャンセル] を選択すると、このアドインのインスタンスの実行中はダイアログが表示されなくなります。 ただし、アドインを再起動すると、ダイアログはもう一度表示されます。

    すべてが正しく設定されている場合は、作業ウィンドウが開き、アドインのウェルカム ページが表示されます。

    [作業ウィンドウを表示] ボタンと、サンプルによって追加された Git the gist 作業ウィンドウ。

ボタンを定義する

基本のアドインの動作を確認したので、カスタマイズしてより多くの機能を追加できます。 既定のマニフェストでは、メッセージ閲覧ウィンドウ用のボタンのみ定義されています。 マニフェストを更新してメッセージ閲覧ウィンドウからボタンを削除し、メッセージ作成ウィンドウ用の 2 つの新しいボタンを定義してみましょう。

  • Insert gist (Gist の挿入): 作業ウィンドウを開くボタン

  • Insert default gist (既定の Gist の挿入): 関数を呼び出すボタン

MessageReadCommandSurface 拡張点を削除する

  1. manifest.xml ファイルを開きます。

  2. MessageReadCommandSurface 型の <ExtensionPoint> 要素を見つけて削除します (終了タグを含む)。 これにより、読み取りメッセージ ウィンドウからアドイン ボタンが削除されます。

MessageComposeCommandSurface 拡張点を追加する

  1. manifest.xmlで、 を読み取</DesktopFormFactor>るマニフェスト内の行を見つけます。

  2. この行の直前に、次の XML マークアップを挿入します。 このマークアップについて、次の情報にご注意ください。

    • の ExtensionPoint xsi:type="MessageComposeCommandSurface">は<、作成メッセージ ウィンドウに追加するボタンを定義していることを示します。

    • id="TabDefault"OfficeTab> 要素を<使用すると、リボンの既定のタブにボタンを追加することを示します。

    • Group> 要素は<groupLabel リソースによってラベルが設定された、新しいボタンのグループ化を定義します。

    • 最初<の Control> 要素には、 を<含む Action> 要素が含まれているため、このボタンによって作業ウィンドウが開きます。xsi:type="ShowTaskPane"

    • 2 番目<の Control> 要素には、 を<含む Action> 要素が含まれているため、このボタンは関数ファイルに含まれる JavaScript 関数を呼び出します。xsi:type="ExecuteFunction"

    <!-- Message Compose -->
    <ExtensionPoint xsi:type="MessageComposeCommandSurface">
      <OfficeTab id="TabDefault">
        <Group id="msgComposeCmdGroup">
          <Label resid="GroupLabel"/>
          <Control xsi:type="Button" id="msgComposeInsertGist">
            <Label resid="TaskpaneButton.Label"/>
            <Supertip>
              <Title resid="TaskpaneButton.Title"/>
              <Description resid="TaskpaneButton.Tooltip"/>
            </Supertip>
            <Icon>
              <bt:Image size="16" resid="Icon.16x16"/>
              <bt:Image size="32" resid="Icon.32x32"/>
              <bt:Image size="80" resid="Icon.80x80"/>
            </Icon>
            <Action xsi:type="ShowTaskpane">
              <SourceLocation resid="Taskpane.Url"/>
            </Action>
          </Control>
          <Control xsi:type="Button" id="msgComposeInsertDefaultGist">
            <Label resid="FunctionButton.Label"/>
            <Supertip>
              <Title resid="FunctionButton.Title"/>
              <Description resid="FunctionButton.Tooltip"/>
            </Supertip>
            <Icon>
              <bt:Image size="16" resid="Icon.16x16"/>
              <bt:Image size="32" resid="Icon.32x32"/>
              <bt:Image size="80" resid="Icon.80x80"/>
            </Icon>
            <Action xsi:type="ExecuteFunction">
              <FunctionName>insertDefaultGist</FunctionName>
            </Action>
          </Control>
        </Group>
      </OfficeTab>
    </ExtensionPoint>
    

マニフェスト内のリソースを更新する

前のコードでは、マニフェストが有効になる前に定義する必要のあるラベル、ヒント、URL が参照されています。 この情報は、マニフェストの [リソース>]< セクションで指定します。

  1. manifest.xmlで、マニフェスト ファイル内の Resources> 要素を見つけて<、要素全体 (終了タグを含む) を削除します。

  2. 同じ場所に次のマークアップを追加して、削除した Resources> 要素を<置き換えます。

    <Resources>
      <bt:Images>
        <bt:Image id="Icon.16x16" DefaultValue="https://localhost:3000/assets/icon-16.png"/>
        <bt:Image id="Icon.32x32" DefaultValue="https://localhost:3000/assets/icon-32.png"/>
        <bt:Image id="Icon.80x80" DefaultValue="https://localhost:3000/assets/icon-80.png"/>
      </bt:Images>
      <bt:Urls>
        <bt:Url id="Commands.Url" DefaultValue="https://localhost:3000/commands.html"/>
        <bt:Url id="Taskpane.Url" DefaultValue="https://localhost:3000/taskpane.html"/>
      </bt:Urls>
      <bt:ShortStrings>
        <bt:String id="GroupLabel" DefaultValue="Git the gist"/>
        <bt:String id="TaskpaneButton.Label" DefaultValue="Insert gist"/>
        <bt:String id="TaskpaneButton.Title" DefaultValue="Insert gist"/>
        <bt:String id="FunctionButton.Label" DefaultValue="Insert default gist"/>
        <bt:String id="FunctionButton.Title" DefaultValue="Insert default gist"/>
      </bt:ShortStrings>
      <bt:LongStrings>
        <bt:String id="TaskpaneButton.Tooltip" DefaultValue="Displays a list of your gists and allows you to insert their contents into the current message."/>
        <bt:String id="FunctionButton.Tooltip" DefaultValue="Inserts the content of the gist you mark as default into the current message."/>
      </bt:LongStrings>
    </Resources>
    
  3. マニフェストへの変更を保存します。

アドインを再インストールする

マニフェストの変更を有効にするには、アドインを再インストールする必要があります。

  1. Web サーバーが実行されている場合は、次のコマンドを実行します。

    npm stop
    
  2. 次のコマンドを実行し、ローカル Web サーバーを起動してアドインのサイドロードを自動的に行います。

    npm start
    

アドインを再インストールした後、メッセージ作成ウィンドウでInsert gistInsert default gistのコマンドを確認して、アドインが正常にインストールされたことを確認できます。 このアドインのビルドはまだ完了していないため、どちらを選択しても何も起こりません。

  • Windows Outlook 2016 以降でこのアドインを実行している場合は、新規作成メッセージ ウィンドウのリボンに 2 つの新しいボタンが表示されます。[gist の挿入] と [既定の gist の挿入] の 2 つの新しいボタンが表示されます。

    Windows 用 Outlook のリボン オーバーフロー メニュー上で強調表示されているアドインのボタン。

  • このアドインをOutlook on the webまたは新しい Outlook on Windows (プレビュー) で実行している場合は、新規作成メッセージ ウィンドウのリボンから [アプリ] を選択し、[Git the gist]\(ギストを Git\) を選択して、[Insert gist] オプションと [Insert default gist]\(既定の GIST の挿入\) オプションを表示します。

    Outlook on the web のメッセージ作成フォーム。アドイン ボタンとポップアップ メニューが強調表示されています。

最初の実行エクスペリエンスを実装する

このアドインでは、ユーザーの GitHub アカウントから Gist を読み込み、ユーザーが既定として選択した Gist を特定できる必要があります。 この目的を達成するため、アドインはユーザーに対して、GitHub のユーザー名を入力し、既存の Gist のコレクションから既定の Gist を選択するためのダイアログを表示する必要があります。 ユーザーからこの情報を収集するためのダイアログを表示する最初の実行エクスペリエンスを実装するには、このセクションの手順を実行します。

ダイアログの UI を作成する

まず、ダイアログの UI を作成します。

  1. ./srcフォルダー内に、settingsという名前の新しいサブフォルダーを作成します。

  2. ./src/settings フォルダーで、 という名前のファイルdialog.html作成します。

  3. dialog.htmlで、次のマークアップを追加して、GitHub ユーザー名のテキスト入力と、JavaScript 経由で設定される gists の空のリストを含む基本的なフォームを定義します。

    <!DOCTYPE html>
    <html>
    
    <head>
      <meta charset="UTF-8" />
      <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
      <title>Settings</title>
    
      <!-- Office JavaScript API -->
      <script type="text/javascript" src="https://appsforoffice.microsoft.com/lib/1.1/hosted/office.js"></script>
    
    <!-- For more information on Fluent UI, visit https://developer.microsoft.com/fluentui. -->
      <link rel="stylesheet" href="https://static2.sharepointonline.com/files/fabric/office-ui-fabric-core/9.6.1/css/fabric.min.css"/>
    
      <!-- Template styles -->
      <link href="dialog.css" rel="stylesheet" type="text/css" />
    </head>
    
    <body class="ms-font-l">
      <main>
        <section class="ms-font-m ms-fontColor-neutralPrimary">
          <div class="not-configured-warning ms-MessageBar ms-MessageBar--warning">
            <div class="ms-MessageBar-content">
              <div class="ms-MessageBar-icon">
                <i class="ms-Icon ms-Icon--Info"></i>
              </div>
              <div class="ms-MessageBar-text">
                Oops! It looks like you haven't configured <strong>Git the gist</strong> yet.
                <br/>
                Please configure your GitHub username and select a default gist, then try that action again!
              </div>
            </div>
          </div>
          <div class="ms-font-xxl">Settings</div>
          <div class="ms-Grid">
            <div class="ms-Grid-row">
              <div class="ms-TextField">
                <label class="ms-Label">GitHub Username</label>
                <input class="ms-TextField-field" id="github-user" type="text" value="" placeholder="Please enter your GitHub username">
              </div>
            </div>
            <div class="error-display ms-Grid-row">
              <div class="ms-font-l ms-fontWeight-semibold">An error occurred:</div>
              <pre><code id="error-text"></code></pre>
            </div>
            <div class="gist-list-container ms-Grid-row">
              <div class="list-title ms-font-xl ms-fontWeight-regular">Choose Default Gist</div>
              <form>
                <div id="gist-list">
                </div>
              </form>
            </div>
          </div>
          <div class="ms-Dialog-actions">
            <div class="ms-Dialog-actionsRight">
              <button class="ms-Dialog-action ms-Button ms-Button--primary" id="settings-done" disabled>
                <span class="ms-Button-label">Done</span>
              </button>
            </div>
          </div>
        </section>
      </main>
      <script type="text/javascript" src="../../node_modules/jquery/dist/jquery.js"></script>
      <script type="text/javascript" src="../helpers/gist-api.js"></script>
      <script type="text/javascript" src="dialog.js"></script>
    </body>
    
    </html>
    

    HTML ファイルが、まだ存在しない JavaScript ファイル gist-api.js を参照することがわかります。 このファイルは、以下の [GitHub からデータを取得する] セクションで作成されます。

  4. 変更内容を保存します。

  5. 次に、 ./src/settings フォルダーに dialog.css という名前のファイル 作成します。

  6. dialog.cssで、次のコードを追加して、dialog.htmlで使用されるスタイル指定します。

    section {
      margin: 10px 20px;
    }
    
    .not-configured-warning {
      display: none;
    }
    
    .error-display {
      display: none;
    }
    
    .gist-list-container {
      margin: 10px -8px;
      display: none;
    }
    
    .list-title {
      border-bottom: 1px solid #a6a6a6;
      padding-bottom: 5px;
    }
    
    ul {
      margin-top: 10px;
    }
    
    .ms-ListItem-secondaryText,
    .ms-ListItem-tertiaryText {
      padding-left: 15px;
    }
    
  7. 変更内容を保存します。

ダイアログの機能を開発する

これでダイアログの UI の定義が完了したので、次に実際に動作するためのコードを記述します。

  1. ./src/settings フォルダーで、 という名前のファイルdialog.js作成します。

  2. 次のコードを追加します。 このコードでは、イベントを登録するために jQuery を使用し、ユーザーの選択内容を呼び出し元に送り返すために messageParent メソッドを使用しています。

    (function() {
      'use strict';
    
      // The onReady function must be run each time a new page is loaded.
      Office.onReady(function() {
        $(document).ready(function() {
          if (window.location.search) {
            // Check if warning should be displayed.
            const warn = getParameterByName('warn');
    
            if (warn) {
              $('.not-configured-warning').show();
            } else {
              // See if the config values were passed.
              // If so, pre-populate the values.
              const user = getParameterByName('gitHubUserName');
              const gistId = getParameterByName('defaultGistId');
    
              $('#github-user').val(user);
              loadGists(user, function(success) {
                if (success) {
                  $('.ms-ListItem').removeClass('is-selected');
                  $('input').filter(function() {
                    return this.value === gistId;
                  }).addClass('is-selected').attr('checked', 'checked');
                  $('#settings-done').removeAttr('disabled');
                }
              });
            }
          }
    
          // When the GitHub username changes,
          // try to load gists.
          $('#github-user').on('change', function() {
            $('#gist-list').empty();
            const ghUser = $('#github-user').val();
    
            if (ghUser.length > 0) {
              loadGists(ghUser);
            }
          });
    
          // When the Done button is selected, send the
          // values back to the caller as a serialized
          // object.
          $('#settings-done').on('click', function() {
            const settings = {};
            settings.gitHubUserName = $('#github-user').val();
            const selectedGist = $('.ms-ListItem.is-selected');
    
            if (selectedGist) {
              settings.defaultGistId = selectedGist.val();
              sendMessage(JSON.stringify(settings));
            }
          });
        });
      });
    
      // Load gists for the user using the GitHub API
      // and build the list.
      function loadGists(user, callback) {
        getUserGists(user, function(gists, error) {
          if (error) {
            $('.gist-list-container').hide();
            $('#error-text').text(JSON.stringify(error, null, 2));
            $('.error-display').show();
    
            if (callback) callback(false);
          } else {
            $('.error-display').hide();
            buildGistList($('#gist-list'), gists, onGistSelected);
            $('.gist-list-container').show();
    
            if (callback) callback(true);
          }
        });
      }
    
      function onGistSelected() {
        $('.ms-ListItem').removeClass('is-selected').removeAttr('checked');
        $(this).children('.ms-ListItem').addClass('is-selected').attr('checked', 'checked');
        $('.not-configured-warning').hide();
        $('#settings-done').removeAttr('disabled');
      }
    
      function sendMessage(message) {
        Office.context.ui.messageParent(message);
      }
    
      function getParameterByName(name, url) {
        if (!url) {
          url = window.location.href;
        }
    
        name = name.replace(/[\[\]]/g, "\\$&");
        const regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
          results = regex.exec(url);
    
        if (!results) return null;
    
        if (!results[2]) return '';
    
        return decodeURIComponent(results[2].replace(/\+/g, " "));
      }
    })();
    
  3. 変更内容を保存します。

Webpackの機能設定を更新する

最後に、プロジェクトのルート ディレクトリにある webpack.config.js ファイルを開き、次の手順を実行します。

  1. configオブジェクト内でentryオブジェクトを探し、dialogの新しいエントリーを追加します。

    dialog: "./src/settings/dialog.js",
    

    これを実行すると、新しいentryオブジェクトは次のようになります。

    entry: {
      polyfill: ["core-js/stable", "regenerator-runtime/runtime"],
      taskpane: ["./src/taskpane/taskpane.js", "./src/taskpane/taskpane.html"],
      commands: "./src/commands/commands.js",
      dialog: "./src/settings/dialog.js",
    },
    
  2. config オブジェクト内で plugins 配列を探します。 new CopyWebpackPlugin オブジェクトの patterns 配列で、taskpane.cssdialog.css の新しいエントリを追加します。

    {
      from: "./src/taskpane/taskpane.css",
      to: "taskpane.css",
    },
    {
      from: "./src/settings/dialog.css",
      to: "dialog.css",
    },
    

    これを実行すると、new CopyWebpackPluginオブジェクトは次のようになります。

    new CopyWebpackPlugin({
      patterns: [
      {
        from: "./src/taskpane/taskpane.css",
        to: "taskpane.css",
      },
      {
        from: "./src/settings/dialog.css",
        to: "dialog.css",
      },
      {
        from: "assets/*",
        to: "assets/[name][ext][query]",
      },
      {
        from: "manifest*.xml",
        to: "[name]" + "[ext]",
        transform(content) {
          if (dev) {
            return content;
          } else {
            return content.toString().replace(new RegExp(urlDev, "g"), urlProd);
          }
        },
      },
    ]}),
    
  3. plugins オブジェクト内の同じ config 配列で、この新しいオブジェクトをその配列の末尾に追加します。

    new HtmlWebpackPlugin({
      filename: "dialog.html",
      template: "./src/settings/dialog.html",
      chunks: ["polyfill", "dialog"]
    })
    

    これを実行すると、新しいplugins配列は次のようになります。

    plugins: [
      new HtmlWebpackPlugin({
        filename: "taskpane.html",
        template: "./src/taskpane/taskpane.html",
        chunks: ["polyfill", "taskpane"],
      }),
      new CopyWebpackPlugin({
        patterns: [
          {
            from: "./src/taskpane/taskpane.css",
            to: "taskpane.css",
          },
          {
            from: "./src/settings/dialog.css",
            to: "dialog.css",
          },
          {
            from: "assets/*",
            to: "assets/[name][ext][query]",
          },
          {
            from: "manifest*.xml",
            to: "[name]." + buildType + "[ext]",
            transform(content) {
              if (dev) {
                return content;
              } else {
                return content.toString().replace(new RegExp(urlDev, "g"), urlProd);
              }
            },
          },
        ],
      }),
      new HtmlWebpackPlugin({
        filename: "commands.html",
        template: "./src/commands/commands.html",
        chunks: ["polyfill", "commands"],
      }),
      new HtmlWebpackPlugin({
        filename: "dialog.html",
        template: "./src/settings/dialog.html",
        chunks: ["polyfill", "dialog"]
      })
    ],
    

GitHub からデータを取得する

上記で作成した dialog.js ファイルでは、GitHub ユーザー名フィールドについて変更イベントが発生したときにアドインが Gist を読み込む必要があることを指定しています。 GitHub からユーザーの Gist を取得するには、GitHub Gist の API を使用します。

  1. ./srcフォルダー内に、helpersという名前の新しいサブフォルダーを作成します。

  2. ./src/helpers フォルダーで、 という名前のファイルgist-api.js作成します。

  3. gist-api.jsで、次のコードを追加して GitHub からユーザーの gists を取得し、gists の一覧を作成します。

    function getUserGists(user, callback) {
      const requestUrl = 'https://api.github.com/users/' + user + '/gists';
    
      $.ajax({
        url: requestUrl,
        dataType: 'json'
      }).done(function(gists) {
        callback(gists);
      }).fail(function(error) {
        callback(null, error);
      });
    }
    
    function buildGistList(parent, gists, clickFunc) {
      gists.forEach(function(gist) {
    
        const listItem = $('<div/>')
          .appendTo(parent);
    
        const radioItem = $('<input>')
          .addClass('ms-ListItem')
          .addClass('is-selectable')
          .attr('type', 'radio')
          .attr('name', 'gists')
          .attr('tabindex', 0)
          .val(gist.id)
          .appendTo(listItem);
    
        const descPrimary = $('<span/>')
          .addClass('ms-ListItem-primaryText')
          .text(gist.description)
          .appendTo(listItem);
    
        const descSecondary = $('<span/>')
          .addClass('ms-ListItem-secondaryText')
          .text(' - ' + buildFileList(gist.files))
          .appendTo(listItem);
    
        const updated = new Date(gist.updated_at);
    
        const descTertiary = $('<span/>')
          .addClass('ms-ListItem-tertiaryText')
          .text(' - Last updated ' + updated.toLocaleString())
          .appendTo(listItem);
    
        listItem.on('click', clickFunc);
      });  
    }
    
    function buildFileList(files) {
    
      let fileList = '';
    
      for (let file in files) {
        if (files.hasOwnProperty(file)) {
          if (fileList.length > 0) {
            fileList = fileList + ', ';
          }
    
          fileList = fileList + files[file].filename + ' (' + files[file].language + ')';
        }
      }
    
      return fileList;
    }
    
  4. 変更内容を保存します。

  5. 次のコマンドを実行してプロジェクトを再構築します。

    npm run build
    

UI のないボタンを実装する

このアドインの [既定の要点の挿入 ] ボタンは、多くのアドイン ボタンと同様に作業ウィンドウを開くのではなく、JavaScript 関数を呼び出す UI なしのボタンです。 ユーザーが [ 既定の gist の挿入 ] ボタンを選択すると、対応する JavaScript 関数によってアドインが構成されているかどうかを確認します。

  • アドインが既に構成されている場合、関数は、ユーザーが既定として選択した要点の内容を読み込み、メッセージの本文に挿入します。

  • アドインがまだ構成されていない場合は、必要な情報を入力するようにユーザーに求める設定ダイアログが表示されます。

関数ファイルを更新する (HTML)

UI レス ボタンによって呼び出される関数は、対応するフォーム ファクターのマニフェストの FunctionFile> 要素によって<指定されるファイルで定義する必要があります。 このアドインのマニフェストでは、https://localhost:3000/commands.html が関数ファイルとして指定されています。

  1. ./src/commands/commands.html を開き、内容全体を次のマークアップに置き換えます。

    <!DOCTYPE html>
    <html>
    
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
    
        <!-- Office JavaScript API -->
        <script type="text/javascript" src="https://appsforoffice.microsoft.com/lib/1.1/hosted/office.js"></script>
    
        <script type="text/javascript" src="../../node_modules/jquery/dist/jquery.js"></script>
        <script type="text/javascript" src="../../node_modules/showdown/dist/showdown.min.js"></script>
        <script type="text/javascript" src="../../node_modules/urijs/src/URI.min.js"></script>
        <script type="text/javascript" src="../helpers/addin-config.js"></script>
        <script type="text/javascript" src="../helpers/gist-api.js"></script>
    </head>
    
    <body>
      <!-- NOTE: The body is empty on purpose. Since functions in commands.js are
           invoked via a button, there is no UI to render. -->
    </body>
    
    </html>
    

    HTML ファイルが、まだ存在しない JavaScript ファイル addin-config.js を参照することがわかります。 このファイルは、このチュートリアルの後半の [構成設定を管理するファイルを作成する] セクションで作成されます。

  2. 変更内容を保存します。

関数ファイルを更新する (JavaScript)

  1. ファイル./src/commands/commands.jsを開き、内容全体を次のコードに置き換えます。 insertDefaultGist 関数によってアドインがまだ構成されていないと判断された場合は、パラメーターが?warn=1ダイアログ URL に追加されることに注意してください。 これにより、./src/settings/dialog.html で定義されているメッセージ バーが設定ダイアログに表示され、このダイアログが表示されている理由をユーザーに示すことができます。

    let config;
    let btnEvent;
    
    // The onReady function must be run each time a new page is loaded.
    Office.onReady();
    
    function showError(error) {
      Office.context.mailbox.item.notificationMessages.replaceAsync('github-error', {
        type: 'errorMessage',
        message: error
      });
    }
    
    let settingsDialog;
    
    function insertDefaultGist(event) {
      config = getConfig();
    
      // Check if the add-in has been configured.
      if (config && config.defaultGistId) {
        // Get the default gist content and insert.
        try {
          getGist(config.defaultGistId, function(gist, error) {
            if (gist) {
              buildBodyContent(gist, function (content, error) {
                if (content) {
                  Office.context.mailbox.item.body.setSelectedDataAsync(
                    content,
                    { coercionType: Office.CoercionType.Html },
                    function (result) {
                      event.completed();
                    }
                  );
                } else {
                  showError(error);
                  event.completed();
                }
              });
            } else {
              showError(error);
              event.completed();
            }
          });
        } catch (err) {
          showError(err);
          event.completed();
        }
    
      } else {
        // Save the event object so we can finish up later.
        btnEvent = event;
        // Not configured yet, display settings dialog with
        // warn=1 to display warning.
        const url = new URI('dialog.html?warn=1').absoluteTo(window.location).toString();
        const dialogOptions = { width: 20, height: 40, displayInIframe: true };
    
        Office.context.ui.displayDialogAsync(url, dialogOptions, function(result) {
          settingsDialog = result.value;
          settingsDialog.addEventHandler(Office.EventType.DialogMessageReceived, receiveMessage);
          settingsDialog.addEventHandler(Office.EventType.DialogEventReceived, dialogClosed);
        });
      }
    }
    
    // Register the function.
    Office.actions.associate("insertDefaultGist", insertDefaultGist);
    
    function receiveMessage(message) {
      config = JSON.parse(message.message);
      setConfig(config, function(result) {
        settingsDialog.close();
        settingsDialog = null;
        btnEvent.completed();
        btnEvent = null;
      });
    }
    
    function dialogClosed(message) {
      settingsDialog = null;
      btnEvent.completed();
      btnEvent = null;
    }
    
  2. 変更内容を保存します。

構成設定を管理するファイルを作成する

  1. ./src/helpers フォルダーに addin-config.js という名前のファイルを作成し、次のコードを追加します。 このコードは、RoamingSettings オブジェクトを使用して、構成値を取得または設定するものです。

    function getConfig() {
      const config = {};
    
      config.gitHubUserName = Office.context.roamingSettings.get('gitHubUserName');
      config.defaultGistId = Office.context.roamingSettings.get('defaultGistId');
    
      return config;
    }
    
    function setConfig(config, callback) {
      Office.context.roamingSettings.set('gitHubUserName', config.gitHubUserName);
      Office.context.roamingSettings.set('defaultGistId', config.defaultGistId);
    
      Office.context.roamingSettings.saveAsync(callback);
    }
    
  2. 変更内容を保存します。

Gist を処理する新しい関数を作成する

  1. ./src/helpers/gist-api.js ファイルを開き、以下の機能を追加します。 次の点に注意してください。

    • gist に HTML が含まれている場合、アドインは HTML をそのままメッセージの本文に挿入します。

    • gist に Markdown が含まれている場合、アドインは ショーダウン ライブラリを使用して Markdown を HTML に変換し、結果の HTML をメッセージの本文に挿入します。

    • Gist に HTML またはマークダウン以外のものが含まれている場合、アドインはそのコンテンツをコード スニペットとしてメッセージの本文に挿入します。

    function getGist(gistId, callback) {
      const requestUrl = 'https://api.github.com/gists/' + gistId;
    
      $.ajax({
        url: requestUrl,
        dataType: 'json'
      }).done(function(gist) {
        callback(gist);
      }).fail(function(error) {
        callback(null, error);
      });
    }
    
    function buildBodyContent(gist, callback) {
      // Find the first non-truncated file in the gist
      // and use it.
      for (let filename in gist.files) {
        if (gist.files.hasOwnProperty(filename)) {
          const file = gist.files[filename];
          if (!file.truncated) {
            // We have a winner.
            switch (file.language) {
              case 'HTML':
                // Insert as is.
                callback(file.content);
                break;
              case 'Markdown':
                // Convert Markdown to HTML.
                const converter = new showdown.Converter();
                const html = converter.makeHtml(file.content);
                callback(html);
                break;
              default:
                // Insert contents as a <code> block.
                let codeBlock = '<pre><code>';
                codeBlock = codeBlock + file.content;
                codeBlock = codeBlock + '</code></pre>';
                callback(codeBlock);
            }
            return;
          }
        }
      }
      callback(null, 'No suitable file found in the gist');
    }
    
  2. 変更内容を保存します。

[既定の Gist の挿入] ボタンをテストする

  1. ローカル Web サーバーがまだ実行されていない場合は、コマンド プロンプトから を実行 npm start します。

  2. Outlook を開き、新しいメッセージを作成します。

  3. メッセージの作成ウィンドウで、[Insert default gist] ボタンを選択します。 GitHub ユーザー名を設定するためのプロンプトから始めて、アドインを構成できるダイアログが表示されます。

    アドインを構成するためのダイアログ プロンプト。

  4. [設定] ダイアログで、GitHub ユーザー名を入力し、 タブ またはダイアログ内の他の場所をクリックして 変更 イベントを呼び出します。これにより、パブリック gists の一覧が読み込まれます。 既定とする Gist を選択し、[Done] を選択します。

    アドインの設定ダイアログ。

  5. もう一度 [Insert default gist] ボタンを選択します。 今度は、Gist のコンテンツが電子メールの本文に挿入されます。

    注:

    Windows 上の Outlook: 最新の設定を選択するには、[メッセージの作成] ウィンドウを閉じて、もう一度開く必要がある場合があります。

作業ウィンドウを実装する

このアドインの [GIST の挿入] ボタンをクリックすると、作業ウィンドウが開き、ユーザーの gist が表示されます。 ここでユーザーはメッセージの本文に挿入する Gist を選択することができます。 ユーザーがまだアドインを構成していない場合、ダイアログが表示されて構成するように求められます。

作業ペインのHTMLを指定する

  1. 作成したプロジェクトでは、作業ペインのHTMLはファイル./src/taskpane/taskpane.htmlで指定されています。 該当ファイルを開き、内容全体を次のマークアップで置き換えます。

    <!DOCTYPE html>
    <html>
    
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Contoso Task Pane Add-in</title>
    
        <!-- Office JavaScript API -->
        <script type="text/javascript" src="https://appsforoffice.microsoft.com/lib/1.1/hosted/office.js"></script>
    
       <!-- For more information on Fluent UI, visit https://developer.microsoft.com/fluentui. -->
        <link rel="stylesheet" href="https://static2.sharepointonline.com/files/fabric/office-ui-fabric-core/9.6.1/css/fabric.min.css"/>
    
        <!-- Template styles -->
        <link href="taskpane.css" rel="stylesheet" type="text/css" />
    </head>
    
    <body class="ms-font-l ms-landing-page">
      <main class="ms-landing-page__main">
        <section class="ms-landing-page__content ms-font-m ms-fontColor-neutralPrimary">
          <div id="not-configured" style="display: none;">
            <div class="centered ms-font-xxl ms-u-textAlignCenter">Welcome!</div>
            <div class="ms-font-xl" id="settings-prompt">Please choose the <strong>Settings</strong> icon at the bottom of this window to configure this add-in.</div>
          </div>
          <div id="gist-list-container" style="display: none;">
            <form>
              <div id="gist-list">
              </div>
            </form>
          </div>
          <div id="error-display" style="display: none;" class="ms-u-borderBase ms-fontColor-error ms-font-m ms-bgColor-error ms-borderColor-error">
          </div>
        </section>
        <button class="ms-Button ms-Button--primary" id="insert-button" tabindex=0 disabled>
          <span class="ms-Button-label">Insert</span>
        </button>
      </main>
      <footer class="ms-landing-page__footer ms-bgColor-themePrimary">
        <div class="ms-landing-page__footer--left">
          <img src="../../assets/logo-filled.png" />
          <h1 class="ms-font-xl ms-fontWeight-semilight ms-fontColor-white">Git the gist</h1>
        </div>
        <div id="settings-icon" class="ms-landing-page__footer--right" aria-label="Settings" tabindex=0>
          <i class="ms-Icon enlarge ms-Icon--Settings ms-fontColor-white"></i>
        </div>
      </footer>
      <script type="text/javascript" src="../../node_modules/jquery/dist/jquery.js"></script>
      <script type="text/javascript" src="../../node_modules/showdown/dist/showdown.min.js"></script>
      <script type="text/javascript" src="../../node_modules/urijs/src/URI.min.js"></script>
      <script type="text/javascript" src="../helpers/addin-config.js"></script>
      <script type="text/javascript" src="../helpers/gist-api.js"></script>
      <script type="text/javascript" src="taskpane.js"></script>
    </body>
    
    </html>
    
  2. 変更内容を保存します。

作業ペインのCSSを指定する

  1. 作成したプロジェクトでは、作業ウィンドウのCSSは./src/taskpane/taskpane.cssファイルで指定されています。 該当ファイルを開き、内容全体を次のコードで置き換えます。

    /* Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See full license in root of repo. */
    html, body {
      width: 100%;
      height: 100%;
      margin: 0;
      padding: 0;
      overflow: auto; }
    
    body {
      position: relative;
      font-size: 16px; }
    
    main {
      height: 100%;
      overflow-y: auto; }
    
    footer {
      width: 100%;
      position: relative;
      bottom: 0;
      margin-top: 10px;}
    
    p, h1, h2, h3, h4, h5, h6 {
      margin: 0;
      padding: 0; }
    
    ul {
      padding: 0; }
    
    #settings-prompt {
      margin: 10px 0;
    }
    
    #error-display {
      padding: 10px;
    }
    
    #insert-button {
      margin: 0 10px;
    }
    
    .clearfix {
      display: block;
      clear: both;
      height: 0; }
    
    .pointerCursor {
      cursor: pointer; }
    
    .invisible {
      visibility: hidden; }
    
    .undisplayed {
      display: none; }
    
    .ms-Icon.enlarge {
      position: relative;
      font-size: 20px;
      top: 4px; }
    
    .ms-ListItem-secondaryText,
    .ms-ListItem-tertiaryText {
      padding-left: 15px;
    }
    
    .ms-landing-page {
      display: -webkit-flex;
      display: flex;
      -webkit-flex-direction: column;
              flex-direction: column;
      -webkit-flex-wrap: nowrap;
              flex-wrap: nowrap;
      height: 100%; }
    
    .ms-landing-page__main {
      display: -webkit-flex;
      display: flex;
      -webkit-flex-direction: column;
              flex-direction: column;
      -webkit-flex-wrap: nowrap;
              flex-wrap: nowrap;
      -webkit-flex: 1 1 0;
              flex: 1 1 0;
      height: 100%; }
    
    .ms-landing-page__content {
      display: -webkit-flex;
      display: flex;
      -webkit-flex-direction: column;
              flex-direction: column;
      -webkit-flex-wrap: nowrap;
              flex-wrap: nowrap;
      height: 100%;
      -webkit-flex: 1 1 0;
              flex: 1 1 0;
      padding: 20px; }
    
    .ms-landing-page__content h2 {
      margin-bottom: 20px; }
    
    .ms-landing-page__footer {
      display: -webkit-inline-flex;
      display: inline-flex;
      -webkit-justify-content: center;
              justify-content: center;
      -webkit-align-items: center;
              align-items: center; }
    
    .ms-landing-page__footer--left {
      transition: background ease 0.1s, color ease 0.1s;
      display: -webkit-inline-flex;
      display: inline-flex;
      -webkit-justify-content: flex-start;
              justify-content: flex-start;
      -webkit-align-items: center;
              align-items: center;
      -webkit-flex: 1 0 0px;
              flex: 1 0 0px;
      padding: 20px; }
    
    .ms-landing-page__footer--left:active {
      cursor: default; }
    
    .ms-landing-page__footer--left--disabled {
      opacity: 0.6;
      pointer-events: none;
      cursor: not-allowed; }
    
    .ms-landing-page__footer--left--disabled:active, .ms-landing-page__footer--left--disabled:hover {
      background: transparent; }
    
    .ms-landing-page__footer--left img {
      width: 40px;
      height: 40px; }
    
    .ms-landing-page__footer--left h1 {
      -webkit-flex: 1 0 0px;
              flex: 1 0 0px;
      margin-left: 15px;
      text-align: left;
      width: auto;
      max-width: auto;
      overflow: hidden;
      white-space: nowrap;
      text-overflow: ellipsis; }
    
    .ms-landing-page__footer--right {
      transition: background ease 0.1s, color ease 0.1s;
      padding: 29px 20px; }
    
    .ms-landing-page__footer--right:active, .ms-landing-page__footer--right:hover {
      background: #005ca4;
      cursor: pointer; }
    
    .ms-landing-page__footer--right:active {
      background: #005ca4; }
    
    .ms-landing-page__footer--right--disabled {
      opacity: 0.6;
      pointer-events: none;
      cursor: not-allowed; }
    
    .ms-landing-page__footer--right--disabled:active, .ms-landing-page__footer--right--disabled:hover {
      background: transparent; }
    
  2. 変更内容を保存します。

作業ペインのJavaScriptを指定する

  1. 作成したプロジェクトでは、作業ペインのJavaScriptはファイル./src/taskpane/taskpane.jsで指定されています。 該当ファイルを開き、内容全体を次のコードで置き換えます。

    (function() {
      'use strict';
    
      let config;
      let settingsDialog;
    
      Office.onReady(function() {
        $(document).ready(function() {
          config = getConfig();
    
          // Check if add-in is configured.
          if (config && config.gitHubUserName) {
            // If configured, load the gist list.
            loadGists(config.gitHubUserName);
          } else {
            // Not configured yet.
            $('#not-configured').show();
          }
    
          // When insert button is selected, build the content
          // and insert into the body.
          $('#insert-button').on('click', function() {
            const gistId = $('.ms-ListItem.is-selected').val();
            getGist(gistId, function(gist, error) {
              if (gist) {
                buildBodyContent(gist, function (content, error) {
                  if (content) {
                    Office.context.mailbox.item.body.setSelectedDataAsync(
                      content,
                      { coercionType: Office.CoercionType.Html },
                      function (result) {
                        if (result.status === Office.AsyncResultStatus.Failed) {
                          showError("Could not insert gist: " + result.error.message);
                        }
                      }
                    );
                  } else {
                    showError('Could not create insertable content: ' + error);
                  }
                });
              } else {
                showError('Could not retrieve gist: ' + error);
              }
            });
          });
    
          // When the settings icon is selected, open the settings dialog.
          $('#settings-icon').on('click', function() {
            // Display settings dialog.
            let url = new URI('dialog.html').absoluteTo(window.location).toString();
            if (config) {
              // If the add-in has already been configured, pass the existing values
              // to the dialog.
              url = url + '?gitHubUserName=' + config.gitHubUserName + '&defaultGistId=' + config.defaultGistId;
            }
    
            const dialogOptions = { width: 20, height: 40, displayInIframe: true };
    
            Office.context.ui.displayDialogAsync(url, dialogOptions, function(result) {
              settingsDialog = result.value;
              settingsDialog.addEventHandler(Office.EventType.DialogMessageReceived, receiveMessage);
              settingsDialog.addEventHandler(Office.EventType.DialogEventReceived, dialogClosed);
            });
          })
        });
      });
    
      function loadGists(user) {
        $('#error-display').hide();
        $('#not-configured').hide();
        $('#gist-list-container').show();
    
        getUserGists(user, function(gists, error) {
          if (error) {
    
          } else {
            $('#gist-list').empty();
            buildGistList($('#gist-list'), gists, onGistSelected);
          }
        });
      }
    
      function onGistSelected() {
        $('#insert-button').removeAttr('disabled');
        $('.ms-ListItem').removeClass('is-selected').removeAttr('checked');
        $(this).children('.ms-ListItem').addClass('is-selected').attr('checked', 'checked');
      }
    
      function showError(error) {
        $('#not-configured').hide();
        $('#gist-list-container').hide();
        $('#error-display').text(error);
        $('#error-display').show();
      }
    
      function receiveMessage(message) {
        config = JSON.parse(message.message);
        setConfig(config, function(result) {
          settingsDialog.close();
          settingsDialog = null;
          loadGists(config.gitHubUserName);
        });
      }
    
      function dialogClosed(message) {
        settingsDialog = null;
      }
    })();
    
  2. 変更内容を保存します。

[Insert gist] ボタンをテストする

  1. ローカル Web サーバーがまだ実行されていない場合は、コマンド プロンプトから を実行 npm start します。

  2. Outlook を開き、新しいメッセージを作成します。

  3. メッセージの作成ウィンドウで、[Insert gist] ボタンを選択します。 作成フォームの右側に作業ウィンドウが表示されます。

  4. 作業ウィンドウで、[Hello World Html] を選択し、[Insert] を選択してメッセージの本文にその Gist を挿入します。

アドイン作業ウィンドウと、メッセージ本文に表示される選択した gist コンテンツ。

次のステップ

このチュートリアルでは、コンテンツをメッセージの本文に挿入するためにメッセージ作成モードで使用可能な Outlook アドインを作成しました。 Outlook アドインの開発に関する詳細については、次の記事に進んでください。

コード サンプル

関連項目