Compartilhar via


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 mensagem de composição
  • 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 você quiser uma versão concluída deste tutorial (usando o manifesto XML), vá até o 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 baixar e instalar a versão certa para seu sistema operacional.

  • 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 você ainda não tiver o Office, poderá se qualificar para uma assinatura de desenvolvedor Microsoft 365 E5 por meio do Programa de Desenvolvedor do Microsoft 365; para obter detalhes, confira as perguntas frequentes. Como alternativa, você pode se inscrever para uma avaliação gratuita de 1 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. Uma pasta que contém o projeto será adicionada 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. As etapas para criar o projeto variam ligeiramente dependendo do tipo de manifesto.

    Observação

    O manifesto unificado do Microsoft 365 permite combinar um Suplemento do Outlook com um aplicativo do Teams como uma única unidade de desenvolvimento e implantação. Estamos trabalhando para estender o suporte para o manifesto unificado para Excel, PowerPoint, Word, desenvolvimento de Copilot personalizado e outras extensões do Microsoft 365. Para saber mais sobre isso, confira Suplementos do Office com o manifesto unificado. Para obter um exemplo de um aplicativo do Teams combinado e suplemento do Outlook, consulte Ofertas de Desconto.

    Adoramos receber seus comentários sobre o manifesto unificado. Se você 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

    • Qual manifesto você gostaria de usar? - unified manifest for Microsoft 365

      Os prompts 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.

    Observação

    Se você estiver usando Node.js versão 20.0.0 ou posterior, poderá ver um aviso quando o gerador executar a instalação de que você tem um mecanismo sem suporte. Estamos trabalhando em uma correção para isso. Enquanto isso, o aviso não afeta o gerador ou o projeto que você gera, portanto, ele pode ser ignorado.

    Dica

    Você pode ignorar as orientações da próximas etapas fornecidas pelo gerador Yeoman após a criação do projeto de suplemento. As instruções passo a passo deste artigo fornecem todas as orientações necessárias para concluir este tutorial.

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

    cd "Git the gist"
    
  4. Esse suplemento usa 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 atualizações a seguir no arquivo de manifesto para especificar algumas informações básicas sobre o suplemento.

  1. Localize a propriedade "descrição", substitua os valores padrão "curto" e "longo" por descrições do suplemento e salve o arquivo.

    "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 usar HTTPS, não HTTP, mesmo enquanto você estiver desenvolvendo. Se você for solicitado a instalar um certificado depois de executar um dos comandos a seguir, aceite o prompt 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.

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

    npm start
    

    Observação

    Se o suplemento não tiver sido carregado automaticamente, siga as instruções nos suplementos do Sideload Outlook para testar para carregar manualmente o 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 será aberto e renderizará 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.

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 de qual manifesto você está usando.

