Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Este tutorial explica cómo crear un complemento de Outlook que puede usarse en modo de composición de mensajes para insertar contenido en el cuerpo de un mensaje.
En este tutorial, aprenderá a:
- Crear un proyecto de complemento de Outlook
- Definir los botones que aparecen en la ventana para redactar mensaje
- Implementar una experiencia de primera ejecución que recopile información y recupere datos de un servicio externo
- Implementar un botón sin interfaz de usuario que invoca una función
- Implementar un panel de tareas que inserta contenido en el cuerpo de un mensaje
Sugerencia
Si desea una versión completa de este tutorial, visite el repositorio de muestras de complementos de Office en GitHub.
Requisitos previos
Node.js (la versión más reciente de Active LTS). Visite el sitioNode.js para descargar e instalar la versión adecuada para su sistema operativo.
La versión más reciente de Yeoman y Generador de Yeoman para complementos de Office. Para instalar estas herramientas globalmente, ejecute el siguiente comando desde el símbolo del sistema.
npm install -g yo generator-officeNota:
Incluso si ya ha instalado el generador Yeoman, recomendamos que actualice el paquete de la versión más reciente desde npm.
Office está conectado a una suscripción Microsoft 365 (incluido Office en la Web).
Nota:
Si aún no tiene Office, es posible que sea apto para una suscripción de desarrollador de Microsoft 365 E5 a través del Programa de desarrollador de Microsoft 365; para obtener más información, consulte las preguntas más frecuentes. Como alternativa, puede registrarse para obtener una prueba gratuita de 1 mes o comprar un plan de Microsoft 365.
Visual Studio Code (VS Code) o el editor de código que prefiera.
Outlook en la Web, en Windows (nuevo o clásico) o en Mac.
una cuenta de GitHub .
Instalación
El complemento de este tutorial lee los gists de una cuenta de GitHub y agrega el gist seleccionado al cuerpo de un mensaje. Siga los pasos siguientes para crear dos gist nuevos que puede usar para probar el complemento que va a crear.
-
En el campo Descripción del gist..., escriba Hola mundo Markdown.
En el campo Nombre del archivo incluye la extensión..., escriba test.md.
Agregue el markdown siguiente al cuadro de texto de varias líneas:
# Hello World This is content converted from Markdown! Here's a JSON sample: ```json { "foo": "bar" } ```Seleccione el botón Crear gist público.
-
En el campo Descripción del gist..., escriba Hola mundo Html.
En el campo Nombre del archivo incluye la extensión..., escriba test.html.
Agregue el markdown siguiente al cuadro de texto de varias líneas:
<html> <head> <style> h1 { font-family: Calibri; } </style> </head> <body> <h1>Hello World!</h1> <p>This is a test</p> </body> </html>Seleccione el botón Crear gist público.
Crear un proyecto de complemento de Outlook
Ejecute el siguiente comando para crear un proyecto de complemento con el generador Yeoman. Una carpeta que contiene el proyecto se agregará al directorio actual.
yo officeNota:
Cuando ejecute el comando
yo office, es posible que reciba mensajes sobre las directivas de recopilación de datos de Yeoman y las herramientas de la CLI de complementos de Office. Use la información adecuada que se proporciona para responder a los mensajes.Cuando se le pida, proporcione la siguiente información para crear el proyecto de complemento.
Los pasos para crear el proyecto varían ligeramente según el tipo de manifiesto.
Nota:
El manifiesto unificado para Microsoft 365 le permite combinar un complemento de Office con una aplicación de Teams en una aplicación para Microsoft 365 como una única unidad de desarrollo e implementación. Para obtener más información, vea Complementos de Office con el manifiesto unificado. Para obtener un ejemplo de una aplicación para Microsoft 365 que combina una aplicación de Teams y un complemento de Outlook, vea Ofertas de descuento.
Nos encanta recibir sus comentarios sobre el manifiesto unificado. Si tiene alguna sugerencia, cree un problema en el repositorio de la biblioteca de JavaScript de Office.
Nota:
El manifiesto unificado para Microsoft 365 no se admite actualmente en Outlook en Mac. Si usa Outlook en Mac, seleccione la pestaña de manifiesto de solo complemento y siga las instrucciones en su lugar. Para obtener más información, consulte Compatibilidad con complementos con el manifiesto unificado para Microsoft 365.
Elija un tipo de proyecto -
Office Add-in Task Pane projectElija un tipo de script -
JavaScript¿Qué nombre quiere asignar al complemento? -
Git the gist¿Qué aplicación cliente de Office desea admitir? -
Outlook¿Qué manifiesto quiere usar? -
unified manifest for Microsoft 365
Después de completar el asistente, el generador creará el proyecto e instalará componentes auxiliares de Node.
Vaya a la carpeta raíz del proyecto.
cd "Git the gist"Abra el proyecto en VS Code o en el editor de código que prefiera.
Sugerencia
En Windows, puede navegar hasta el directorio raíz del proyecto desde la línea de comandos y, después, puede escribir
code .para abrir esa carpeta en VS Code. En un equipo Mac, necesitará agregar el comandocodea la ruta de acceso antes de poder usar ese comando para abrir la carpeta de proyecto en VS Code.
Actualizar el manifiesto
El manifiesto de un complemento controla cómo se muestra en Outlook. Define cómo se muestran el complemento en la lista de complementos y los botones que aparecen en la cinta de opciones, y establece las direcciones URL de los archivos HTML y JavaScript que ese complemento usa.
Especificar información básica
Realice las siguientes actualizaciones en el archivo de manifiesto para especificar cierta información básica sobre el complemento.
Busque la
"description"propiedad, reemplace los valores predeterminados"short"por"full"descripciones del complemento y guarde el archivo."description": { "short": "Gets gists.", "full": "Allows users to access their GitHub gists." },Guarde el archivo.
Probar el complemento generado
Antes de seguir, vamos a probar el complemento básico que creó el generador para confirmar que el proyecto está configurado correctamente.
Nota:
Los complementos de Office deben usar HTTPS, no HTTP, incluso mientras desarrolla. Si se le solicita que instale un certificado después de ejecutar uno de los siguientes comandos, acepte la solicitud para instalar el certificado que proporciona el generador de Yeoman. Es posible que también deba ejecutar el símbolo del sistema o el terminal como administrador para que se realicen los cambios.
Si es la primera vez que desarrolla un complemento de Office en su equipo, es posible que se le pida en la línea de comandos que conceda a Microsoft Edge WebView una exención de bucle invertido ("¿Permitir el bucle invertido de localhost para Microsoft Edge WebView?"). Cuando se le solicite, enter
Ypara permitir la exención. Tenga en cuenta que necesitará privilegios de administrador para permitir la exención. Una vez permitido, no se le solicitará una exención cuando transfiera localmente complementos de Office en el futuro (a menos que quite la exención de su equipo). Para obtener más información, consulte "No podemos abrir este complemento desde localhost" al cargar un complemento de Office o al usar Fiddler.
Cuando usa por primera vez el generador de Yeoman para desarrollar un complemento de Office, el explorador predeterminado abre una ventana en la que se le pedirá que inicie sesión en su cuenta de Microsoft 365. Si no aparece una ventana de inicio de sesión y encuentra un error de instalación de prueba o de tiempo de espera de inicio de sesión, ejecute
atk auth login m365.
Ejecute el siguiente comando en el directorio raíz del proyecto. Al ejecutar este comando, se inicia el servidor web local y se transfiere localmente el complemento.
npm startNota:
Cuando usa por primera vez el generador de Yeoman para desarrollar un complemento de Office, el explorador predeterminado abre una ventana en la que se le pedirá que inicie sesión en su cuenta de Microsoft 365. Si no aparece una ventana de inicio de sesión y se produce un error de instalación de prueba o de tiempo de espera de inicio de sesión, ejecútelo
atk auth login m365antes de volver a ejecutarlonpm start.Si el complemento no se transfirió localmente automáticamente, siga las instrucciones de Instalación de prueba de complementos de Outlook para realizar una instalación de prueba de la transferencia local del complemento en Outlook.
En Outlook, abra un mensaje existente y seleccione el botón Mostrar panel de tareas.
Cuando se le solicite el cuadro de diálogo WebView Stop On Load, seleccione Aceptar.
Nota:
Si selecciona Cancelar, no se volverá a mostrar el cuadro de diálogo mientras se esté ejecutando esta instancia del complemento. Sin embargo, si reinicia el complemento, verá el cuadro de diálogo de nuevo.
Si todo se ha configurado correctamente, el panel de tareas se abre y representa la página principal del complemento.
Cuando desee detener el servidor web local y desinstalar el complemento, siga las instrucciones aplicables:
Para detener el servidor, ejecute el siguiente comando. Si ha utilizado
npm start, el siguiente comando también debería desinstalar el complemento.npm stopSi transfirió localmente el complemento, vea Quitar un complemento transferido localmente.
Definir botones
Ahora que ya ha comprobado que el complemento base funciona, puede personalizarlo para agregar más funcionalidades. De forma predeterminada, el manifiesto 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:
Mostrar la lista de esencia: un botón que abre un panel de tareas
Insertar gist predeterminado: un botón que invoca una función
El procedimiento depende del manifiesto que se use.
Siga estos pasos:
Abra el archivo manifest.json .
En la
"extensions.runtimes"matriz, hay dos objetos de tiempo de ejecución. Para el segundo, con el"id"de"CommandsRuntime", cambia el"actions.id"a"insertDefaultGist". Es el nombre de una función que creará en un paso posterior. Cuando haya terminado, el objeto en tiempo de ejecución debe tener un aspecto similar al siguiente:{ "id": "CommandsRuntime", "type": "general", "code": { "page": "https://localhost:3000/commands.html" }, "actions": [ { "id": "insertDefaultGist", "type": "executeFunction" } ] }Cambie el elemento de la
"extensions.ribbons.contexts"matriz a"mailCompose". Esto significa que los botones solo aparecen en un mensaje nuevo o en una ventana de respuesta."contexts": [ "mailCompose" ],La
"extensions.ribbons.tabs.groups"matriz contiene un objeto de grupo. Haga los siguientes cambios en este objeto.- Cambie la
"id"propiedad a"msgComposeCmdGroup". - Cambie la
"label"propiedad a "Git the gist".
- Cambie la
Ese mismo objeto de grupo tiene una
"controls"matriz con dos objetos de control. Necesitamos hacer cambios en el JSON para cada uno de ellos. En la primera, sigue estos pasos.- Cambie el
"id"formato a"msgComposeShowGistListTaskPane". - Cambie a "Mostrar lista de
"label"esencia". - Cambie a "Mostrar lista de
"supertip.title"esencia". - Cambie a
"supertip.description""Muestra una lista de los elementos esenciales y le permite insertar su contenido en el mensaje actual".
- Cambie el
En el segundo objeto de control, siga estos pasos.
- Cambie el
"id"formato a"msgComposeInsertDefaultGist". -
"label"Cambie a "Insertar esencia predeterminada". -
"supertip.title"Cambie a "Insertar esencia predeterminada". - Cambie a
"supertip.description""Inserta el contenido de la esencia que marca como predeterminada en el mensaje actual". - Cambie el
"actionId"formato a"insertDefaultGist". Esto coincide con el"action.id"de lo"CommandsRuntime"que estableció en un paso anterior.
Cuando haya terminado, la
"ribbons"propiedad debe verse de la siguiente manera:"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": "Display gist list", "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": "Display gist list", "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" } ] } ] } ] } ]- Cambie el
Guarde los cambios en el manifiesto.
Reinstalar el complemento
Debe reinstalar el complemento para que los cambios del manifiesto surtan efecto.
Si el servidor web se está ejecutando, ejecute el siguiente comando.
npm stopEjecute el siguiente comando para iniciar el servidor web local y transferir local y automáticamente el complemento.
npm start
Después de reinstalar el complemento, puede comprobar que se instaló correctamente comprobando los comandos Mostrar lista gist e Insertar gist predeterminado en una ventana de redacción de mensajes. Tenga en cuenta que 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 clásico en Windows o en Outlook en Mac, debería ver dos nuevos botones en la cinta de opciones de la ventana de redacción de mensajes: Mostrar la lista gist e Insertar gist predeterminado.
Si está ejecutando este complemento en Outlook en la Web o en el nuevo Outlook en Windows, seleccione Aplicaciones en la cinta de opciones de la ventana de redacción del mensaje y, a continuación, seleccione Git the gist para ver la lista Mostrar gist e Insertar las opciones predeterminadas de gist.
Sugerencia
Para obtener instrucciones sobre cómo buscar el complemento en la cinta de opciones, vea Usar complementos en Outlook.
Implementar una experiencia de primera ejecución
Este complemento debe poder leer GIST desde una cuenta de GitHub e identificar el gist predeterminado elegido. Para lograr estos objetivos, el complemento le pide que proporcione su nombre de usuario de GitHub y elija un gist predeterminado de la colección de gist existentes. Complete los pasos de esta sección para implementar una experiencia de primera ejecución que muestre un cuadro de diálogo para recopilar esta información.
Crear la interfaz de usuario del cuadro de diálogo
Comencemos por crear la interfaz de usuario del cuadro de diálogo.
En la carpeta ./src, cree una subcarpeta denominada configuración.
En la carpeta ./src/settings , cree un archivo llamado dialog.html.
En dialog.html, agregue el siguiente marcado para definir un formulario básico con una entrada de texto para un nombre de usuario de GitHub y una lista vacía para gists que se rellenarán 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/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.1.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="../helpers/gist-api.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 Capturar datos de GitHub .
Guarde los cambios.
A continuación, cree un archivo en la carpeta ./src/settings denominado dialog.css.
En dialog.css, agregue el código siguiente para especificar los estilos que utiliza dialog.html.
body { background-color: #ffffff; } 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; }Guarde los cambios.
Desarrollar la funcionalidad del cuadro de diálogo
Ahora que ha definido el cuadro de diálogo de interfaz de usuario, puede escribir el código que le permite funcionar.
En la carpeta ./src/settings , cree un archivo llamado dialog.js.
Agregue el siguiente código.
(function () { "use strict"; // The onReady function must be run each time a new page is loaded. Office.onReady(function () { function initializeDialog() { if (window.location.search) { // Check if warning should be displayed. const warn = getParameterByName("warn"); if (warn) { document.querySelector(".not-configured-warning").style.display = "block"; } else { // See if the config values were passed. // If so, pre-populate the values. const user = getParameterByName("gitHubUserName"); const gistId = getParameterByName("defaultGistId"); document.getElementById("github-user").value = user; loadGists(user, function (success) { if (success) { document.querySelectorAll(".ms-ListItem").forEach(function (item) { item.classList.remove("is-selected"); if (item.value === gistId) { item.classList.add("is-selected"); item.checked = true; } }); document.getElementById("settings-done").disabled = false; } }); } } // When the GitHub username changes, try to load gists. document.getElementById("github-user").addEventListener("change", function () { document.getElementById("gist-list").textContent = ""; const ghUser = document.getElementById("github-user").value; if (ghUser.length > 0) { loadGists(ghUser); } }); // When the Done button is selected, send the values back to the caller as a serialized object. document.getElementById("settings-done").addEventListener("click", function () { const settings = {}; settings.gitHubUserName = document.getElementById("github-user").value; const selectedGist = document.querySelector(".ms-ListItem.is-selected"); if (selectedGist) { settings.defaultGistId = selectedGist.value; sendMessage(JSON.stringify(settings)); } }); } if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", initializeDialog); } else { initializeDialog(); } }); // Load gists using the GitHub API and build the list. function loadGists(user, callback) { getUserGists(user, function (gists, error) { if (error) { document.querySelector(".gist-list-container").style.display = "none"; document.getElementById("error-text").textContent = JSON.stringify(error, null, 2); document.querySelector(".error-display").style.display = "block"; if (callback) callback(false); } else { document.querySelector(".error-display").style.display = "none"; buildGistList(document.getElementById("gist-list"), gists, onGistSelected); document.querySelector(".gist-list-container").style.display = "block"; if (callback) callback(true); } }); } function onGistSelected() { document.querySelectorAll(".ms-ListItem").forEach(function (item) { item.classList.remove("is-selected"); item.checked = false; }); const selectedItem = this.querySelector(".ms-ListItem"); selectedItem.classList.add("is-selected"); selectedItem.checked = true; document.querySelector(".not-configured-warning").style.display = "none"; document.getElementById("settings-done").disabled = false; } function sendMessage(message) { Office.context.ui.messageParent(message); } function getParameterByName(name, url) { return new URL(url || window.location.href).searchParams.get(name); } })();Guarde los cambios.
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.
Busque el objeto
entryen el objetoconfigy agregue una nueva entrada paradialog.dialog: "./src/settings/dialog.js",Después de hacer esto, el nuevo
entryobjeto tiene el siguiente aspecto: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", },Busque la matriz
pluginsen el objetoconfig. En la matrizpatternsdel objetonew 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", },Después de hacer esto, el
new CopyWebpackPluginobjeto tiene un aspecto similar al siguiente. Observe la ligera diferencia si el complemento usa solo el manifiesto del complemento.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*.*", to: "[name]" + "[ext]", transform(content) { if (dev) { return content; } else { return content.toString().replace(new RegExp(urlDev, "g"), urlProd); } }, }, ]}),En la misma matriz
pluginsdentro del objetoconfig, agregue el nuevo objeto al final de esa matriz.new HtmlWebpackPlugin({ filename: "dialog.html", template: "./src/settings/dialog.html", chunks: ["polyfill", "dialog"] })Después de hacer esto, la nueva
pluginsmatriz tiene un aspecto similar al siguiente. Observe la ligera diferencia si el complemento usa solo el manifiesto del complemento.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*.*", 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 acaba de crear 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 GISTS de GitHub, usará la API de GitHub Gists.
En la carpeta ./src, cree una subcarpeta denominada auxiliares.
En la carpeta ./src/helpers , cree un archivo llamado gist-api.js.
En gist-api.js, agregue el código siguiente para recuperar los gists de GitHub y crear la lista de gists.
function getUserGists(user, callback) { const requestUrl = "https://api.github.com/users/" + encodeURIComponent(user) + "/gists"; fetchJson(requestUrl, callback); } function buildGistList(parent, gists, clickFunc) { gists.forEach(function (gist) { const listItem = document.createElement("div"); parent.appendChild(listItem); const radioItem = document.createElement("input"); radioItem.classList.add("ms-ListItem", "is-selectable"); radioItem.type = "radio"; radioItem.name = "gists"; radioItem.tabIndex = 0; radioItem.value = gist.id; listItem.appendChild(radioItem); const descPrimary = document.createElement("span"); descPrimary.classList.add("ms-ListItem-primaryText"); descPrimary.textContent = gist.description; listItem.appendChild(descPrimary); const descSecondary = document.createElement("span"); descSecondary.classList.add("ms-ListItem-secondaryText"); descSecondary.textContent = " - " + buildFileList(gist.files); listItem.appendChild(descSecondary); const updated = new Date(gist.updated_at); const descTertiary = document.createElement("span"); descTertiary.classList.add("ms-ListItem-tertiaryText"); descTertiary.textContent = " - Last updated " + updated.toLocaleString(); listItem.appendChild(descTertiary); listItem.addEventListener("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; }Guarde los cambios.
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 invoca una función de JavaScript, en lugar de abrir un panel de tareas como hacen muchos botones de complemento. Al seleccionar el botón Insertar esencia predeterminada , la función JavaScript correspondiente comprueba si el complemento está configurado.
Si el complemento ya se ha configurado, la función carga el contenido de la esencia que ha seleccionado como predeterminada y lo inserta en el cuerpo del mensaje.
Si el complemento aún no se ha configurado, el cuadro de diálogo de configuración le pedirá un nombre de usuario de GitHub.
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 ./src/commands/commands.html y reemplace todo el contenido con el siguiente marcado.
<!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/hosted/office.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>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 tutorial.
Guarde los cambios.
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. Tenga en cuenta que si la función insertDefaultGist determina que el complemento aún no se ha configurado, agrega el
?warn=1parámetro a la dirección URL del cuadro de diálogo. Si lo hace, el cuadro de diálogo de configuración representa la barra de mensajes definida en ./src/settings/dialog.htmlpara decirle por qué está viendo el cuadro 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, }, 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 URL("dialog.html?warn=1", window.location.href).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; }Guarde los cambios.
Crear un archivo para administrar la configuración
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); }Guarde los cambios.
Crear funciones nuevas para procesar los gist
Abra el archivo ./src/helpers/gist-api.js y agregue las siguientes funciones. Tenga en cuenta lo siguiente:
Si el gist contiene HTML, el complemento inserta el HTML tal cual en el cuerpo del mensaje.
Si el gist contiene Markdown, el complemento usa la API Markdown de GitHub para convertir Markdown a HTML y, a continuación, 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.
function getGist(gistId, callback) { const requestUrl = "https://api.github.com/gists/" + encodeURIComponent(gistId); fetchJson(requestUrl, callback); } function fetchJson(url, callback) { fetch(url, { headers: { Accept: "application/vnd.github+json" } }) .then(function (response) { if (!response.ok) { throw new Error("GitHub request failed: " + response.status + " " + response.statusText); } return response.json(); }) .then(function (data) { callback(data); }) .catch(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) { switch (file.language) { case "HTML": // Insert as is. callback(file.content); break; case "Markdown": // Use GitHub's renderer so gist Markdown matches github.com. fetch("https://api.github.com/markdown", { method: "POST", headers: { Accept: "application/vnd.github+json", "Content-Type": "application/json", }, body: JSON.stringify({ text: file.content, mode: "gfm" }), }) .then(function (response) { if (!response.ok) { throw new Error("GitHub Markdown request failed: " + response.status + " " + response.statusText); } return response.text(); }) .then(function (html) { callback(html); }) .catch(function (error) { callback(null, error); }); break; default: // Insert contents as a <code> block. const codeElement = document.createElement("code"); codeElement.textContent = file.content; const preElement = document.createElement("pre"); preElement.appendChild(codeElement); callback(preElement.outerHTML); } return; } } } callback(null, "No suitable file found in the gist"); }Guarde los cambios.
Probar el botón Insertar gist predeterminado
Si el servidor web local aún no se está ejecutando, ejecútelo
npm startdesde el símbolo del sistema.Abra Outlook y redacte un mensaje nuevo.
En la ventana de composición de mensaje, seleccione el botón Insertar gist predeterminado. Aparece un cuadro de diálogo que te pide que establezcas tu nombre de usuario de GitHub.
En el cuadro de diálogo de configuración, escriba el nombre de usuario de GitHub y, a continuación, use la tecla TAB o haga clic en otra parte del cuadro de diálogo para invocar el evento de cambio . Esta acción carga su lista de esencias públicas. Seleccione un gist para que sea el predeterminado y seleccione Listo.
Vuelva a seleccionar el botón Insertar gist predeterminado. Esta vez, el contenido del gist predeterminado se inserta en el cuerpo del mensaje.
Nota:
Outlook clásico en Windows: Para obtener la configuración más reciente, es posible que deba cerrar y volver a abrir la ventana para redactar el mensaje.
Implementar un panel de tareas
El botón Mostrar lista de gist del complemento abre un panel de tareas y muestra su esencia. A continuación, puede seleccionar uno de los gist para insertarlo en el cuerpo del mensaje. Si todavía no ha configurado el complemento, se le pedirá que lo haga.
Especifique el código HTML para el panel de tareas
En el proyecto que ha creado, el panel de tareas HTML se especifica en el archivo ./src/taskpane/taskpane.html. Abra el archivo 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" /> <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/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.1.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="../helpers/addin-config.js"></script> <script type="text/javascript" src="../helpers/gist-api.js"></script> </body> </html>Guarde los cambios.
Especifique el CSS para el panel de tareas
En el proyecto que ha creado, el panel de tareas CSS se especifica en el archivo ./src/taskpane/taskpane.css. Abra el archivo y reemplace todo el contenido con el código siguiente.
/* 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; background-color: #ffffff; } 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; }Guarde los cambios.
Especifique el JavaScript para el panel de tareas
En el proyecto que ha creado, el panel de tareas JavaScript se especifica en el archivo ./src/taskpane/taskpane.js. Abra el archivo y reemplace todo el contenido con el código siguiente.
(function () { "use strict"; let config; let settingsDialog; Office.onReady(function () { function initializeTaskPane() { 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. document.getElementById("not-configured").style.display = ""; } // When insert button is selected, build the content and insert into the body. document.getElementById("insert-button").addEventListener("click", function () { const selectedGist = document.querySelector(".ms-ListItem.is-selected"); const gistId = selectedGist && selectedGist.value; 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. document.getElementById("settings-icon").addEventListener("click", function () { // Display settings dialog. const url = new URL("dialog.html", window.location.href); if (config) { // If the add-in has already been configured, pass the existing values to the dialog. url.searchParams.set("gitHubUserName", config.gitHubUserName); url.searchParams.set("defaultGistId", config.defaultGistId); } const dialogOptions = { width: 20, height: 40, displayInIframe: true }; Office.context.ui.displayDialogAsync(url.toString(), dialogOptions, function (result) { settingsDialog = result.value; settingsDialog.addEventHandler(Office.EventType.DialogMessageReceived, receiveMessage); settingsDialog.addEventHandler(Office.EventType.DialogEventReceived, dialogClosed); }); }); } if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", initializeTaskPane); } else { initializeTaskPane(); } }); function loadGists(user) { document.getElementById("error-display").style.display = "none"; document.getElementById("not-configured").style.display = "none"; document.getElementById("gist-list-container").style.display = ""; getUserGists(user, function (gists, error) { if (error) { } else { const gistList = document.getElementById("gist-list"); gistList.textContent = ""; buildGistList(gistList, gists, onGistSelected); } }); } function onGistSelected() { document.getElementById("insert-button").disabled = false; document.querySelectorAll(".ms-ListItem").forEach(function (item) { item.classList.remove("is-selected"); item.checked = false; }); const selectedItem = this.querySelector(".ms-ListItem"); selectedItem.classList.add("is-selected"); selectedItem.checked = true; } function showError(error) { document.getElementById("not-configured").style.display = "none"; document.getElementById("gist-list-container").style.display = "none"; const errorDisplay = document.getElementById("error-display"); errorDisplay.textContent = error; errorDisplay.style.display = ""; } function receiveMessage(message) { config = JSON.parse(message.message); setConfig(config, function (result) { settingsDialog.close(); settingsDialog = null; loadGists(config.gitHubUserName); }); } function dialogClosed(message) { settingsDialog = null; } })();Guarde los cambios.
Probar el botón "Mostrar lista de esencia"
Si el servidor web local aún no se está ejecutando, ejecútelo
npm startdesde el símbolo del sistema.Abra Outlook y redacte un mensaje nuevo.
En la ventana de redacción del mensaje, seleccione el botón Mostrar lista esencial . Se abre un panel de tareas.
En el panel de tareas, seleccione el gist Hola mundo Html e insertar para insertar el gist en el cuerpo del mensaje.
Pasos siguientes
En este tutorial, ha creado un complemento de Outlook que puede usarse en modo de composición de mensajes para insertar contenido en el cuerpo de un mensaje. Para obtener más información sobre el desarrollo de complementos de Outlook, pase al siguiente artículo.
Ejemplos de código
- Tutorial completado del complemento de Outlook: resultado de completar este tutorial.