Руководство. Сборка надстройки Outlook для создания сообщения

В этом руководстве разъясняется, как выполнить сборку надстройки Outlook, которую можно использовать в режиме создания сообщения для вставки содержимого в его текст.

В этом руководстве описан порядок выполнения перечисленных ниже задач.

  • Создание проекта надстройки Outlook
  • Определение кнопок, отображаемых в окне создания сообщения
  • Реализация интерфейса первого запуска, который собирает сведения от пользователя и получает данные из внешней службы
  • Реализация кнопки без пользовательского интерфейса, вызывающей функцию
  • Реализация области задач, вставляющей содержимое в текст сообщения

Совет

Если вам нужна полная версия этого руководства (с использованием манифеста только надстройки), перейдите в репозиторий примеров надстроек Office на сайте GitHub.

Предварительные условия

  • Visual Studio Code (VS Code) или предпочитаемый редактор кода.

  • Outlook в Интернете, новый Outlook в Windows или Outlook 2016 или более поздней версии в Windows (подключен к учетной записи Microsoft 365).

  • Учетная запись GitHub .

Настройка

Надстройка, создаваемая с помощью этого руководства, считывает элементы gist из учетной записи GitHub пользователя и добавляет выбранные элементы gist в текст сообщения. Выполните указанные ниже действия для создания двух новых элементов gist, с помощью которых можно проверить создаваемую надстройку.

  1. Выполните вход в GitHub.

  2. Создайте новый элемент gist.

    • В поле Gist description... (Описание gist) введите Hello World Markdown.

    • В поле Filename including extension... (Имя файла с расширением) введите test.md.

    • Добавьте в многострочное текстовое поле указанную ниже разметку.

      # Hello World
      
      This is content converted from Markdown!
      
      Here's a JSON sample:
      
        ```json
        {
          "foo": "bar"
        }
        ```
      
    • Нажмите кнопку Create public gist (Создать общедоступный элемент gist).

  3. Создайте другой элемент gist.

    • В поле Gist description... (Описание gist) введите Hello World Html.

    • В поле Filename including extension... (Имя файла с расширением) введите test.html.

    • Добавьте в многострочное текстовое поле указанную ниже разметку.

      <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 (Создать общедоступный элемент gist).

Создание проекта надстройки Outlook

  1. Выполните следующую команду, чтобы создать проект надстройки с помощью генератора Yeoman. Папка, содержащая проект, будет добавлена в текущий каталог.

    yo office
    

    Примечание.

    При выполнении команды yo office может появиться запрос о политиках сбора данных генератора Yeoman и средств CLI надстройки Office. Используйте предоставленные сведения, чтобы ответить на запросы подходящим образом.

    При появлении запроса предоставьте следующую информацию для создания проекта надстройки.

  2. Действия по созданию проекта немного различаются в зависимости от типа манифеста.

    Примечание.

    Единый манифест для Microsoft 365 позволяет объединить надстройку Outlook с приложением Teams в качестве единого блока разработки и развертывания. Мы работаем над расширением поддержки унифицированного манифеста для Excel, PowerPoint, Word, пользовательской разработки Copilot и других расширений Microsoft 365. Дополнительные сведения об этом см. в статье Надстройки Office с унифицированным манифестом. Пример объединенного приложения Teams и надстройки Outlook см. в разделе Предложения скидок.

    Мы любим получать ваши отзывы о едином манифесте. Если у вас есть какие-либо предложения, создайте проблему в репозитории библиотеки JavaScript для Office.

    • Выберите тип проекта - Office Add-in Task Pane project

    • Выберите тип сценария - JavaScript

    • Как вы хотите назвать свою надстройку? - Git the gist

    • Какое клиентское приложение Office должно поддерживаться? - Outlook

    • Какой манифест вы хотите использовать? - unified manifest for Microsoft 365

      Запросы и ответы для генератора Yeoman с выбранными параметрами унифицированного манифеста и JavaScript.

    После завершения работы мастера генератор создаст проект и установит вспомогательные компоненты Node.

  3. Перейдите к корневому каталогу проекта.

    cd "Git the gist"
    
  4. Эта надстройка использует следующие библиотеки.

    • Библиотека Showdown для преобразования Markdown в HTML.
    • Библиотека URI.js для создания относительных URL-адресов.
    • Библиотека jQuery для упрощения взаимодействия с DOM.

    Чтобы установить эти инструменты для своего проекта, выполните в корневом каталоге проекта указанную ниже команду.

    npm install showdown urijs jquery --save
    
  5. Откройте проект в VS Code или используемом вами редакторе кода.

    Совет

    В Windows вы можете перейти в корневой каталог проекта с помощью командной строки и ввести code ., чтобы открыть эту папку в VS Code. На компьютере Mac потребуется добавить в путь команду code перед использованием этой команды для открытия папки проекта в VS Code.