Siga as seguintes etapas:

  1. Abra o arquivo manifest.json .

  2. Na matriz "extensions.runtimes", há dois objetos de runtime. Para o segundo, com a "id" de "CommandsRuntime", altere o "actions.id" para "insertDefaultGist". Esse é o nome de uma função que você cria em uma etapa posterior. Quando terminar, o objeto runtime deve ser semelhante ao seguinte:

    {
        "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". Isso significa que os botões serão exibidos apenas em uma 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 neste 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 "controles" com dois objetos de controle. Precisamos fazer alterações no JSON para cada um deles. No primeiro, siga estas etapas.

    1. Altere a "id" para "msgComposeInsertGist".
    2. Altere o "rótulo" para "Inserir gist".
    3. Altere o "supertip.title" para "Inserir gist".
    4. Altere a "supertip.description" para "Exibe uma lista de seus gists e permite que você insira seu conteúdo na mensagem atual".
  6. No segundo objeto de controle, siga estas etapas.

    1. Altere a "id" para "msgComposeInsertDefaultGist".
    2. Altere o "rótulo" para "Inserir gist padrão".
    3. Altere o "supertip.title" para "Inserir gist padrão".
    4. Altere a "supertip.description" para "Insere o conteúdo da gist que você marca como padrão na mensagem atual".
    5. Altere o "actionId" para "insertDefaultGist". Isso corresponde ao "action.id" do "CommandsRuntime" que você definiu em uma etapa anterior.

    Quando terminar, a propriedade "ribbons" deve se parecer com a seguinte:

    "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 comando a seguir.

    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 você estiver executando esse suplemento em Outlook 2016 ou posterior no Windows, verá dois novos botões na faixa de opções da janela de mensagem de composição: Inserir gist e Inserir gist padrão.

    O menu de estouro de faixa de opções no Outlook clássico no Windows com os botões do suplemento realçados.

  • Se você estiver executando esse suplemento em Outlook na Web ou novo Outlook no Windows (versão prévia), selecione Aplicativos na faixa de opções da janela de mensagem de composição e selecione Git a essência para ver as opções Inserir gist e Inserir gist padrão.

    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 as etapas nesta seção para implementar uma experiência de primeira execução que exibe uma caixa de diálogo para coletar essas informações do usuário.

Create a interface do usuário da caixa de diálogo

Vamos começar criando a interface do usuário 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 arquivo chamado dialog.html.

  3. Em dialog.html, adicione a marcação a seguir para definir um formulário básico com uma entrada de texto para um nome de usuário do GitHub e uma lista vazia para gists que serão preenchidas via 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://static2.sharepointonline.com/files/fabric/office-ui-fabric-core/9.6.1/css/fabric.min.css"/>
    
      <!-- Template styles -->
      <link href="dialog.css" rel="stylesheet" type="text/css" />
    </head>
    
    <body class="ms-font-l">
      <main>
        <section class="ms-font-m ms-fontColor-neutralPrimary">
          <div class="not-configured-warning ms-MessageBar ms-MessageBar--warning">
            <div class="ms-MessageBar-content">
              <div class="ms-MessageBar-icon">
                <i class="ms-Icon ms-Icon--Info"></i>
              </div>
              <div class="ms-MessageBar-text">
                Oops! It looks like you haven't configured <strong>Git the gist</strong> yet.
                <br/>
                Please configure your GitHub username and select a default gist, then try that action again!
              </div>
            </div>
          </div>
          <div class="ms-font-xxl">Settings</div>
          <div class="ms-Grid">
            <div class="ms-Grid-row">
              <div class="ms-TextField">
                <label class="ms-Label">GitHub Username</label>
                <input class="ms-TextField-field" id="github-user" type="text" value="" placeholder="Please enter your GitHub username">
              </div>
            </div>
            <div class="error-display ms-Grid-row">
              <div class="ms-font-l ms-fontWeight-semibold">An error occurred:</div>
              <pre><code id="error-text"></code></pre>
            </div>
            <div class="gist-list-container ms-Grid-row">
              <div class="list-title ms-font-xl ms-fontWeight-regular">Choose Default Gist</div>
              <form>
                <div id="gist-list">
                </div>
              </form>
            </div>
          </div>
          <div class="ms-Dialog-actions">
            <div class="ms-Dialog-actionsRight">
              <button class="ms-Dialog-action ms-Button ms-Button--primary" id="settings-done" disabled>
                <span class="ms-Button-label">Done</span>
              </button>
            </div>
          </div>
        </section>
      </main>
      <script type="text/javascript" src="../../node_modules/jquery/dist/jquery.js"></script>
      <script type="text/javascript" src="../helpers/gist-api.js"></script>
      <script type="text/javascript" src="dialog.js"></script>
    </body>
    
    </html>
    

    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 arquivo na pasta ./src/settings chamada dialog.css.

  6. Em dialog.css, adicione o código a seguir para especificar os estilos usados 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 arquivo chamado 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 fazer isso, o new CopyWebpackPlugin objeto será semelhante ao seguinte. Observe a pequena diferença se o suplemento usar o manifesto XML.

    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 XML 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 fazer isso, a nova plugins matriz parecerá ike o seguinte. Observe a pequena diferença se o suplemento usar o manifesto XML.

    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 XML 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 arquivo chamado gist-api.js.

  3. Em gist-api.js, adicione o código a seguir para recuperar os gists do usuário do GitHub e compilar 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 padrão desse suplemento é um botão sem interface do usuário que invoca uma função JavaScript, em vez de abrir um painel de tarefas como muitos botões de suplemento fazem. Quando o usuário seleciona o botão Inserir gist padrão , a função JavaScript correspondente verifica se o suplemento foi configurado.

  • Se o suplemento já tiver sido configurado, a função carregará o conteúdo da essência selecionada pelo usuário como padrão e a inserirá no corpo da mensagem.

  • Se o suplemento ainda não tiver sido configurado, a caixa de diálogo configurações solicitará que o usuário forneça as informações necessárias.

Atualizar o arquivo de função (HTML)

Uma função invocada por um botão sem interface do usuário deve ser definida no arquivo 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 marcação a seguir.

    <!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. Observe que, se a função insertDefaultGist determinar que o suplemento ainda não foi configurado, ele adicionará o ?warn=1 parâmetro à 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 a essência contiver HTML, o suplemento inserirá o HTML como está no corpo da mensagem.

    • Se a essência contiver Markdown, o suplemento usará a biblioteca Showdown para converter o Markdown em HTML e inserirá 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 do prompt de comando.

  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 configurações, insira seu nome de usuário do GitHub e, em seguida, tab ou clique em outro lugar na caixa de diálogo para invocar o evento de alteração , que deve carregar 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 exibe as gists do usuário. 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://static2.sharepointonline.com/files/fabric/office-ui-fabric-core/9.6.1/css/fabric.min.css"/>
    
        <!-- Template styles -->
        <link href="taskpane.css" rel="stylesheet" type="text/css" />
    </head>
    
    <body class="ms-font-l ms-landing-page">
      <main class="ms-landing-page__main">
        <section class="ms-landing-page__content ms-font-m ms-fontColor-neutralPrimary">
          <div id="not-configured" style="display: none;">
            <div class="centered ms-font-xxl ms-u-textAlignCenter">Welcome!</div>
            <div class="ms-font-xl" id="settings-prompt">Please choose the <strong>Settings</strong> icon at the bottom of this window to configure this add-in.</div>
          </div>
          <div id="gist-list-container" style="display: none;">
            <form>
              <div id="gist-list">
              </div>
            </form>
          </div>
          <div id="error-display" style="display: none;" class="ms-u-borderBase ms-fontColor-error ms-font-m ms-bgColor-error ms-borderColor-error">
          </div>
        </section>
        <button class="ms-Button ms-Button--primary" id="insert-button" tabindex=0 disabled>
          <span class="ms-Button-label">Insert</span>
        </button>
      </main>
      <footer class="ms-landing-page__footer ms-bgColor-themePrimary">
        <div class="ms-landing-page__footer--left">
          <img src="../../assets/logo-filled.png" />
          <h1 class="ms-font-xl ms-fontWeight-semilight ms-fontColor-white">Git the gist</h1>
        </div>
        <div id="settings-icon" class="ms-landing-page__footer--right" aria-label="Settings" tabindex=0>
          <i class="ms-Icon enlarge ms-Icon--Settings ms-fontColor-white"></i>
        </div>
      </footer>
      <script type="text/javascript" src="../../node_modules/jquery/dist/jquery.js"></script>
      <script type="text/javascript" src="../../node_modules/showdown/dist/showdown.min.js"></script>
      <script type="text/javascript" src="../../node_modules/urijs/src/URI.min.js"></script>
      <script type="text/javascript" src="../helpers/addin-config.js"></script>
      <script type="text/javascript" src="../helpers/gist-api.js"></script>
      <script type="text/javascript" src="taskpane.js"></script>
    </body>
    
    </html>
    
  2. 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 do prompt de comando.

  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