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.
Visual Studio Code (VS Code) ou o editor de código preferencial.
Outlook na Web, novo Outlook no Windows ou Outlook 2016 ou posterior no Windows (ligado a uma conta do Microsoft 365).
Uma conta do GitHub .
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.
-
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.
-
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
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.
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
Depois que você concluir o assistente, o gerador criará o projeto e instalará os componentes Node de suporte.
Navegue até o diretório raiz do projeto.
cd "Git the gist"
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
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 comandocode
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.
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." },
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.
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.
No Outlook, abra uma mensagem existente e selecione o botão Mostrar Painel de Tarefas.
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.
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:
Abra o ficheiro manifest.json .
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" } ] }
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" ],
A matriz "extensions.ribbons.tabs.groups" tem um objeto de grupo. Faça as seguintes alterações a este objeto.
- Altere a propriedade "id" para "msgComposeCmdGroup".
- Altere a propriedade "label" para "Git the gist".
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.
- Altere o "id" para "msgComposeInsertGist".
- Altere a "etiqueta" para "Insert gist".
- Altere "supertip.title" para "Insert gist".
- Altere a "supertip.description" para "Apresenta uma lista dos seus gists e permite-lhe inserir os respetivos conteúdos na mensagem atual".
No segundo objeto de controlo, siga estes passos.
- Altere o "id" para "msgComposeInsertDefaultGist".
- Altere a "etiqueta" para "Inserir gist predefinido".
- Altere "supertip.title" para "Insert default gist".
- Altere a "supertip.description" para "Insere o conteúdo da imagem que marca como predefinição na mensagem atual".
- 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" } ] } ] } ] } ]
Salve suas alterações no manifesto.
Reinstalar o suplemento
Você deve reinstalar o complemento para que as alterações de manifesto entrem em vigor.
Se o servidor Web estiver em execução, execute o seguinte comando.
npm stop
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.
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.
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.
Dentro da pasta ./src, crie uma nova subpasta chamada configurações.
Na pasta ./src/settings , crie um ficheiro com o nome dialog.html.
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.
Salve suas alterações.
Em seguida, crie um ficheiro na pasta ./src/settings com o nome dialog.css.
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; }
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.
Na pasta ./src/settings , crie um ficheiro com o nome dialog.js.
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, " ")); } })();
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.
Localize o objeto
entry
dentro do objetoconfig
e adicione uma nova entrada paradialog
.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", },
Localize a
plugins
matriz dentro doconfig
objeto. Na matrizpatterns
do objetonew 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); } }, }, ]}),
Na mesma matriz
plugins
do objetoconfig
, 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.
Dentro da pasta ./src, crie uma nova subpasta chamada auxiliares.
Na pasta ./src/helpers , crie um ficheiro com o nome gist-api.js.
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; }
Salve suas alterações.
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.
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.
Salve suas alterações.
Atualizar o arquivo de função (JavaScript)
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; }
Salve suas alterações.
Criar um arquivo para gerenciar configurações
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); }
Salve suas alterações.
Criar novas funções para processar gists
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'); }
Salve suas alterações.
Testar o botão Inserir gist padrão
Se o servidor Web local ainda não estiver em execução, execute
npm start
a partir da linha de comandos.Abra o Outlook e redija uma nova mensagem.
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.
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.
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
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>
Salve suas alterações.
Especificar o CSS para o painel de tarefas
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; }
Salve suas alterações.
Especificar o JavaScript para o painel de tarefas
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; } })();
Salve suas alterações.
Testar o botão Inserir gist
Se o servidor Web local ainda não estiver em execução, execute
npm start
a partir da linha de comandos.Abra o Outlook e redija uma nova mensagem.
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.
No painel de tarefas, selecione a gist Olá mundo Html e selecione Inserir para inserir esse gist 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
- Tutorial de suplemento do Outlook concluído: o resultado da conclusão deste tutorial.