Ejercicio: Agregar botones y comandos

Completado

En este ejercicio, agregará funcionalidad adicional al complemento que creó en un ejercicio anterior. Creará dos nuevos botones que insertan un gist específico o un gist predeterminado en un mensaje. También implementará una experiencia de primera ejecución, donde se le pedirá el nombre de usuario de GitHub para recuperar los gists.

Importante

En este ejercicio se presupone que ha creado el proyecto de complemento de Office Outlook con el generador de Yeoman y ha probado que funciona en Outlook en un ejercicio anterior de este módulo.

Definir botones

De forma predeterminada, el manifiesto de complemento define solo los botones de la ventana de mensaje leído. Vamos a actualizar el manifiesto para eliminar los botones de la ventana de mensaje leído y definir los dos nuevos botones de la ventana de composición de mensaje:

  • Insertar gist: un botón que abre un panel de tareas
  • Insertar gist predeterminado: un botón que invoca una función

Eliminar punto de extensión MessageReadCommandSurface

Abra el archivo manifest.xml y busque el elemento ExtensionPoint con tipo MessageReadCommandSurface. Elimine este elemento ExtensionPoint (incluida la etiqueta de cierre) para eliminar los botones de la ventana de mensaje leído.

Agregar el punto de extensión MessageComposeCommandSurface

Busque la línea en el manifiesto que lea </DesktopFormFactor>. Justo antes de esta línea, inserte el marcado XML siguiente.

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

Nota:

  • El ExtensionPoint con xsi:type="MessageComposeCommandSurface" indica que está definiendo botones para agregarlos a la ventana de composición de mensaje.
  • Al usar el elemento OfficeTab con id="TabDefault", está indicando que quiere agregar los botones a la pestaña predeterminada de la cinta de opciones.
  • El elemento Group define la agrupación de los botones nuevos, con una etiqueta establecida por el recurso groupLabel.
  • El primer elemento Control contiene un elemento Action con xsi:type="ShowTaskPane", por lo que este botón abrirá un panel de tareas.
  • El segundo elemento Control contiene un elemento Action con xsi:type="ExecuteFunction", por lo que este botón invocará una función de JavaScript contenida en el archivo de la función.

Actualizar recursos en el manifiesto

El código anterior hace referencia a etiquetas, información sobre herramientas y direcciones URL que debe definir para que el manifiesto sea válido. Especifique esta información en la sección Resources del manifiesto.

  1. Busque el elemento Resources en el archivo de manifiesto y elimine todo el elemento (incluida la etiqueta de cierre).

  2. En la misma ubicación, agregue el marcado siguiente para reemplazar el elemento Resources que ha eliminado:

    <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. Guarde los cambios en el manifiesto.

Reinstalar el complemento

Dado que ha realizado cambios en el manifiesto, vuelva a instalar el complemento para que estos cambios surtan efecto.

  1. Si está ejecutando el servidor web, cierre la ventana de comandos del nodo.

  2. Ejecute el siguiente comando para iniciar el servidor web local y transferir local y automáticamente el complemento.

    npm start
    

Después de volver a instalar el complemento, puede comprobar que se ha instalado correctamente buscando los comandos Insertar gist e Insertar gist predeterminado en la ventana de composición de mensaje. No sucederá nada si selecciona uno de estos elementos, ya que aún no ha terminado de crear este complemento.

  • Si está ejecutando este complemento en Outlook 2016 o versiones posteriores en Windows, verá dos nuevos botones en la cinta de opciones de la ventana de composición de mensaje: Insertar gist e Insertar gist predeterminado.

    Captura de pantalla de Outlook en Windows con los botones del complemento resaltados en el menú de desbordamiento de la cinta de opciones.

  • Si está ejecutando este complemento de Outlook en la Web, verá un botón nuevo en la parte inferior de la ventana de composición de mensaje. Seleccione el botón para ver las opciones Insertar gist e Insertar gist predeterminado.

    Captura de pantalla del formulario de redacción de mensajes en Outlook en la Web con el botón de complemento y el menú emergente resaltados.