Обновление манифеста

Манифест надстройки управляет ее отображением в Outlook. Он определяет, как надстройка отображается в списке, а также задает кнопки на ленте и URL-адреса файлов HTML и JavaScript, используемых надстройкой.

Указание основных сведений

Внесите следующие обновления в файл манифеста, чтобы указать некоторые основные сведения о надстройке.

  1. Найдите свойство description, замените значения по умолчанию short и long описаниями надстройки и сохраните файл.

    "description": {
        "short": "Gets gists.",
        "full": "Allows users to access their GitHub gists."
    },
    
  2. Сохраните файл.

Тестирование созданной надстройки

Прежде чем продолжить, протестируйте базовую надстройку, созданную генератором, чтобы подтвердить правильную настройку проекта.

Примечание.

  • Надстройки Office должны использовать HTTPS, а не HTTP, даже во время разработки. Если после выполнения одной из следующих команд вам будет предложено установить сертификат, примите запрос на установку сертификата, который предоставляет генератор Yeoman. Кроме того, вам может потребоваться запустить командную строку или терминал с правами администратора, чтобы внести изменения.

  • Если вы впервые разрабатываете надстройку Office на компьютере, в командной строке может появиться запрос на предоставление Microsoft Edge WebView исключения замыкания на себя ("Разрешить замыкания на себя локальный узел для Microsoft Edge WebView?"). При появлении запроса введите Y , чтобы разрешить исключение. Обратите внимание, что вам потребуются права администратора, чтобы разрешить исключение. После этого вам не следует запрашивать исключение при загрузке неопубликованных надстроек Office в будущем (если вы не удалите исключение с компьютера). Дополнительные сведения см. в разделе "Не удается открыть эту надстройку из localhost" при загрузке надстройки Office или с помощью Fiddler.

    Запрос в командной строке, чтобы разрешить Microsoft Edge WebView исключения замыкания на себя.

  1. Выполните следующую команду в корневом каталоге своего проекта. При выполнении этой команды запускается локальный веб-сервер, и надстройка загружается неопубликованно.

    npm start
    

    Примечание.

    Если надстройка не была автоматически загружена неопубликованным приложением, следуйте инструкциям в разделе Загрузка неопубликованных надстроек Outlook для тестирования, чтобы вручную загрузить надстройку неопубликованного приложения в Outlook.

  2. Откройте в Outlook существующее сообщение и нажмите кнопку Показать область задач.

  3. При появлении запроса с диалоговым окном Остановка при загрузке веб-представления выберите ОК.

    Примечание.

    После нажатия кнопки Отменадиалоговое окно не будет отображаться в процессе работы с этим экземпляром надстройки. Однако при перезапуске надстройки диалоговое окно снова появится.

    Если все настроено правильно, откроется область задач и отобразится страница приветствия надстройки.

    Кнопка

  4. Если вы хотите остановить локальный веб-сервер и удалить надстройку, следуйте применимым инструкциям:

    • Чтобы остановить сервер, выполните следующую команду. Если вы использовали npm start, следующая команда также должна удалить надстройку.

      npm stop
      
    • Если вы вручную загрузили неопубликованную надстройку, см. статью Удаление неопубликоченной надстройки.

Определение кнопок

После проверки работы базовой надстройки можно ее настроить для добавления функций. По умолчанию манифест определяет только кнопки для окна чтения сообщения. Можно обновить манифест, чтобы удалить кнопки из окна чтения сообщения и определить две новые кнопки для окна создания сообщения:

  • Insert gist (Вставить gist): кнопка, открывающая область задач

  • Insert default gist (Вставить gist по умолчанию): кнопка, вызывающая функцию

