このチュートリアルでは、コンテンツをメッセージの本文に挿入するためにメッセージ作成モードで使用可能な Outlook アドインをビルドする方法について説明します。
このチュートリアルの内容:
- Outlook アドイン プロジェクトを作成する
- メッセージ作成ウィンドウに表示されるボタンを定義する
- 外部サービスから情報を収集してデータを取得する最初の実行エクスペリエンスを実装する
- 関数を呼び出す、UI のないボタンを実装する
- メッセージの本文にコンテンツを挿入する作業ウィンドウを実装する
ヒント
このチュートリアルの完成版については、 GitHub の Office アドイン サンプル リポジトリを参照してください。
前提条件
Node.js (Active 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、Windows (新規またはクラシック)、または Mac の場合。
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 の統合マニフェストを使用すると、Office アドインと Teams アプリを Microsoft 365 用アプリに組み合わせて、開発と展開の 1 つの単位として行うことができます。 詳細については、「 統合されたマニフェストを使用した Office アドイン」を参照してください。 Teams アプリと Outlook アドインを組み合わせた Microsoft 365 用アプリのサンプルについては、「 割引オファー」を参照してください。
統合マニフェストに関するフィードバックをお待ちしております。 提案がある場合は、 Office JavaScript ライブラリのリポジトリで問題を作成してください。
注:
Microsoft 365 の統合マニフェストは、現在 Outlook on Mac ではサポートされていません。 Outlook on Mac を使用している場合は、[ アドインのみのマニフェスト ] タブを選択し、代わりにそれらの指示に従います。 詳細については、「 Microsoft 365 の統合マニフェストを使用したアドインのサポート」を参照してください。
Choose a project type: (プロジェクトの種類を選択) -
Office Add-in Task Pane projectChoose a script type: (スクリプトの種類を選択) -
JavaScriptWhat would you want to name your add-in? (アドインの名前を何にしますか) -
Git the gistWhich Office client application would you like to support?: (どの Office クライアント アプリケーションをサポートしますか) -
Outlookどのマニフェストを使用しますか? -
unified manifest for Microsoft 365
ウィザードを完了すると、ジェネレーターによってプロジェクトが作成されて、サポートしているノード コンポーネントがインストールされます。
プロジェクトのルート ディレクトリに移動します。
cd "Git the gist"VS Code またはお好みのコード エディターでプロジェクトを開きます。
ヒント
Windows では、コマンド ラインからプロジェクトのルート ディレクトリに移動し、
code .を入力して VS Code でそのフォルダーを開くことができます。 Mac では、VS Code でプロジェクト フォルダーを開くためにそのコマンドを使用する前に、codeコマンドをパスに追加する必要があります。
マニフェストを更新する
アドインのマニフェストは、Outlook での表示方法を制御します。 またマニフェストは、アドインをアドイン一覧に表示する方法とリボンに表示するボタンを定義し、アドインによって使用される HTML ファイルと JavaScript ファイルの URL を設定します。
基本的な情報を指定する
アドインに関するいくつかの基本情報を指定するために、マニフェスト ファイルで次の更新を行います。
"description"プロパティを見つけて、既定の"short"値と"full"値をアドインの説明に置き換えて、ファイルを保存します。"description": { "short": "Gets gists.", "full": "Allows users to access their GitHub gists." },ファイルを保存します。
生成されたアドインをテストする
この先に進める前に、ジェネレーターによって生成されたアドインをテストして、プロジェクトが正しく設定されていることを確認します。
注:
Office アドインでは、開発中であっても、HTTP ではなく HTTPS を使用してください。 次のコマンドのいずれかを実行した後に証明書をインストールするように求められた場合は、ヨーマン ジェネレーターが提供する証明書をインストールするように求めるメッセージを受け入れます。 変更を行うには、管理者としてコマンド プロンプトまたはターミナルを実行する必要がある場合もあります。
コンピューターで Office アドインを初めて開発する場合は、コマンド ラインで、Microsoft Edge WebView にループバックの適用除外 (「Microsoft Edge WebView のローカル ホスト ループバックを許可しますか?」)を確認するメッセージが表示されることがあります。 プロンプトが表示されたら、「
Y」と入力して除外を許可します。 除外を有効にするには管理者特権が必要であることに注意してください。 許可されると、今後 Office アドインをサイドロードするときに除外を求められることはありません (コンピューターから除外を削除しない限り)。 詳細については、 Office アドインを読み込むとき、または Fiddler を使用するときに、「ローカル ホストからこのアドインを開くことができません」を参照してください。
初めてヨーマン ジェネレーターを使用して Office アドインを開発すると、既定のブラウザーでウィンドウが開き、Microsoft 365 アカウントへのサインインを求められます。 サインイン ウィンドウが表示されず、サイドローディングまたはログインのタイムアウト エラーが発生した場合は、
atk auth login m365を実行します。
プロジェクトのルート ディレクトリから次のコマンドを実行します。 このコマンドを実行すると、ローカルの Web サーバーが起動し、アドインがサイドロードされます。
npm start注:
初めてヨーマン ジェネレーターを使用して Office アドインを開発すると、既定のブラウザーでウィンドウが開き、Microsoft 365 アカウントへのサインインを求められます。 サインイン ウィンドウが表示されず、サイドローディングまたはログイン タイムアウト エラーが発生した場合は、
npm startを再実行する前にatk auth login m365を実行してください。アドインが自動的にサイドロードされなかった場合は、「 テスト用に Outlook アドインをサイドロード する」の手順に従って、Outlook でアドインを手動でサイドロードします。
Outlookで既存のメッセージを開き、タスクパネルを表示ボタンを選択します。
WebView Stop On Load ダイアログ ボックスでプロンプトが表示されたら、[OK] を選択します。
注:
[キャンセル] を選択すると、このアドインのインスタンスの実行中はダイアログが表示されなくなります。 ただし、アドインを再起動すると、ダイアログはもう一度表示されます。
すべてが正しく設定されている場合は、作業ウィンドウが開き、アドインのウェルカム ページが表示されます。
ローカル Web サーバーを停止してアドインをアンインストールする場合は、該当する手順に従います。
サーバーを停止するには、次のコマンドを実行します。
npm startを使用していた場合は、次のコマンドによりアドインもアンインストールされます。npm stopアドインを手動でサイドロードした場合は、「 サイドロードされたアドインを削除する」を参照してください。
ボタンを定義する
基本のアドインの動作を確認したので、カスタマイズしてより多くの機能を追加できます。 既定のマニフェストでは、メッセージ閲覧ウィンドウ用のボタンのみ定義されています。 マニフェストを更新してメッセージ閲覧ウィンドウからボタンを削除し、メッセージ作成ウィンドウ用の 2 つの新しいボタンを定義してみましょう。
要点リストの表示: 作業ウィンドウを開くボタン
Insert default gist (既定の Gist の挿入): 関数を呼び出すボタン
手順は、使用しているマニフェストによって異なります。
次の手順を実行します。
manifest.json ファイルを開きます。
"extensions.runtimes"配列には、2 つのランタイム オブジェクトがあります。 2 つ目については、"CommandsRuntime"の"id"で、"actions.id"を"insertDefaultGist"に変更します。 これは、後の手順で作成する関数の名前です。 完了すると、ランタイム オブジェクトは次のようになります。{ "id": "CommandsRuntime", "type": "general", "code": { "page": "https://localhost:3000/commands.html" }, "actions": [ { "id": "insertDefaultGist", "type": "executeFunction" } ] }"extensions.ribbons.contexts"配列内の項目を"mailCompose"に変更します。 つまり、ボタンは新しいメッセージまたは返信ウィンドウにのみ表示されます。"contexts": [ "mailCompose" ],"extensions.ribbons.tabs.groups"配列にはグループ オブジェクトが含まれています。 このオブジェクトに次の変更を加えます。-
"id"プロパティを"msgComposeCmdGroup"に変更します。 -
"label"プロパティを "Git the gist" に変更します。
-
その同じグループ オブジェクトには、2 つのコントロール オブジェクトを含む
"controls"配列があります。 それぞれの JSON に変更を加える必要があります。 最初の手順では、次の手順を実行します。-
"id"を ["msgComposeShowGistListTaskPane"] に変更します。 -
"label"を "Display gist list" (要点リストの表示) に変更します。 -
"supertip.title"を "Display gist list" (要点リストの表示) に変更します。 -
"supertip.description"を "Gist の一覧を表示し、その内容を現在のメッセージに挿入できるようにします" に変更します。
-
2 番目のコントロール オブジェクトで、以下の手順を実行します。
-
"id"を ["msgComposeInsertDefaultGist"] に変更します。 -
"label"を "既定の要点を挿入" に変更します。 -
"supertip.title"を "既定の要点を挿入" に変更します。 -
"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": "Display gist list", "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": "Display gist list", "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
アドインを再インストールした後、[ gist リストを表示する ] コマンドと [メッセージ作成ウィンドウに 既定の gist を挿入 する] コマンドを確認することで、アドインが正常にインストールされたことを確認できます。 このアドインのビルドはまだ完了していないため、どちらを選択しても何も起こりません。
従来の Outlook on Windows または Outlook on Mac でこのアドインを実行している場合は、メッセージ作成ウィンドウのリボンに [ gist リストの表示 ] と [既定の gist の挿入] という 2 つの新しいボタンが表示されます。
このアドインを Outlook on the web または新しい Outlook on Windows で実行している場合は、メッセージ作成ウィンドウのリボンから [アプリ] を選択し、[Git the gist] を選択して [Gist リストの表示] と [既定の gist オプションの挿入] を表示します。
ヒント
リボンでアドインを見つける方法のガイダンスについては、「 Outlook でアドインを使う」を参照してください。
最初の実行エクスペリエンスを実装する
このアドインは、GitHub アカウントから gist を読み取り、選択した既定の gist を識別できる必要があります。 これらの目標を達成するために、アドインでは、GitHub ユーザー名を入力し、既存の gist のコレクションから既定の gist を選択するように求められます。 この情報を収集するためのダイアログを表示する最初の実行エクスペリエンスを実装するには、このセクションの手順を完了します。
ダイアログの UI を作成する
まず、ダイアログの UI を作成します。
./srcフォルダー内に、settingsという名前の新しいサブフォルダーを作成します。
./src/settings フォルダに、dialog.htmlという名前のファイルを作成します。
dialog.htmlで、次のマークアップを追加して、GitHub ユーザー名のテキスト入力と、JavaScript 経由で設定される gist の空のリストを含む基本フォームを定義します。
<!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/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.1.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="../helpers/gist-api.js"></script> </body> </html>HTML ファイルが、まだ存在しない JavaScript ファイル gist-api.js を参照することがわかります。 このファイルは、[ GitHub からデータを取得 ] セクションに作成されます。
変更内容を保存します。
次に、 ./src/settings フォルダーに dialog.css という名前のファイルを作成します。
dialog.css で、次のコードを追加して、dialog.htmlで使用されるスタイルを指定します。
body { background-color: #ffffff; } 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という名前のファイルを作成します。
次のコードを追加します。
(function () { "use strict"; // The onReady function must be run each time a new page is loaded. Office.onReady(function () { function initializeDialog() { if (window.location.search) { // Check if warning should be displayed. const warn = getParameterByName("warn"); if (warn) { document.querySelector(".not-configured-warning").style.display = "block"; } else { // See if the config values were passed. // If so, pre-populate the values. const user = getParameterByName("gitHubUserName"); const gistId = getParameterByName("defaultGistId"); document.getElementById("github-user").value = user; loadGists(user, function (success) { if (success) { document.querySelectorAll(".ms-ListItem").forEach(function (item) { item.classList.remove("is-selected"); if (item.value === gistId) { item.classList.add("is-selected"); item.checked = true; } }); document.getElementById("settings-done").disabled = false; } }); } } // When the GitHub username changes, try to load gists. document.getElementById("github-user").addEventListener("change", function () { document.getElementById("gist-list").textContent = ""; const ghUser = document.getElementById("github-user").value; if (ghUser.length > 0) { loadGists(ghUser); } }); // When the Done button is selected, send the values back to the caller as a serialized object. document.getElementById("settings-done").addEventListener("click", function () { const settings = {}; settings.gitHubUserName = document.getElementById("github-user").value; const selectedGist = document.querySelector(".ms-ListItem.is-selected"); if (selectedGist) { settings.defaultGistId = selectedGist.value; sendMessage(JSON.stringify(settings)); } }); } if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", initializeDialog); } else { initializeDialog(); } }); // Load gists using the GitHub API and build the list. function loadGists(user, callback) { getUserGists(user, function (gists, error) { if (error) { document.querySelector(".gist-list-container").style.display = "none"; document.getElementById("error-text").textContent = JSON.stringify(error, null, 2); document.querySelector(".error-display").style.display = "block"; if (callback) callback(false); } else { document.querySelector(".error-display").style.display = "none"; buildGistList(document.getElementById("gist-list"), gists, onGistSelected); document.querySelector(".gist-list-container").style.display = "block"; if (callback) callback(true); } }); } function onGistSelected() { document.querySelectorAll(".ms-ListItem").forEach(function (item) { item.classList.remove("is-selected"); item.checked = false; }); const selectedItem = this.querySelector(".ms-ListItem"); selectedItem.classList.add("is-selected"); selectedItem.checked = true; document.querySelector(".not-configured-warning").style.display = "none"; document.getElementById("settings-done").disabled = false; } function sendMessage(message) { Office.context.ui.messageParent(message); } function getParameterByName(name, url) { return new URL(url || window.location.href).searchParams.get(name); } })();変更内容を保存します。
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*.*", 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*.*", 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 から Gists を取得するには、 GitHub Gists API を使用します。
./srcフォルダー内に、helpersという名前の新しいサブフォルダーを作成します。
./src/helpers フォルダーに、gist-api.jsという名前のファイルを作成します。
gist-api.jsで、次のコードを追加して GitHub から gist を取得し、gist のリストを作成します。
function getUserGists(user, callback) { const requestUrl = "https://api.github.com/users/" + encodeURIComponent(user) + "/gists"; fetchJson(requestUrl, callback); } function buildGistList(parent, gists, clickFunc) { gists.forEach(function (gist) { const listItem = document.createElement("div"); parent.appendChild(listItem); const radioItem = document.createElement("input"); radioItem.classList.add("ms-ListItem", "is-selectable"); radioItem.type = "radio"; radioItem.name = "gists"; radioItem.tabIndex = 0; radioItem.value = gist.id; listItem.appendChild(radioItem); const descPrimary = document.createElement("span"); descPrimary.classList.add("ms-ListItem-primaryText"); descPrimary.textContent = gist.description; listItem.appendChild(descPrimary); const descSecondary = document.createElement("span"); descSecondary.classList.add("ms-ListItem-secondaryText"); descSecondary.textContent = " - " + buildFileList(gist.files); listItem.appendChild(descSecondary); const updated = new Date(gist.updated_at); const descTertiary = document.createElement("span"); descTertiary.classList.add("ms-ListItem-tertiaryText"); descTertiary.textContent = " - Last updated " + updated.toLocaleString(); listItem.appendChild(descTertiary); listItem.addEventListener("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 関数によってアドインが構成されているかどうかがチェックされます。
アドインが既に構成されている場合、関数は既定値として選択した gist の内容を読み込み、それをメッセージの本文に挿入します。
アドインがまだ構成されていない場合は、設定ダイアログで GitHub ユーザー名の入力を求められます。
関数ファイルを更新する (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/hosted/office.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, }, function (result) {} ); } 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 URL("dialog.html?warn=1", window.location.href).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 ファイルを開き、以下の機能を追加します。 次の点に注意してください。
要点に HTML が含まれている場合、アドインは HTML をそのままメッセージの本文に挿入します。
gist に Markdown が含まれている場合、アドインは GitHub Markdown API を使用して Markdown を HTML に変換し、結果の HTML をメッセージの本文に挿入します。
Gist に HTML またはマークダウン以外のものが含まれている場合、アドインはそのコンテンツをコード スニペットとしてメッセージの本文に挿入します。
function getGist(gistId, callback) { const requestUrl = "https://api.github.com/gists/" + encodeURIComponent(gistId); fetchJson(requestUrl, callback); } function fetchJson(url, callback) { fetch(url, { headers: { Accept: "application/vnd.github+json" } }) .then(function (response) { if (!response.ok) { throw new Error("GitHub request failed: " + response.status + " " + response.statusText); } return response.json(); }) .then(function (data) { callback(data); }) .catch(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) { switch (file.language) { case "HTML": // Insert as is. callback(file.content); break; case "Markdown": // Use GitHub's renderer so gist Markdown matches github.com. fetch("https://api.github.com/markdown", { method: "POST", headers: { Accept: "application/vnd.github+json", "Content-Type": "application/json", }, body: JSON.stringify({ text: file.content, mode: "gfm" }), }) .then(function (response) { if (!response.ok) { throw new Error("GitHub Markdown request failed: " + response.status + " " + response.statusText); } return response.text(); }) .then(function (html) { callback(html); }) .catch(function (error) { callback(null, error); }); break; default: // Insert contents as a <code> block. const codeElement = document.createElement("code"); codeElement.textContent = file.content; const preElement = document.createElement("pre"); preElement.appendChild(codeElement); callback(preElement.outerHTML); } return; } } } callback(null, "No suitable file found in the gist"); }変更内容を保存します。
[既定の Gist の挿入] ボタンをテストする
ローカルの Web サーバーがまだ実行されていない場合は、コマンド プロンプトから
npm startを実行します。Outlook を開き、新しいメッセージを作成します。
メッセージの作成ウィンドウで、[Insert default gist] ボタンを選択します。 GitHub ユーザー名の設定を求めるダイアログが表示されます。
設定ダイアログで、GitHub ユーザー名を入力し、 Tab キーを押すかダイアログ内の別の場所をクリックして、 変更 イベントを呼び出します。 このアクションにより、パブリック Gist のリストが読み込まれます。 既定とする要点を選択し、[ 完了] を選択します。
もう一度 [Insert default gist] ボタンを選択します。 今回は、既定の gist の内容がメッセージの本文に挿入されます。
注:
Windows のクラシック Outlook: 最新の設定を取得するには、メッセージ作成ウィンドウを閉じて再度開く必要がある場合があります。
作業ウィンドウを実装する
このアドインの [要点リストの表示 ] ボタンをクリックすると、作業ウィンドウが開き、目的の要点が表示されます。 その後、メッセージの本文に挿入する gist の 1 つを選択できます。 アドインをまだ構成していない場合は、その操作を求めるメッセージが表示されます。
作業ペインの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/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.1.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="../helpers/addin-config.js"></script> <script type="text/javascript" src="../helpers/gist-api.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; background-color: #ffffff; } 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 () { function initializeTaskPane() { 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. document.getElementById("not-configured").style.display = ""; } // When insert button is selected, build the content and insert into the body. document.getElementById("insert-button").addEventListener("click", function () { const selectedGist = document.querySelector(".ms-ListItem.is-selected"); const gistId = selectedGist && selectedGist.value; 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. document.getElementById("settings-icon").addEventListener("click", function () { // Display settings dialog. const url = new URL("dialog.html", window.location.href); if (config) { // If the add-in has already been configured, pass the existing values to the dialog. url.searchParams.set("gitHubUserName", config.gitHubUserName); url.searchParams.set("defaultGistId", config.defaultGistId); } const dialogOptions = { width: 20, height: 40, displayInIframe: true }; Office.context.ui.displayDialogAsync(url.toString(), dialogOptions, function (result) { settingsDialog = result.value; settingsDialog.addEventHandler(Office.EventType.DialogMessageReceived, receiveMessage); settingsDialog.addEventHandler(Office.EventType.DialogEventReceived, dialogClosed); }); }); } if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", initializeTaskPane); } else { initializeTaskPane(); } }); function loadGists(user) { document.getElementById("error-display").style.display = "none"; document.getElementById("not-configured").style.display = "none"; document.getElementById("gist-list-container").style.display = ""; getUserGists(user, function (gists, error) { if (error) { } else { const gistList = document.getElementById("gist-list"); gistList.textContent = ""; buildGistList(gistList, gists, onGistSelected); } }); } function onGistSelected() { document.getElementById("insert-button").disabled = false; document.querySelectorAll(".ms-ListItem").forEach(function (item) { item.classList.remove("is-selected"); item.checked = false; }); const selectedItem = this.querySelector(".ms-ListItem"); selectedItem.classList.add("is-selected"); selectedItem.checked = true; } function showError(error) { document.getElementById("not-configured").style.display = "none"; document.getElementById("gist-list-container").style.display = "none"; const errorDisplay = document.getElementById("error-display"); errorDisplay.textContent = error; errorDisplay.style.display = ""; } function receiveMessage(message) { config = JSON.parse(message.message); setConfig(config, function (result) { settingsDialog.close(); settingsDialog = null; loadGists(config.gitHubUserName); }); } function dialogClosed(message) { settingsDialog = null; } })();変更内容を保存します。
[Display gist list] ボタンをテストする
ローカルの Web サーバーがまだ実行されていない場合は、コマンド プロンプトから
npm startを実行します。Outlook を開き、新しいメッセージを作成します。
メッセージ作成ウィンドウで、[ 要点リストの表示 ] ボタンを選択します。 作業ウィンドウが開きます。
作業ウィンドウで、[Hello World Html] を選択し、[Insert] を選択してメッセージの本文にその Gist を挿入します。
次のステップ
このチュートリアルでは、コンテンツをメッセージの本文に挿入するためにメッセージ作成モードで使用可能な Outlook アドインを作成しました。 Outlook アドインの開発に関する詳細については、次の記事に進んでください。
コード サンプル
- 完成した Outlook アドイン チュートリアル: このチュートリアルを完了した結果。
関連項目
Office Add-ins