Implementar una experiencia de primera ejecución

Este complemento debe poder leer gist desde la cuenta GitHub del usuario e identificar cuál ha elegido el usuario como gist predeterminado. Para lograr estos objetivos, el complemento debe pedir al usuario que proporcione su nombre de usuario de GitHub y seleccione un gist predeterminado desde su colección de gist existentes. Siga los pasos de esta sección para implementar una experiencia de primera ejecución que mostrará un cuadro de diálogo para obtener la información del usuario.

Recopilar datos de un usuario

Comencemos creando la interfaz de usuario para el cuadro de diálogo en sí. En la carpeta ./src, cree una subcarpeta denominada configuración. En la carpeta ./src/settings, cree un archivo denominado dialog.html y agregue el marcado siguiente para definir un formulario básico con una entrada de texto para un nombre de usuario de GitHub y una lista vacía para gist que deberá rellenarse mediante 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>

Quizás haya observado que el archivo HTML hace referencia a un archivo JavaScript, gist-api.js, que aún no existe. Este archivo se creará en la sección siguiente Capturar datos de GitHub.

Luego, cree un archivo denominado dialog.css en la carpeta ./src/settings y agregue el código siguiente para especificar los estilos usados por 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;
}

Ahora que ha definido el cuadro de diálogo de interfaz de usuario, puede escribir el código que le permite funcionar. Cree un archivo denominado dialog.css en la carpeta ./src/settings y agregue el código siguiente. Este código usa jQuery para registrar eventos y la función messageParent para enviar las opciones del usuario al autor de la llamada.

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

Actualizar los valores de configuración de webpack

Por último, abra el archivo webpack.config.js en el directorio raíz del proyecto y siga los pasos siguientes.

  1. Busque el objeto entry en el objeto config y agregue una nueva entrada para dialog.

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

    Una vez hecho esto, el nuevo objeto entry tendrá el siguiente aspecto:

    entry: {
      polyfill: ["core-js/stable", "regenerator-runtime/runtime"],
      taskpane: "./src/taskpane/taskpane.js",
      commands: "./src/commands/commands.js",
      dialog: "./src/settings/dialog.js"
    },
    
  2. Busque la matriz plugins en el objeto config. En la matriz patterns del objeto new CopyWebpackPlugin, agregue nuevas entradas para taskpane.css y dialog.css.

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

    Una vez hecho esto, el objeto new CopyWebpackPlugin tendrá el siguiente aspecto:

    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. En la misma matriz plugins dentro del objeto config, agregue el nuevo objeto al final de esa matriz.

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

    Una vez hecho esto, la nueva matriz plugins tendrá el siguiente aspecto:

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

Extraer datos de GitHub

El archivo dialog.js que ha creado especifica que el complemento debe cargar gist cuando se active el evento change para el campo de nombre de usuario de GitHub. Para recuperar los gist de usuario desde GitHub, usará la API de gist de GitHub.

En la carpeta ./src, cree una subcarpeta denominada auxiliares. En la carpeta ./src/helpers, cree un archivo denominado gist-api.js y agregue el código siguiente para recuperar los gist del usuario desde GitHub y crear la lista de gist.

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

Ejecute el siguiente comando para volver a compilar el proyecto.

npm run build

Implementar un botón sin interfaz de usuario

El botón Insertar gist predeterminado de este complemento es un botón sin interfaz de usuario que invocará una función de JavaScript, en lugar de abrir un panel de tareas como hacen varios botones de complemento. Cuando el usuario selecciona el botón Insertar gist predeterminado, la función de JavaScript correspondiente comprobará que el complemento ha sido configurado.

  • Si el complemento ya está configurado, la función cargará el contenido del gist que el usuario ha seleccionado como el predeterminado y lo insertará en el cuerpo del mensaje.
  • Si el complemento no se ha configurado aún, el cuadro de diálogo de configuración le pedirá al usuario que lo configure antes de continuar.