Процедура зависит от используемого манифеста.

Выполните следующие действия.

  1. Откройте файл manifest.json .

  2. В массиве extensions.runtimes есть два объекта среды выполнения. Для второго параметра с идентификатором 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"
            }
        ]
    }
    
  3. Измените элемент в массиве extensions.ribbons.contexts на mailCompose. Это означает, что кнопки будут отображаться только в новом сообщении или окне ответа.

    "contexts": [
        "mailCompose"
    ],
    
  4. Массив extensions.ribbons.tabs.groups содержит объект group. Внесите следующие изменения в этот объект.

    1. Измените свойство id на msgComposeCmdGroup.
    2. Измените свойство label на Git the gist.
  5. Этот же объект группы содержит массив элементов управления с двумя объектами управления. Необходимо внести изменения в JSON для каждого из них. В первом из них выполните следующие действия.

    1. Измените идентификатор на msgComposeInsertGist.
    2. Измените метку на "Вставка gist".
    3. Измените "supertip.title" на "Insert gist".
    4. Измените "supertip.description" на "Отображает список объектов gists и позволяет вставлять их содержимое в текущее сообщение".
  6. Во втором объекте элемента управления выполните следующие действия.

    1. Измените идентификатор на msgComposeInsertDefaultGist.
    2. Измените метку на "Вставить gist по умолчанию".
    3. Измените "supertip.title" на "Вставить gist по умолчанию".
    4. Измените значение "supertip.description" на "Вставляет содержимое gist, которое вы помечаете как по умолчанию, в текущее сообщение".
    5. Измените значение actionId на insertDefaultGist. Это соответствует action.id "CommandsRuntime", заданного на предыдущем шаге.

    По завершении свойство 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"
                                }
                            ]
                        }
                    ]
                }
            ]
        }
    ]
    
  7. Сохраните изменения манифеста.

Переустановка надстройки

Чтобы изменения манифеста вступили в силу, переустановите надстройку.

  1. Если веб-сервер работает, выполните следующую команду.

    npm stop
    
  2. Выполните указанные ниже команды, чтобы запустить локальный веб-сервер и автоматически загрузить неопубликованную надстройку.

    npm start
    

После повторной установки надстройки можно убедиться, что она установлена успешно, проверив команды Insert gist и Insert default gist в окне составления сообщений. Обратите внимание, что при выборе этих двух элементов ничего не происходит, так как вы еще не закончили создание этой надстройки.

  • Если вы используете эту надстройку в Outlook 2016 или более поздней версии в Windows, на ленте окна создания сообщения должны появиться две новые кнопки: Вставка gist и Вставка gist по умолчанию.

    Меню переполнения ленты в классическом Outlook в Windows с выделенными кнопками надстройки.

  • Если вы используете эту надстройку в Outlook в Интернете или outlook в Windows, выберите Приложения на ленте окна создания сообщения, а затем выберите Git the gist, чтобы просмотреть параметры Вставка gist и Вставка gist по умолчанию.

    Форма создания сообщения в Outlook в Интернете с выделенной кнопкой надстройки и всплывающим меню.

Реализация интерфейса первого запуска

Эта надстройка должна иметь возможность считывать элементы gist из учетной записи GitHub пользователя и определять, какой из них пользователь выбрал в качестве используемого по умолчанию. Для выполнения этих целей надстройка должна предложить пользователю указать его имя пользователя GitHub и выбрать элемент gist в качестве используемого по умолчанию из его коллекции существующих элементов gist. Выполните действия, описанные в этом разделе, чтобы реализовать интерфейс первого запуска, в котором отображается диалоговое окно для сбора этих сведений от пользователя.

Создание пользовательского интерфейса диалогового окна

Начнем с создания пользовательского интерфейса для диалогового окна.

  1. Создайте в папке ./src новую подпапку с именем settings.

  2. В папке ./src/settings создайте файл с именем dialog.html.

  3. В dialog.htmlдобавьте следующую разметку, чтобы определить базовую форму с текстовым вводом для имени пользователя GitHub и пустым списком gists, который будет заполнен с помощью JavaScript.

    <!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 ниже.

  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. Сохраните изменения.

