Tutorial: criar uma mensagem para compor o suplemento do Outlook

Este tutorial ensina como criar um suplemento que pode ser usado em mensagens no modo de redação do Outlook para inserir conteúdo no corpo de uma mensagem.

Neste tutorial, você vai:

  • Criar um projeto de um suplemento do Outlook
  • Definir botões que aparecem na janela de composição de mensagens
  • Implementar uma experiência de primeira execução que coleta informações do usuário e busca os dados de um serviço externo
  • Implementar um botão sem interface do usuário que chame uma função
  • Implementar um painel de tarefas que insere o conteúdo no corpo de uma mensagem

Dica

Se quiser uma versão completa deste tutorial (com o manifesto apenas de suplemento), aceda ao repositório de exemplos de Suplementos do Office no GitHub.

Pré-requisitos

  • Node.js (a versão mais recente de LTS). Visite o siteNode.js para transferir e instalar a versão certa para o seu sistema operativo.

  • A versão mais recente do Yeoman e do Yeoman gerador de Suplementos do Office. Para instalar essas ferramentas globalmente, execute o seguinte comando por meio do prompt de comando.

    npm install -g yo generator-office
    

    Observação

    Mesmo se você já instalou o gerador Yeoman, recomendamos atualizar seu pacote para a versão mais recente do npm.

  • Office conectado a uma assinatura Microsoft 365 (incluindo o Office na web).

    Observação

    Se ainda não tiver o Office, poderá qualificar-se para uma subscrição de programador Microsoft 365 E5 através do Programa para Programadores do Microsoft 365. Para obter detalhes, consulte as FAQ. Em alternativa, pode inscrever-se numa avaliação gratuita de um mês ou comprar um plano do Microsoft 365.

Configurar

O suplemento que você criará neste tutorial lerá gists da conta do GitHub do usuário e adicionará a essência selecionada ao corpo de uma mensagem. Conclua as etapas a seguir para criar duas gists novas que você pode usar para testar o suplemento que você vai criar.

  1. Faça logon no GitHub.

  2. Crie uma nova gist.

    • No campodescrição do gist... , insira a Markdown Olá Mundo.

    • No campo nome do arquivo como extensão... campo, insira test.md.

    • Adicione a seguinte marcação para a caixa de texto de várias linhas.

      # Hello World
      
      This is content converted from Markdown!
      
      Here's a JSON sample:
      
        ```json
        {
          "foo": "bar"
        }
        ```
      
    • Selecione o botão criar gist público.

  3. Criar outro novo gist.

    • No campodescrição do gist... , insira Olá Mundo.

    • No campo nome do arquivo como extensão... campo, insira test.html.

    • Adicione a seguinte marcação para a caixa de texto de várias linhas.

      <html>
        <head>
          <style>
          h1 {
            font-family: Calibri;
          }
          </style>
        </head>
        <body>
          <h1>Hello World!</h1>
          <p>This is a test</p>
        </body>
      </html>
      
    • Selecione o botão criar gist público.

Criar um projeto de um suplemento do Outlook

  1. Execute o comando a seguir para criar um projeto de suplemento usando o gerador Yeoman. Será adicionada uma pasta que contém o projeto ao diretório atual.

    yo office
    

    Observação

    Ao executar o comando yo office, você receberá informações sobre as políticas de coleta de dados de Yeoman e as ferramentas da CLI do suplemento do Office. Use as informações fornecidas para responder às solicitações como achar melhor.

    Quando solicitado, forneça as informações a seguir para criar seu projeto de suplemento.

  2. Os passos para criar o projeto variam ligeiramente consoante o tipo de manifesto.

    Observação

    O manifesto unificado do Microsoft 365 permite-lhe combinar um Suplemento do Outlook com uma aplicação do Teams como uma única unidade de desenvolvimento e implementação. Estamos a trabalhar para expandir o suporte para o manifesto unificado para o Excel, PowerPoint, Word, desenvolvimento copilot personalizado e outras extensões do Microsoft 365. Para obter mais informações, consulte Suplementos do Office com o manifesto unificado. Para obter um exemplo de uma aplicação combinada do Teams e suplemento do Outlook, consulte Ofertas de Desconto.

    Adoramos receber o seu feedback sobre o manifesto unificado. Se tiver alguma sugestão, crie um problema no repositório da Biblioteca JavaScript do Office.

    • Escolha o tipo de projeto - Office Add-in Task Pane project

    • Escolha o tipo de script - JavaScript

    • Qual será o nome do suplemento? - Git the gist

    • Você gostaria de proporcionar suporte para qual aplicativo cliente do Office? - Outlook

    • Que manifesto gostaria de utilizar? - unified manifest for Microsoft 365

      Os pedidos e respostas para o gerador Yeoman com o manifesto unificado e as opções javaScript escolhidas.

    Depois que você concluir o assistente, o gerador criará o projeto e instalará os componentes Node de suporte.

  3. Navegue até o diretório raiz do projeto.

    cd "Git the gist"
    
  4. Este suplemento utiliza as seguintes bibliotecas.

    • Biblioteca Showdown para converter Markdown em HTML.
    • Biblioteca URI.js para criar URLs relativos.
    • biblioteca jQuery para simplificar as interações do DOM.

    Para instalar essas ferramentas para o seu projeto, execute o seguinte comando no diretório raiz do projeto.

    npm install showdown urijs jquery --save
    
  5. Abra o projeto no VS Code ou no seu editor de código preferido.

    Dica

    No Windows, navegue até o diretório raiz do projeto por meio da linha de comando e, em seguida, insira code . para abrir essa pasta no VS Code. No Mac, você precisará adicionar o comando code ao caminho antes de poder usá-lo para abrir a pasta do projeto no VS Code.

