Exercício – Adicionar botões e comandos

Concluído

Neste exercício, você adicionará funcionalidade extra ao suplemento criado em um exercício anterior. Você criará dois novos botões que inserem uma gist específica ou uma gist padrão em uma mensagem. Você também implementará uma experiência de primeira execução, na qual será solicitado que seu nome de usuário do GitHub recupere seus gists.

Importante

Este exercício pressupõe que você criou o projeto de suplemento do Office Outlook com o gerador Yeoman e testou que ele funciona no Outlook em um exercício anterior neste módulo.

Definir botões

Por padrão, o manifesto do suplemento define apenas 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

Remover o ponto de extensão MessageReadCommandSurface

Abra o arquivo manifest. XML e localize o elemento ExtensionPoint com tipo MessageReadCommandSurface. Exclua esse elemento ExtensionPoint(incluindo a marca de fechamento) para remover os botões na janela de mensagem de leitura.

Adicionar o ponto de extensão MessageComposeCommandSurface

Encontre a seguinte linha no manifesto: </DesktopFormFactor>. Imediatamente antes dessa linha, insira a marcação XML a seguir.

<!-- Message Compose -->
<ExtensionPoint xsi:type="MessageComposeCommandSurface">
  <OfficeTab id="TabDefault">
    <Group id="msgComposeCmdGroup">
      <Label resid="GroupLabel"/>
      <Control xsi:type="Button" id="msgComposeInsertGist">
        <Label resid="TaskpaneButton.Label"/>
        <Supertip>
          <Title resid="TaskpaneButton.Title"/>
          <Description resid="TaskpaneButton.Tooltip"/>
        </Supertip>
        <Icon>
          <bt:Image size="16" resid="Icon.16x16"/>
          <bt:Image size="32" resid="Icon.32x32"/>
          <bt:Image size="80" resid="Icon.80x80"/>
        </Icon>
        <Action xsi:type="ShowTaskpane">
          <SourceLocation resid="Taskpane.Url"/>
        </Action>
      </Control>
      <Control xsi:type="Button" id="msgComposeInsertDefaultGist">
        <Label resid="FunctionButton.Label"/>
        <Supertip>
          <Title resid="FunctionButton.Title"/>
          <Description resid="FunctionButton.Tooltip"/>
        </Supertip>
        <Icon>
          <bt:Image size="16" resid="Icon.16x16"/>
          <bt:Image size="32" resid="Icon.32x32"/>
          <bt:Image size="80" resid="Icon.80x80"/>
        </Icon>
        <Action xsi:type="ExecuteFunction">
          <FunctionName>insertDefaultGist</FunctionName>
        </Action>
      </Control>
    </Group>
  </OfficeTab>
</ExtensionPoint>

Observação

  • ExtensionPoint com xsi:type="MessageComposeCommandSurface" indica que você está definindo botões para adicionar à janela de composição de mensagem.
  • Ao usar um elemento OfficeTab com id="TabDefault", você indica que quer adicionar os botões à guia padrão da faixa de opções.
  • O elemento Group define o agrupamento dos novos botões, com um rótulo definido pelo recurso groupLabel.
  • O primeiro elemento Control contém um elemento Action com xsi:type="ShowTaskPane", portanto, esse botão abre um painel de tarefas.
  • O segundo elemento Control contém um elemento Action com xsi:type="ExecuteFunction", o que indica que esse botão invoca uma função JavaScript contida no arquivo de função.

Atualização de recursos no manifesto