Разработка функциональных возможностей диалогового окна

Теперь, после определения пользовательского интерфейса диалогового окна, можно написать код для выполнения в нем действий.

  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. Найдите объект entry в объекте config и добавьте новую запись для 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. Найдите массив plugins в объекте config. В массив patterns объекта new CopyWebpackPlugin добавьте новые записи для 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);
          }
        },
      },
    ]}),
    
  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*.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 определяет, что надстройка должна загружать элементы gist, если возникает событие change для поля имени пользователя GitHub. Для получения элементов gist пользователя из GitHub используется API элементов gist GitHub.

  1. Создайте в папке ./src новую подпапку с именем helpers.

  2. В папке ./src/helpers создайте файл с именем gist-api.js.

  3. В gist-api.jsдобавьте следующий код для получения элементов gists пользователя из GitHub и создания списка 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
    

Реализация кнопки без пользовательского интерфейса

Кнопка "Вставить gist" этой надстройки по умолчанию — это кнопка без пользовательского интерфейса, которая вызывает функцию JavaScript, а не открывает область задач, как это делают многие кнопки надстройки. Когда пользователь нажимает кнопку Вставить gist по умолчанию , соответствующая функция JavaScript проверяет, настроена ли надстройка.

  • Если надстройка уже настроена, функция загружает содержимое gist, выбранного пользователем в качестве значения по умолчанию, и вставляет его в текст сообщения.

  • Если надстройка еще не настроена, в диалоговом окне параметров пользователю будет предложено указать необходимые сведения.

Обновление файла функции (HTML)

Функция, вызываемая кнопкой без пользовательского интерфейса, должна быть определена в файле, указанном элементом <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, надстройка использует библиотеку Showdown для преобразования Markdown в HTML, а затем вставляет полученный HTML-код в текст сообщения.

    • Если элемент gist содержит любой код, отличный от HTML или Markdown, надстройка вставит его в текст сообщения как фрагмент кода.

    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. Сохраните изменения.

Тестирование кнопки Insert default gist (Вставить gist по умолчанию)

  1. Если локальный веб-сервер еще не запущен, выполните команду npm start из командной строки.

  2. Откройте Outlook и создайте новое сообщение.

  3. В окне создания сообщения нажмите кнопку Insert default gist (Вставить gist по умолчанию). Вы увидите диалоговое окно, в котором можно настроить надстройку, указав имя пользователя GitHub в диалоговом окне с соответствующим приглашением.

    Диалоговое окно с предложением настроить надстройку.

  4. В диалоговом окне параметров введите имя пользователя GitHub, а затем нажмите клавишу TAB или щелкните в другом месте диалогового окна, чтобы вызвать событие изменения , которое должно загрузить список общедоступных элементов gist. Выберите элемент gist в качестве используемого по умолчанию и нажмите кнопку Done (Готово).

    Диалоговое окно параметров надстройки.

  5. Снова нажмите кнопку Insert default gist (Вставить gist по умолчанию). На этот раз содержимое элемента gist должно быть вставлено в текст сообщения.

    Примечание.

    Outlook для Windows: чтобы применить последние параметры, может потребоваться закрытие и повторное открытие окна создания сообщения.

Реализация области задач

Кнопка "Вставить 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://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>
    
  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 (Вставить gist)

  1. Если локальный веб-сервер еще не запущен, выполните команду npm start из командной строки.

  2. Откройте Outlook и создайте новое сообщение.

  3. В окне создания сообщения нажмите кнопку Insert gist (Вставить gist). Справа от формы создания сообщения должна открыться область задач.

  4. В области задач выберите элемент gist Hello World Html и нажмите кнопку Insert (Вставить) для вставки этого элемента gist в текст сообщения.

Область задач надстройки и выделенное содержимое элемента gist, отображаемое в тексте сообщения.

Дальнейшие действия

С помощью этого руководства вы выполнили сборку надстройки Outlook, которую можно использовать в режиме создания сообщения для вставки содержимого в его текст. Чтобы узнать больше о разработке надстроек Outlook, перейдите к следующей статье.

Примеры кода

См. также