Atualizar o manifesto

O manifesto controla como o suplemento é exibido no Outlook. Ele define a maneira como o suplemento aparece na lista de suplementos e os botões que aparecem na faixa de opções, além de definir as URLs para os arquivos HTML e JavaScript usados pelo suplemento.

Especifique as informações básicas

Faça as seguintes atualizações no ficheiro de manifesto para especificar algumas informações básicas sobre o suplemento.

  1. Localize a propriedade "description", substitua os valores predefinidos "curto" e "longo" por descrições do suplemento e guarde o ficheiro.

    "description": {
        "short": "Gets gists.",
        "full": "Allows users to access their GitHub gists."
    },
    
  2. Salve o arquivo.

Testar o suplemento gerado

Antes de prosseguir, vamos testar o suplemento básico que criou o gerador para confirmar que o projeto está configurado corretamente.

Observação

  • Os Suplementos do Office devem utilizar HTTPS e não HTTP, mesmo quando estiver a desenvolver. Se lhe for pedido para instalar um certificado depois de executar um dos seguintes comandos, aceite o pedido para instalar o certificado que o gerador Yeoman fornece. Você também pode executar o prompt de comando ou terminal como administrador para que as alterações sejam feitas.

  • Se esta for a primeira vez que desenvolve um Suplemento do Office no seu computador, poderá ser-lhe pedido na linha de comandos para conceder ao Microsoft Edge WebView uma isenção de loopback ("Permitir loopback localhost para o Microsoft Edge WebView?"). Quando lhe for pedido, introduza Y para permitir a isenção. Tenha em atenção que precisará de privilégios de administrador para permitir a isenção. Uma vez permitido, não lhe deverá ser pedida uma isenção quando carregar os Suplementos do Office no futuro (a menos que remova a isenção do seu computador). Para saber mais, consulte "Não é possível abrir este suplemento a partir do localhost" ao carregar um Suplemento do Office ou ao utilizar o Fiddler.

    A linha de comandos na linha de comandos para permitir ao Microsoft Edge WebView uma isenção de loopback.

  1. Execute o seguinte comando no diretório raiz do seu projeto. Quando executa este comando, o servidor Web local é iniciado e o suplemento é sideloaded.

    npm start
    

    Observação

    Se o suplemento não tiver sido carregado automaticamente em sideload, siga as instruções em Sideload Suplementos do Outlook para testar para fazer sideload manualmente do suplemento no Outlook.

  2. No Outlook, abra uma mensagem existente e selecione o botão Mostrar Painel de Tarefas.

  3. Quando solicitado com a caixa de diálogo Parar na Carga do Modo de Exibição da Web, selecione OK.

    Observação

    Se você selecionar Cancelar, a caixa de diálogo não será mostrada novamente enquanto esta instância do suplemento estiver em execução. No entanto, se você reiniciar o suplemento, você verá a caixa de diálogo novamente.

    Se tudo tiver sido configurado corretamente, o painel de tarefas é aberto e compõe a página de boas-vindas do suplemento.

    O botão Mostrar painel de tarefas e o painel de tarefas gist do Git adicionados pela amostra.

  4. Quando quiser parar o servidor Web local e desinstalar o suplemento, siga as instruções aplicáveis:

    • Para parar o servidor, execute o seguinte comando. Se tiver utilizado npm start, o seguinte comando também deve desinstalar o suplemento.

      npm stop
      
    • Se tiver carregado manualmente o suplemento em sideload, consulte Remover um suplemento sideloaded.