Actualizar el archivo de la función (HTML)

Una función que se ha invocado por un botón sin interfaz de usuario debe definirse en el archivo que se ha especificado por el elemento FunctionFile en el manifiesto del factor de forma correspondiente. El manifiesto de este complemento especifica https://localhost:3000/commands.html como el archivo de la función.

Abra el archivo ./src/commands/commands.html y reemplace todo el contenido con el marcado siguiente.

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

Quizás haya observado que el archivo HTML hace referencia a un archivo JavaScript, addin-config.js, que aún no existe. Este archivo se creará en la sección Crear un archivo para administrar las opciones de configuración más adelante en este ejercicio.

Actualizar el archivo de la función (JavaScript)

Abra el archivo ./src/commands/commands.js y reemplace todo el contenido con el siguiente código. Si la función insertDefaultGist() determina que el complemento no se ha configurado aún, agrega el parámetro ?warn=1 a la dirección URL del cuadro de diálogo. Al hacer eso, el cuadro de diálogo de configuración representa la barra de mensajes que se define ./configuración/dialog.html, para indicar al usuario por qué está viendo el cuadro 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;
}

Crear un archivo para administrar la configuración

El archivo HTML de la función hace referencia a un archivo denominado addin-config.js, que aún no existe. En la carpeta ./src/helpers, cree un archivo denominado addin-config.js y agregue el código siguiente. El código usa el objeto RoamingSettings para obtener y establecer los valores de configuración.

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

Crear funciones nuevas para procesar los gist

Abra el archivo ./src/helpers/gist-api.js y agregue las siguientes funciones.

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

Nota:

  • Si el gist contiene HTML, el complemento inserta el HTML tal y como está en el cuerpo del mensaje.
  • Si el gist contiene Markdown, el complemento usa la biblioteca Showdown para convertirlo en HTML y después inserta el HTML resultante en el cuerpo del mensaje.
  • Si el gist contiene algo que no sea un HTML o Markdown, el complemento lo inserta en el cuerpo del mensaje como un fragmento de código.

Probar el botón Insertar gist predeterminado

Si el servidor aún no se está ejecutando, guarde todos los cambios y ejecute npm start desde el símbolo del sistema. Para probar el botón Insertar gist predeterminado, complete los pasos siguientes.

  1. Abra Outlook y redacte un mensaje nuevo.

  2. En la ventana de composición de mensaje, seleccione el botón Insertar gist predeterminado. Se le pedirá que configure el complemento.

    Captura de pantalla del mensaje del complemento que se va a configurar.

  3. En el cuadro de diálogo de configuración, escriba el nombre de usuario de GitHub y, a continuación, use Ficha o seleccione otra parte del cuadro de diálogo para invocar el evento de cambio, que debe cargar la lista de gists. Seleccione un gist para establecerlo como predeterminado y seleccione Listo.

    Captura de pantalla del cuadro de diálogo de configuración del complemento.

  4. Vuelva a seleccionar el botón Insertar gist predeterminado. Esta vez, debería ver el contenido del gist insertado en el cuerpo del correo electrónico.

    Nota:

    Outlook en Windows: para elegir la configuración más reciente, deberá cerrar y volver a abrir la ventana de redacción del mensaje.

Resumen

En este ejercicio, ha agregado funcionalidad adicional al complemento que creó en un ejercicio anterior. Ha creado dos nuevos botones que insertan un gist específico o un gist predeterminado en un mensaje. También implementó una experiencia de primera ejecución, en la que se le solicitó el nombre de usuario de GitHub para recuperar los gists.

Comprobar sus conocimientos

1.

Para agregar un botón a un complemento de redacción de mensajes de Outlook, ¿a qué tipo de punto de extensión en el archivo de manifiesto del complemento debe agregar el control?

2.

Para llamar a una función de JavaScript desde un botón de la cinta de opciones de la aplicación de Office definida en el manifiesto del complemento, ¿cuál de las siguientes acciones debe hacer?