チュートリアル: メッセージ作成 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 プランを購入することもできます。
Visual Studio Code (VS Code) または任意のコード エディター。
Outlook on the web、新しい Outlook on Windows、または Windows でOutlook 2016以降 (Microsoft 365 アカウントに接続)。
GitHub アカウント。
セットアップ
このチュートリアルで作成するアドインは、ユーザーの GitHub アカウントから Gist を読み込み、選択した Gist をメッセージの本文に追加します。 ビルドするアドインのテストに使用可能な 2 つの新しい Gist を作成するには、次の手順を実行します。
GitHub にログインします。
新しい 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] ボタンを選択します。
-
[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 アドイン プロジェクトを作成する
次のコマンドを実行し、Yeoman ジェネレーターを使用してアドイン プロジェクトを作成します。 プロジェクトを含むフォルダーが現在のディレクトリに追加されます。
yo office
注:
yo office
コマンドを実行すると、Yeoman のデータ収集ポリシーと Office アドイン CLI ツールに関するプロンプトが表示される場合があります。 提供された情報を使用して、必要に応じてプロンプトに応答します。プロンプトが表示されたら、以下の情報を入力してアドイン プロジェクトを作成します。
プロジェクトを作成する手順は、マニフェストの種類によって若干異なります。
注:
Microsoft 365 の統合マニフェストを使用すると、Outlook アドインと Teams アプリを 1 つの開発と展開の 1 つのユニットとして組み合わせることができます。 統合マニフェストのサポートを Excel、PowerPoint、Word、カスタム Copilot 開発、および Microsoft 365 のその他の拡張機能に拡張しています。 詳細については、「 統合マニフェストを使用した Office アドイン」を参照してください。 Teams アプリと Outlook アドインの組み合わせのサンプルについては、「 割引プラン」を参照してください。
統合マニフェストに関するフィードバックをお寄せください。 提案がある場合は、 Office JavaScript ライブラリのリポジトリに問題を作成してください。
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
どのマニフェストを使用しますか? -
unified manifest for Microsoft 365
ウィザードを完了すると、ジェネレーターによってプロジェクトが作成されて、サポートしているノード コンポーネントがインストールされます。
プロジェクトのルート ディレクトリに移動します。
cd "Git the gist"
このアドインでは、次のライブラリを使用します。
これらのツールをプロジェクトにインストールするには、プロジェクトのルート ディレクトリで次のコマンドを実行します。
npm install showdown urijs jquery --save
VS Code またはお好みのコード エディターでプロジェクトを開きます。
ヒント
Windows では、コマンド ラインからプロジェクトのルート ディレクトリに移動し、
code .
を入力して VS Code でそのフォルダーを開くことができます。 Mac では、VS Code でプロジェクト フォルダーを開くためにそのコマンドを使用する前に、code
コマンドをパスに追加する必要があります。
マニフェストを更新する
アドインのマニフェストは、Outlook での表示方法を制御します。 またマニフェストは、アドインをアドイン一覧に表示する方法とリボンに表示するボタンを定義し、アドインによって使用される HTML ファイルと JavaScript ファイルの URL を設定します。
基本的な情報を指定する
マニフェスト ファイルで次の更新を行って、アドインに関する基本的な情報を指定します。
"description" プロパティを見つけ、既定の "short" と "long" の値をアドインの説明に置き換えて、ファイルを保存します。
"description": { "short": "Gets gists.", "full": "Allows users to access their GitHub gists." },
ファイルを保存します。
生成されたアドインをテストする
この先に進める前に、ジェネレーターによって生成されたアドインをテストして、プロジェクトが正しく設定されていることを確認します。
注:
Office アドインでは、開発中でも HTTP ではなく HTTPS を使用する必要があります。 次のいずれかのコマンドを実行した後に証明書のインストールを求められた場合は、Yeoman ジェネレーターが提供する証明書をインストールするプロンプトに同意します。 変更を行うには、管理者としてコマンド プロンプトまたはターミナルを実行する必要がある場合もあります。
初めてコンピューターで Office アドインを開発する場合は、コマンド ラインで、Microsoft Edge WebView にループバックの除外を許可するように求められる場合があります (「Microsoft Edge WebView の localhost ループバックを許可する」)。 メッセージが表示されたら、「
Y
」と入力して除外を許可します。 除外を許可するには管理者特権が必要であることに注意してください。 許可されたら、(マシンから除外を削除しない限り) 今後 Office アドインをサイドロードするときに、除外を求められません。 詳細については、 Office アドインを読み込むか Fiddler を使用する場合は、「localhost からこのアドインを開くことができない」を参照してください。
プロジェクトのルート ディレクトリから次のコマンドを実行します。 このコマンドを実行すると、ローカル Web サーバーが起動し、アドインがサイドロードされます。
npm start
注:
アドインが自動的にサイドロードされなかった場合は、「 Outlook アドインをサイドロードしてテストする 」の手順に従って、Outlook でアドインを手動でサイドロードします。
Outlookで既存のメッセージを開き、タスクパネルを表示ボタンを選択します。
WebView Stop On Load ダイアログ ボックスでプロンプトが表示されたら、[OK] を選択します。
注:
[キャンセル] を選択すると、このアドインのインスタンスの実行中はダイアログが表示されなくなります。 ただし、アドインを再起動すると、ダイアログはもう一度表示されます。
すべてが正しく設定されている場合は、作業ウィンドウが開き、アドインのウェルカム ページが表示されます。
ローカル Web サーバーを停止してアドインをアンインストールする場合は、該当する手順に従います。
サーバーを停止するには、次のコマンドを実行します。
npm start
を使用した場合は、次のコマンドもアドインをアンインストールする必要があります。npm stop
アドインを手動でサイドロードした場合は、「 サイドロードされたアドインを削除する」を参照してください。
ボタンを定義する
基本のアドインの動作を確認したので、カスタマイズしてより多くの機能を追加できます。 既定のマニフェストでは、メッセージ閲覧ウィンドウ用のボタンのみ定義されています。 マニフェストを更新してメッセージ閲覧ウィンドウからボタンを削除し、メッセージ作成ウィンドウ用の 2 つの新しいボタンを定義してみましょう。
Insert gist (Gist の挿入): 作業ウィンドウを開くボタン
Insert default gist (既定の Gist の挿入): 関数を呼び出すボタン
プロシージャは、使用しているマニフェストによって異なります。
次の手順を実行します。
manifest.json ファイルを開きます。
"extensions.runtimes" 配列には、2 つのランタイム オブジェクトがあります。 2 つ目の "id" が "CommandsRuntime" の場合は、"actions.id" を "insertDefaultGist" に変更します。 これは、後の手順で作成する関数の名前です。 完了すると、ランタイム オブジェクトは次のようになります。
{ "id": "CommandsRuntime", "type": "general", "code": { "page": "https://localhost:3000/commands.html", "script": "https://localhost:3000/commands.js" }, "lifetime": "short", "actions": [ { "id": "insertDefaultGist", "type": "executeFunction", "displayName": "action" } ] }
"extensions.ribbons.contexts" 配列の項目を "mailCompose" に変更します。 つまり、ボタンは新しいメッセージまたは返信ウィンドウにのみ表示されます。
"contexts": [ "mailCompose" ],
"extensions.ribbons.tabs.groups" 配列にはグループ オブジェクトがあります。 このオブジェクトに次の変更を加えます。
- "id" プロパティを "msgComposeCmdGroup" に変更します。
- "label" プロパティを "Git the gist" に変更します。
同じグループ オブジェクトには、2 つのコントロール オブジェクトを持つ "controls" 配列があります。 それぞれの JSON に変更を加える必要があります。 最初の手順では、次の手順を実行します。
- "id" を "msgComposeInsertGist" に変更します。
- "label" を "Insert gist" に変更します。
- "supertip.title" を "Insert gist" に変更します。
- "supertip.description" を "gists の一覧を表示し、その内容を現在のメッセージに挿入できます" に変更します。
2 番目のコントロール オブジェクトで、次の手順を実行します。
- "id" を "msgComposeInsertDefaultGist" に変更します。
- "label" を "Insert default gist" に変更します。
- "supertip.title" を "Insert default gist" に変更します。
- "supertip.description" を "既定としてマークした要点の内容を現在のメッセージに挿入します" に変更します。
- "actionId" を "insertDefaultGist" に変更します。 これは、前の手順で設定した "CommandsRuntime" の "action.id" と一致します。
完了すると、"ribbons" プロパティは次のようになります。
"ribbons": [ { "contexts": [ "mailCompose" ], "tabs": [ { "builtInTabId": "TabDefault", "groups": [ { "id": "msgComposeCmdGroup", "label": "Git the gist", "icons": [ { "size": 16, "file": "https://localhost:3000/assets/icon-16.png" }, { "size": 32, "file": "https://localhost:3000/assets/icon-32.png" }, { "size": 80, "file": "https://localhost:3000/assets/icon-80.png" } ], "controls": [ { "id": "msgComposeInsertGist", "type": "button", "label": "Insert gist", "icons": [ { "size": 16, "file": "https://localhost:3000/assets/icon-16.png" }, { "size": 32, "file": "https://localhost:3000/assets/icon-32.png" }, { "size": 80, "file": "https://localhost:3000/assets/icon-80.png" } ], "supertip": { "title": "Insert gist", "description": "Displays a list of your gists and allows you to insert their contents into the current message." }, "actionId": "TaskPaneRuntimeShow" }, { "id": "msgComposeInsertDefaultGist", "type": "button", "label": "Insert default gist", "icons": [ { "size": 16, "file": "https://localhost:3000/assets/icon-16.png" }, { "size": 32, "file": "https://localhost:3000/assets/icon-32.png" }, { "size": 80, "file": "https://localhost:3000/assets/icon-80.png" } ], "supertip": { "title": "Insert default gist", "description": "Inserts the content of the gist you mark as default into the current message." }, "actionId": "insertDefaultGist" } ] } ] } ] } ]
マニフェストへの変更を保存します。
アドインを再インストールする
マニフェストの変更を有効にするには、アドインを再インストールする必要があります。
Web サーバーが実行されている場合は、次のコマンドを実行します。
npm stop
次のコマンドを実行し、ローカル Web サーバーを起動してアドインのサイドロードを自動的に行います。
npm start
アドインを再インストールした後、メッセージ作成ウィンドウでInsert gistとInsert default gistのコマンドを確認して、アドインが正常にインストールされたことを確認できます。 このアドインのビルドはまだ完了していないため、どちらを選択しても何も起こりません。
Windows Outlook 2016 以降でこのアドインを実行している場合は、新規作成メッセージ ウィンドウのリボンに 2 つの新しいボタンが表示されます。[gist の挿入] と [既定の gist の挿入] の 2 つの新しいボタンが表示されます。
このアドインをOutlook on the webまたは新しい Outlook on Windows で実行している場合は、新規作成メッセージ ウィンドウのリボンから [アプリ] を選択し、[Git the gist]\(ギストの挿入\) と [既定の GIST の挿入] オプションを表示します。
最初の実行エクスペリエンスを実装する
このアドインでは、ユーザーの GitHub アカウントから Gist を読み込み、ユーザーが既定として選択した Gist を特定できる必要があります。 この目的を達成するため、アドインはユーザーに対して、GitHub のユーザー名を入力し、既存の Gist のコレクションから既定の Gist を選択するためのダイアログを表示する必要があります。 ユーザーからこの情報を収集するためのダイアログを表示する最初の実行エクスペリエンスを実装するには、このセクションの手順を実行します。
ダイアログの UI を作成する
まず、ダイアログの UI を作成します。
./srcフォルダー内に、settingsという名前の新しいサブフォルダーを作成します。
./src/settings フォルダーで、 という名前のファイルdialog.html作成します。
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://res-1.cdn.office.net/files/fabric-cdn-prod_20230815.002/office-ui-fabric-core/11.0.0/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 からデータを取得する] セクションで作成されます。
変更内容を保存します。
次に、 ./src/settings フォルダーに dialog.css という名前のファイル を作成します。
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; }
変更内容を保存します。
ダイアログの機能を開発する
これでダイアログの UI の定義が完了したので、次に実際に動作するためのコードを記述します。
./src/settings フォルダーで、 という名前のファイルdialog.js作成します。
次のコードを追加します。 このコードでは、イベントを登録するために 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, " ")); } })();
変更内容を保存します。
Webpackの機能設定を更新する
最後に、プロジェクトのルート ディレクトリにある webpack.config.js ファイルを開き、次の手順を実行します。
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", },
config
オブジェクト内でplugins
配列を探します。new CopyWebpackPlugin
オブジェクトのpatterns
配列で、taskpane.css と dialog.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*.json", // The file extension is "xml" if the add-in only manifest is being used. to: "[name]" + "[ext]", transform(content) { if (dev) { return content; } else { return content.toString().replace(new RegExp(urlDev, "g"), urlProd); } }, }, ]}),
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*.json", // The file extension is "xml" if the add-in only manifest is being used. 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 を使用します。
./srcフォルダー内に、helpersという名前の新しいサブフォルダーを作成します。
./src/helpers フォルダーで、 という名前のファイルgist-api.js作成します。
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; }
変更内容を保存します。
次のコマンドを実行してプロジェクトを再構築します。
npm run build
UI のないボタンを実装する
このアドインの [既定の要点の挿入 ] ボタンは、多くのアドイン ボタンと同様に作業ウィンドウを開くのではなく、JavaScript 関数を呼び出す UI なしのボタンです。 ユーザーが [ 既定の gist の挿入 ] ボタンを選択すると、対応する JavaScript 関数によってアドインが構成されているかどうかを確認します。
アドインが既に構成されている場合、関数は、ユーザーが既定として選択した要点の内容を読み込み、メッセージの本文に挿入します。
アドインがまだ構成されていない場合は、必要な情報を入力するようにユーザーに求める設定ダイアログが表示されます。
関数ファイルを更新する (HTML)
UI レス ボタンによって呼び出される関数は、対応するフォーム ファクターのマニフェストの <FunctionFile> 要素によって指定されたファイルで定義する必要があります。 このアドインのマニフェストでは、https://localhost:3000/commands.html
が関数ファイルとして指定されています。
./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 を参照することがわかります。 このファイルは、このチュートリアルの後半の [構成設定を管理するファイルを作成する] セクションで作成されます。
変更内容を保存します。
関数ファイルを更新する (JavaScript)
ファイル./src/commands/commands.jsを開き、内容全体を次のコードに置き換えます。 insertDefaultGist 関数でアドインがまだ構成されていないと判断された場合は、ダイアログ URL に
?warn=1
パラメーターが追加されることに注意してください。 これにより、./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; }
変更内容を保存します。
構成設定を管理するファイルを作成する
./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); }
変更内容を保存します。
Gist を処理する新しい関数を作成する
./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'); }
変更内容を保存します。
[既定の Gist の挿入] ボタンをテストする
ローカル Web サーバーがまだ実行されていない場合は、コマンド プロンプトから
npm start
を実行します。Outlook を開き、新しいメッセージを作成します。
メッセージの作成ウィンドウで、[Insert default gist] ボタンを選択します。 GitHub ユーザー名を設定するためのプロンプトから始めて、アドインを構成できるダイアログが表示されます。
[設定] ダイアログで、GitHub ユーザー名を入力し、 タブ またはダイアログ内の他の場所をクリックして 変更 イベントを呼び出します。これにより、パブリック gists の一覧が読み込まれます。 既定とする Gist を選択し、[Done] を選択します。
もう一度 [Insert default gist] ボタンを選択します。 今度は、Gist のコンテンツが電子メールの本文に挿入されます。
注:
Windows 上の Outlook: 最新の設定を選択するには、[メッセージの作成] ウィンドウを閉じて、もう一度開く必要がある場合があります。
作業ウィンドウを実装する
このアドインの [GIST の挿入] ボタンをクリックすると、作業ウィンドウが開き、ユーザーの gist が表示されます。 ここでユーザーはメッセージの本文に挿入する Gist を選択することができます。 ユーザーがまだアドインを構成していない場合、ダイアログが表示されて構成するように求められます。
作業ペインのHTMLを指定する
作成したプロジェクトでは、作業ペインの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://res-1.cdn.office.net/files/fabric-cdn-prod_20230815.002/office-ui-fabric-core/11.0.0/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>
変更内容を保存します。
作業ペインのCSSを指定する
作成したプロジェクトでは、作業ウィンドウの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; }
変更内容を保存します。
作業ペインのJavaScriptを指定する
作成したプロジェクトでは、作業ペインの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; } })();
変更内容を保存します。
[Insert gist] ボタンをテストする
ローカル Web サーバーがまだ実行されていない場合は、コマンド プロンプトから
npm start
を実行します。Outlook を開き、新しいメッセージを作成します。
メッセージの作成ウィンドウで、[Insert gist] ボタンを選択します。 作成フォームの右側に作業ウィンドウが表示されます。
作業ウィンドウで、[Hello World Html] を選択し、[Insert] を選択してメッセージの本文にその Gist を挿入します。
次のステップ
このチュートリアルでは、コンテンツをメッセージの本文に挿入するためにメッセージ作成モードで使用可能な Outlook アドインを作成しました。 Outlook アドインの開発に関する詳細については、次の記事に進んでください。
コード サンプル
- 完了した Outlook アドイン チュートリアル: このチュートリアルを完了した結果。
関連項目
Office Add-ins