Definir botões

Agora que você verificou que o complemento básico funciona, você pode personalizá-lo para adicionar mais funcionalidades. Por padrão, o manifesto define apenas os botões para a janela de mensagem de leitura. Vamos atualizar o manifesto para remover os botões na janela de mensagem de leitura e definir dois novos botões para a janela de mensagem de texto:

  • Inserir gist: um botão que abre um painel de tarefas

  • Inserir gist padrão: um botão que invoca uma função

O procedimento depende do manifesto que está a utilizar.

Siga os seguintes passos:

  1. Abra o ficheiro manifest.json .

  2. Na matriz "extensions.runtimes", existem dois objetos de runtime. Para o segundo, com o "id" de "CommandsRuntime", altere o "actions.id" para "insertDefaultGist". Este é o nome de uma função que cria num passo posterior. Quando terminar, o objeto runtime deverá ter o seguinte aspeto:

    {
        "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. Altere o item na matriz "extensions.ribbons.contexts" para "mailCompose". Isto significa que os botões só serão apresentados numa nova mensagem ou janela de resposta.

    "contexts": [
        "mailCompose"
    ],
    
  4. A matriz "extensions.ribbons.tabs.groups" tem um objeto de grupo. Faça as seguintes alterações a este objeto.

    1. Altere a propriedade "id" para "msgComposeCmdGroup".
    2. Altere a propriedade "label" para "Git the gist".
  5. Esse mesmo objeto de grupo tem uma matriz de "controlos" com dois objetos de controlo. Temos de fazer alterações ao JSON para cada um deles. Na primeira, siga estes passos.

    1. Altere o "id" para "msgComposeInsertGist".
    2. Altere a "etiqueta" para "Insert gist".
    3. Altere "supertip.title" para "Insert gist".
    4. Altere a "supertip.description" para "Apresenta uma lista dos seus gists e permite-lhe inserir os respetivos conteúdos na mensagem atual".
  6. No segundo objeto de controlo, siga estes passos.

    1. Altere o "id" para "msgComposeInsertDefaultGist".
    2. Altere a "etiqueta" para "Inserir gist predefinido".
    3. Altere "supertip.title" para "Insert default gist".
    4. Altere a "supertip.description" para "Insere o conteúdo da imagem que marca como predefinição na mensagem atual".
    5. Altere o "actionId" para "insertDefaultGist". Isto corresponde ao "action.id" do "CommandsRuntime" que definiu num passo anterior.

    Quando terminar, a propriedade "frisos" deverá ter o seguinte aspeto:

    "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. Salve suas alterações no manifesto.

Reinstalar o suplemento

Você deve reinstalar o complemento para que as alterações de manifesto entrem em vigor.

  1. Se o servidor Web estiver em execução, execute o seguinte comando.

    npm stop
    
  2. Execute o comando a seguir para iniciar o servidor Web local e realizar o sideload automático do suplemento.

    npm start
    

Depois de reinstalar o suplemento, você pode verificar se ele foi instalado com êxito verificando os comandos Inserir gist e Inserir gist padrão na janela de composição de mensagem. Observe que nada acontece quando você escolhe um destes itens, porque você ainda não terminou de criar este suplemento.

  • Se estiver a executar este suplemento no Outlook 2016 ou posterior no Windows, deverá ver dois novos botões no friso da janela de composição de mensagens: Inserir gist e Inserir gist predefinido.

    O menu do friso no Outlook clássico no Windows com os botões do suplemento realçados.

  • Se estiver a executar este suplemento no Outlook na Web ou no novo Outlook no Windows, selecione Aplicações no friso da janela de composição de mensagens e, em seguida, selecione Git o ícone para ver as opções inserir gist e Inserir gist predefinidas.

    O formulário de redação de mensagem no Outlook na Web com o botão do suplemento e o menu pop-up realçados.

Implementando uma experiência de primeira execução

Este suplemento precisa ser capaz de ler gists da conta do GitHub do usuário e identificar qual deles o usuário escolheu como a essência padrão. Para obter esses objetivos, o suplemento deverá solicitar ao usuário para fornecer o nome de usuário do GitHub e escolher uma essência padrão do seu conjunto de gists existentes. Conclua os passos nesta secção para implementar uma experiência de primeira execução que apresenta uma caixa de diálogo para recolher estas informações do utilizador.

Criar a IU da caixa de diálogo

Vamos começar por criar a IU para a caixa de diálogo.

  1. Dentro da pasta ./src, crie uma nova subpasta chamada configurações.

  2. Na pasta ./src/settings , crie um ficheiro com o nome dialog.html.

  3. No dialog.html, adicione a seguinte marcação para definir um formulário básico com uma entrada de texto para um nome de utilizador do GitHub e uma lista vazia para os gists que serão preenchidos através de 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>
    

    Você deve ter notado que o arquivo HTML faz referência a um arquivo JavaScript, gist-api.js, que ainda não existe. Esse arquivo será criado na seção abaixo Buscar dados do GitHub.

  4. Salve suas alterações.

  5. Em seguida, crie um ficheiro na pasta ./src/settings com o nome dialog.css.

  6. No dialog.css, adicione o seguinte código para especificar os estilos utilizados pelo 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. Salve suas alterações.

Desenvolver a funcionalidade da caixa de diálogo

Agora que você definiu a IU da caixa de diálogo, você pode escrever código que realmente faz alguma coisa.

  1. Na pasta ./src/settings , crie um ficheiro com o nome dialog.js.

  2. Adicione o código a seguir. Observe que esse código usa jQuery para registrar eventos e usa o método messageParent para enviar as opções do usuário de volta ao chamador.

    (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. Salve suas alterações.

Atualizar as configurações webpack config

Por fim, abra o arquivo webpack.config.js encontrado no diretório raiz do projeto e conclua as etapas a seguir.

  1. Localize o objeto entry dentro do objeto config e adicione uma nova entrada para dialog.

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

    Após fazer isso, o novo objeto entry ficará assim:

    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. Localize a plugins matriz dentro do config objeto. Na matriz patterns do objeto new CopyWebpackPlugin, adicione novas entradas para taskpane.css e dialog.css.

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

    Depois de o fazer, o new CopyWebpackPlugin objeto terá o seguinte aspeto. Tenha em atenção a ligeira diferença se o suplemento utilizar o manifesto apenas do suplemento.

    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. Na mesma matriz plugins do objeto config, adicione esse novo objeto ao final da matriz.

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

    Depois de o fazer, a nova plugins matriz terá o seguinte aspeto. Tenha em atenção a ligeira diferença se o suplemento utilizar o manifesto apenas do suplemento.

    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"]
      })
    ],
    