O código anterior faz referência a rótulos, dicas de ferramenta e URLs que você deve definir antes que o manifesto seja válido. Especifique essas informações na Resources do manifesto.

  1. Localize o elemento Resources no arquivo do manifesto e exclua o elemento inteiro (incluindo sua marca de fechamento).

  2. Nesse mesmo local, adicione a seguinte marcação para substituir o elemento Resources removido:

    <Resources>
      <bt:Images>
        <bt:Image id="Icon.16x16" DefaultValue="https://localhost:3000/assets/icon-16.png"/>
        <bt:Image id="Icon.32x32" DefaultValue="https://localhost:3000/assets/icon-32.png"/>
        <bt:Image id="Icon.80x80" DefaultValue="https://localhost:3000/assets/icon-80.png"/>
      </bt:Images>
      <bt:Urls>
        <bt:Url id="Commands.Url" DefaultValue="https://localhost:3000/commands.html"/>
        <bt:Url id="Taskpane.Url" DefaultValue="https://localhost:3000/taskpane.html"/>
      </bt:Urls>
      <bt:ShortStrings>
        <bt:String id="GroupLabel" DefaultValue="Git the gist"/>
        <bt:String id="TaskpaneButton.Label" DefaultValue="Insert gist"/>
        <bt:String id="TaskpaneButton.Title" DefaultValue="Insert gist"/>
        <bt:String id="FunctionButton.Label" DefaultValue="Insert default gist"/>
        <bt:String id="FunctionButton.Title" DefaultValue="Insert default gist"/>
      </bt:ShortStrings>
      <bt:LongStrings>
        <bt:String id="TaskpaneButton.Tooltip" DefaultValue="Displays a list of your gists and allows you to insert their contents into the current message."/>
        <bt:String id="FunctionButton.Tooltip" DefaultValue="Inserts the content of the gist you mark as default into the current message."/>
      </bt:LongStrings>
    </Resources>
    
  3. Salve suas alterações no manifesto.

Reinstalar o suplemento

Como você fez alterações no manifesto, reinstale o suplemento para que essas alterações entrem em vigor.

  1. Se o servidor Web estiver em execução, feche a janela de comando do nó.

  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. Nada acontecerá se você selecionar qualquer um desses itens, porque você ainda não concluiu a criação deste suplemento.

  • Se você estiver executando este suplemento no Outlook 2016 ou posterior no Windows, deverá ver dois novos botões na faixa de opções da janela de composição da mensagem: Inserir gist e Inserir gist padrão.

    Captura de tela do Outlook no Windows com os botões de suplementos destacados no menu de estouro da faixa de opções.

  • Se você estiver usando este suplemento no Outlook na Web, você verá um botão na parte inferior da janela de composição de mensagem. Selecione esse botão para ver as opções Insert Gist e Insert Default Gist.

    Captura de tela do formulário de redação de mensagem no Outlook na Web com o botão de suplemento e o menu pop-up destacados.

Implementando uma experiência de primeira execução

Esse suplemento precisa ler gists da conta do GitHub do usuário e identificar qual usuário escolheu como o gist padrão. Para atingir essas metas, o suplemento deve solicitar que o usuário forneça seu nome de usuário do GitHub e escolha um gist padrão em sua coleção de gists existentes. Conclua as etapas nesta seção para implementar uma experiência de primeira execução que será exibida uma caixa de diálogo para obter essas informações do usuário.

Coletar dados do usuário

Para começar, vamos criar o UI para a caixa de diálogo. Dentro da pasta ./src, crie uma nova subpasta chamada configurações. Na pasta ./src/settings, crie um arquivo chamado dialog.htmle 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á populada 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.

Em seguida, crie um arquivo na pasta ./src/settings chamado dialog.css e adicione o seguinte código para especificar os estilos que são 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;
}

Agora que você definiu a IU da caixa de diálogo, você pode escrever código que realmente faz alguma coisa. Crie um arquivo na pasta ./src/settings chamado dialog.js e adicione o seguinte código. Esse código usa jQuery para registrar eventos e usa a função messageParent para enviar as opções do usuário de volta para o chamador.

(function(){
  'use strict';

  // The Office initialize function must be run each time a new page is loaded.
  Office.initialize = function(reason){
    jQuery(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, " "));
  }
})();

Atualizar as configurações webpack config

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

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

    Após fazer isso, o new CopyWebpackPlugin objeto terá a seguinte aparência:

    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*.xml",
        to: "[name]." + buildType + "[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"]
    })
    

    Após fazer isso, a nova matriz plugins ficará assim:

    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*.xml",
          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 arquivo chamado gist-api.js e adicione o seguinte código para recuperar gists do usuário no 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 desc = $('<span/>')
      .addClass('ms-ListItem-primaryText')
      .text(gist.description)
      .appendTo(listItem);

    const desc = $('<span/>')
      .addClass('ms-ListItem-secondaryText')
      .text(' - ' + buildFileList(gist.files))
      .appendTo(listItem);

    const updated = new Date(gist.updated_at);

    const desc = $('<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;
}

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 do suplemento é um botão sem interface do usuário que invocará uma função JavaScript, em vez de abrir um painel de tarefas como muitos dos botões suplementares. Quando o usuário seleciona o botão Inserir gist padrão, a função JavaScript correspondente verificará se o suplemento foi configurado.

  • Se o suplemento já tiver sido configurado, a função carregará o conteúdo do gist selecionado pelo usuário como padrão e o inserirá no corpo da mensagem.
  • Se o suplemento ainda não tiver sido configurado, a caixa de diálogo de configurações solicitará ao usuário que 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 devem ser definidas no arquivo especificado pelo elemento FunctionFile no manifesto para o fator forma correspondente. O manifesto deste suplemento especifica https://localhost:3000/commands.html como o arquivo de função.