Buscar dados do GitHub

O arquivo dialog.js que você acabou de criar especifica que o suplemento deve carregar gists quando o evento de alteração for acionado para o campo de nome de usuário do GitHub. Para recuperar gists do usuário do GitHub, você usará o GitHub Gists API.

  1. Dentro da pasta ./src, crie uma nova subpasta chamada auxiliares.

  2. Na pasta ./src/helpers , crie um ficheiro com o nome gist-api.js.

  3. No gist-api.js, adicione o seguinte código para obter os gists do utilizador a partir do GitHub e criar a lista de 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. Salve suas alterações.

  5. Execute o seguinte comando para recriar o projeto.

    npm run build
    

Implementar um botão sem interface do usuário

O botão inserir gist predefinido deste suplemento é um botão sem IU que invoca uma função JavaScript, em vez de abrir um painel de tarefas como muitos botões de suplemento. Quando o utilizador seleciona o botão Inserir gist predefinido , a função JavaScript correspondente verifica se o suplemento foi configurado.

  • Se o suplemento já tiver sido configurado, a função carrega o conteúdo da imagem que o utilizador selecionou como predefinição e insere-o no corpo da mensagem.

  • Se o suplemento ainda não tiver sido configurado, a caixa de diálogo de definições pede ao utilizador para fornecer as informações necessárias.

Atualizar o arquivo de função (HTML)

Uma função invocada por um botão sem IU tem de ser definida no ficheiro especificado pelo <elemento FunctionFile> no manifesto para o fator de formulário correspondente. O manifesto deste suplemento especifica https://localhost:3000/commands.html como o arquivo de função.

  1. Abra o ./src/commands/commands.html e substitua todo o conteúdo pela seguinte marcação.

    <!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>
    

    Você deve ter notado que o arquivo HTML faz referência a um arquivo JavaScript, addin-config.js, que ainda não existe. Esse arquivo será criado na seção Criar um arquivo para gerenciar as definições de configuração posteriormente neste tutorial.

  2. Salve suas alterações.

Atualizar o arquivo de função (JavaScript)

  1. Abra o arquivo ./src/commands/commands.js e substitua todo o conteúdo pelo código a seguir. Tenha em atenção que se a função insertDefaultGist determinar que o suplemento ainda não foi configurado, adiciona o ?warn=1 parâmetro ao URL da caixa de diálogo. Isso faz com que a caixa de diálogo de configurações renderize a barra de mensagens definida no ./src/settings/dialog.html, para informar ao usuário por que ele vê a caixa de diálogo.

    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. Salve suas alterações.

Criar um arquivo para gerenciar configurações

  1. Na pasta ./src/helpers, crie um arquivo chamado addin-config.js e adicione o código a seguir. O código usa o Objeto RoamingSettings para definir valores de configuração.

    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. Salve suas alterações.

Criar novas funções para processar gists

  1. Em seguida, abra o arquivo ./src/helpers/gist-api.js e adicione as seguintes funções. Observe o seguinte:

    • Se o gist contiver HTML, o suplemento insere o HTML tal como está no corpo da mensagem.

    • Se o gist contiver Markdown, o suplemento utiliza a biblioteca Showdown para converter o Markdown em HTML e, em seguida, insere o HTML resultante no corpo da mensagem.

    • Se o gist contiver algo diferente de HTML ou Markdown, o suplemento o inserirá no corpo da mensagem como um snippet de código.

    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. Salve suas alterações.

Testar o botão Inserir gist padrão

  1. Se o servidor Web local ainda não estiver em execução, execute npm start a partir da linha de comandos.

  2. Abra o Outlook e redija uma nova mensagem.

  3. Na janela de mensagem de texto, selecione o botão Inserir Gist Padrão. Você verá uma caixa de diálogo na qual é possível configurar o suplemento, começando com o prompt para definir seu nome de usuário do GitHub.

    O prompt de caixa de diálogo para configurar o suplemento.

  4. Na caixa de diálogo de definições, introduza o seu nome de utilizador do GitHub e, em seguida, prima a Tecla de Tabulação ou clique noutro local na caixa de diálogo para invocar o evento de alteração , que deve carregar a sua lista de gists públicos. Selecione um gist para ser o padrão e selecione Concluído.

    A caixa de diálogo de configurações do suplemento.

  5. Clique no botão Insert Default Gist novamente. Desta vez, você deverá ver o conteúdo do gist inserido no corpo do email.

    Observação

    Outlook no Windows: Para selecionar as configurações mais recentes, talvez seja necessário fechar e reabrir a janela de composição de mensagens.

Implementar um painel de tarefas

O botão Inserir gist deste suplemento abre um painel de tarefas e apresenta os gists do utilizador. Em seguida, o usuário pode selecionar uma das gists para inserir no corpo da mensagem. Se o usuário ainda não tiver configurado o suplemento, ele será solicitado a fazer isso.

Especificar o arquivo HTML para o painel de tarefas

  1. No projeto que você criou, o painel de tarefas HTML é especificado no arquivo ./src/taskpane/taskpane.html. Abra o arquivo e substitua todo o conteúdo pela seguinte marcação.

    <!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. Salve suas alterações.

Especificar o CSS para o painel de tarefas

  1. No projeto que você criou, o painel de tarefas CSS é especificado no arquivo ./src/taskpane/taskpane.css. Abra o arquivo e substitua todo o conteúdo pelo seguinte código.

    /* 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. Salve suas alterações.

Especificar o JavaScript para o painel de tarefas

  1. No projeto que você criou, o painel de tarefas JavaScript é especificado no arquivo ./src/taskpane/taskpane.js. Abra o arquivo e substitua todo o conteúdo pelo seguinte código.

    (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. Salve suas alterações.

Testar o botão Inserir gist

  1. Se o servidor Web local ainda não estiver em execução, execute npm start a partir da linha de comandos.

  2. Abra o Outlook e redija uma nova mensagem.

  3. Na janela de mensagem de texto, selecione o botão Inserir gist. Você verá um painel de tarefas aberto à direita do formulário de texto.

  4. No painel de tarefas, selecione a gist Olá mundo Html e selecione Inserir para inserir esse gist no corpo da mensagem.

O painel de tarefas do suplemento e o conteúdo do GIST selecionado exibido no corpo da mensagem.

Próximas etapas

Neste tutorial, você criou um suplemento do Outlook que pode ser usado no modo de composição de mensagens para inserir conteúdo no corpo de uma mensagem. Para saber mais sobre o desenvolvimento de suplementos do Outlook, continue no seguinte artigo.

Exemplos de código

Confira também