Abra o arquivo ./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 by design. Since functions in commands.js are invoked using 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.

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. Se a insertDefaultGist() função determinar que o suplemento ainda não foi configurado, ele adicionará o parâmetro ?warn=1 à URL da caixa de diálogo. Isso faz com que a caixa de diálogo de configurações renderize a barra de mensagens que está definida em ./settings/dialog.html, para informar ao usuário porque está vendo a caixa de diálogo.

let config;
let btnEvent;

// The initialize function must be run each time a new page is loaded.
Office.initialize = function (reason) {
};

// Add any UI-less function here.
function showError(error) {
  Office.context.mailbox.item.notificationMessages.replaceAsync('github-error', {
    type: 'errorMessage',
    message: error
  }, function(result){
  });
}

let settingsDialog;

function insertDefaultGist(event) {

  config = getConfig();

  // Check if the add-in has been configured.
  if (config && config.defaultGistId) {
    // Get the default gist content and insert.
    try {
      getGist(config.defaultGistId, function(gist, error) {
        if (gist) {
          buildBodyContent(gist, function (content, error) {
            if (content) {
              Office.context.mailbox.item.body.setSelectedDataAsync(content,
                {coercionType: Office.CoercionType.Html}, function(result) {
                  event.completed();
              });
            } else {
              showError(error);
              event.completed();
            }
          });
        } else {
          showError(error);
          event.completed();
        }
      });
    } catch (err) {
      showError(err);
      event.completed();
    }

  } else {
    // Save the event object so we can finish up later.
    btnEvent = event;
    // Not configured yet, display settings dialog with
    // warn=1 to display warning.
    const url = new URI('../src/settings/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(Microsoft.Office.WebExtension.EventType.DialogMessageReceived, receiveMessage);
      settingsDialog.addEventHandler(Microsoft.Office.WebExtension.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;
}

Criar um arquivo para gerenciar configurações

O arquivo de função HTML faz referência a um arquivo chamado suplemento config.js, que ainda não existe. 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);
}

Criar novas funções para processar gists

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

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');
}

Observação

  • Se o gist contiver HTML, o suplemento inserirá o HTML como está no corpo da mensagem.
  • Se o gist 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.

Testar o botão Inserir gist padrão

Se o servidor ainda não estiver em execução, salve todas as alterações e execute o início do npm no prompt de comando. Para testar o botão Inserir gist padrão, conclua as etapas a seguir:

  1. Abra o Outlook e redija uma nova mensagem.

  2. Na janela de mensagem de texto, selecione o botão Inserir Gist Padrão. Você deve ser solicitado a configurar o suplemento.

    Captura de tela do prompt do suplemento a ser configurado.

  3. Na caixa de diálogo de 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. Selecione um gist para ser o padrão e selecione Concluído.

    Captura de tela da caixa de diálogo de configurações do suplemento.

  4. 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.

Resumo

Neste exercício, você adicionou funcionalidade extra ao suplemento criado em um exercício anterior. Você criou dois novos botões que inseriram um gist específico ou um gist padrão em uma mensagem. Você também implementou uma experiência de primeira execução, na qual foi solicitado que seu nome de usuário do GitHub recuperasse seus gists.

Teste o seu conhecimento

1.

Para adicionar um botão a um suplemento de redação de mensagens do Outlook, você deve adicionar o controle para qual tipo de ponto de extensão no arquivo de manifesto do suplemento?

2.

Para chamar uma função JavaScript de um botão na faixa de opções do aplicativo do Office definida no manifesto do suplemento, você deve fazer qual das seguintes opções?