357 lines
17 KiB
JavaScript
357 lines
17 KiB
JavaScript
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// Licensed under the MIT license.
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.sideloadAddIn = exports.getTemplatePath = exports.generateSideloadUrl = exports.generateSideloadFile = void 0;
|
|
const tslib_1 = require("tslib");
|
|
const fs = require("fs");
|
|
const AdmZip = require("adm-zip");
|
|
const office_addin_manifest_1 = require("office-addin-manifest");
|
|
const open = require("open");
|
|
const semver = require("semver");
|
|
const os = require("os");
|
|
const path = require("path");
|
|
const appType_1 = require("./appType");
|
|
const dev_settings_1 = require("./dev-settings");
|
|
const process_1 = require("./process");
|
|
const prompt_1 = require("./prompt");
|
|
const registry = require("./registry");
|
|
const defaults_1 = require("./defaults");
|
|
const office_addin_usage_data_1 = require("office-addin-usage-data");
|
|
/* global __dirname, Buffer, console, process, URL */
|
|
/**
|
|
* Create an Office document in the temporary files directory
|
|
* which can be opened to launch the Office app and load the add-in.
|
|
* @param app Office app
|
|
* @param manifest Manifest for the add-in.
|
|
* @returns Path to the file.
|
|
*/
|
|
function generateSideloadFile(app, manifest, document) {
|
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
if (!manifest.id) {
|
|
throw new office_addin_usage_data_1.ExpectedError("The manifest does not contain the id for the add-in.");
|
|
}
|
|
if (!manifest.officeAppType) {
|
|
throw new office_addin_usage_data_1.ExpectedError("The manifest does not contain the OfficeApp xsi:type.");
|
|
}
|
|
if (!manifest.version) {
|
|
throw new office_addin_usage_data_1.ExpectedError("The manifest does not contain the version for the add-in.");
|
|
}
|
|
const addInType = (0, office_addin_manifest_1.getAddInTypeForManifestOfficeAppType)(manifest.officeAppType);
|
|
if (!addInType) {
|
|
throw new office_addin_usage_data_1.ExpectedError("The manifest contains an unsupported OfficeApp xsi:type.");
|
|
}
|
|
const documentWasProvided = document && document !== "";
|
|
const templatePath = documentWasProvided ? path.resolve(document) : getTemplatePath(app, addInType);
|
|
if (!templatePath) {
|
|
throw new office_addin_usage_data_1.ExpectedError(`Sideload is not supported for apptype: ${addInType}.`);
|
|
}
|
|
const appName = (0, office_addin_manifest_1.getOfficeAppName)(app);
|
|
const extension = path.extname(templatePath);
|
|
const pathToWrite = makePathUnique(path.join(os.tmpdir(), `${appName} add-in ${manifest.id}${extension}`), true);
|
|
if (!documentWasProvided) {
|
|
const webExtensionPath = getWebExtensionPath(app, addInType);
|
|
if (!webExtensionPath) {
|
|
throw new office_addin_usage_data_1.ExpectedError("Don't know the webextension path.");
|
|
}
|
|
// replace the placeholder id and version
|
|
const templateZip = new AdmZip(templatePath);
|
|
const outZip = new AdmZip();
|
|
const extEntry = templateZip.getEntry(webExtensionPath);
|
|
if (!extEntry) {
|
|
throw new office_addin_usage_data_1.ExpectedError("webextension was not found.");
|
|
}
|
|
const webExtensionXml = templateZip
|
|
.readAsText(extEntry)
|
|
.replace(/00000000-0000-0000-0000-000000000000/g, manifest.id)
|
|
.replace(/1.0.0.0/g, manifest.version);
|
|
const webExtensionFolderPath = webExtensionPath.substring(0, webExtensionPath.lastIndexOf("/"));
|
|
templateZip.getEntries().forEach(function (entry) {
|
|
var data = entry.getData();
|
|
if (entry == extEntry && manifest.manifestType == office_addin_manifest_1.ManifestType.XML) {
|
|
data = Buffer.from(webExtensionXml);
|
|
}
|
|
// If manifestType is JSON, remove the web extension folder
|
|
if (!entry.entryName.startsWith(webExtensionFolderPath) || manifest.manifestType !== office_addin_manifest_1.ManifestType.JSON) {
|
|
outZip.addFile(entry.entryName, data, entry.comment, entry.attr);
|
|
}
|
|
});
|
|
// Write the file
|
|
yield outZip.writeZipPromise(pathToWrite);
|
|
}
|
|
else {
|
|
yield fs.promises.copyFile(templatePath, pathToWrite);
|
|
}
|
|
return pathToWrite;
|
|
});
|
|
}
|
|
exports.generateSideloadFile = generateSideloadFile;
|
|
/**
|
|
* Create an Office document url with query params which can be opened
|
|
* to register an Office add-in in Office Online.
|
|
* @param manifestPath Path to the manifest file for the Office Add-in.
|
|
* @param documentUrl Office Online document url
|
|
* @param isTest Indicates whether to append test query param to suppress Office Online dialogs.
|
|
* @returns Document url with query params appended.
|
|
*/
|
|
function generateSideloadUrl(manifestFileName, manifest, documentUrl, isTest = false) {
|
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
const testQueryParam = "&wdaddintest=true";
|
|
if (!manifest.id) {
|
|
throw new office_addin_usage_data_1.ExpectedError("The manifest does not contain the id for the add-in.");
|
|
}
|
|
if (manifest.defaultSettings === undefined || manifest.defaultSettings.sourceLocation === undefined) {
|
|
throw new office_addin_usage_data_1.ExpectedError("The manifest does not contain the SourceLocation for the add-in");
|
|
}
|
|
const sourceLocationUrl = new URL(manifest.defaultSettings.sourceLocation);
|
|
if (sourceLocationUrl.protocol.indexOf("https") === -1) {
|
|
throw new office_addin_usage_data_1.ExpectedError("The SourceLocation in the manifest does not use the HTTPS protocol.");
|
|
}
|
|
if (sourceLocationUrl.host.indexOf("localhost") === -1 && sourceLocationUrl.host.indexOf("127.0.0.1") === -1) {
|
|
throw new office_addin_usage_data_1.ExpectedError("The hostname specified by the SourceLocation in the manifest is not supported for sideload. The hostname should be 'localhost' or 127.0.0.1.");
|
|
}
|
|
let queryParms = `&wdaddindevserverport=${sourceLocationUrl.port}&wdaddinmanifestfile=${manifestFileName}&wdaddinmanifestguid=${manifest.id}`;
|
|
if (isTest) {
|
|
queryParms = `${queryParms}${testQueryParam}`;
|
|
}
|
|
return `${documentUrl}${queryParms}`;
|
|
});
|
|
}
|
|
exports.generateSideloadUrl = generateSideloadUrl;
|
|
/**
|
|
* Returns the path to the document used as a template for sideloading,
|
|
* or undefined if sideloading is not supported.
|
|
* @param app Specifies the Office app.
|
|
* @param addInType Specifies the type of add-in.
|
|
*/
|
|
function getTemplatePath(app, addInType) {
|
|
switch (app) {
|
|
case office_addin_manifest_1.OfficeApp.Excel:
|
|
switch (addInType) {
|
|
case office_addin_manifest_1.AddInType.Content:
|
|
return path.resolve(__dirname, "../templates/ExcelWorkbookWithContent.xlsx");
|
|
case office_addin_manifest_1.AddInType.TaskPane:
|
|
return path.resolve(__dirname, "../templates/ExcelWorkbookWithTaskPane.xlsx");
|
|
}
|
|
break;
|
|
case office_addin_manifest_1.OfficeApp.PowerPoint:
|
|
switch (addInType) {
|
|
case office_addin_manifest_1.AddInType.Content:
|
|
return path.resolve(__dirname, "../templates/PowerPointPresentationWithContent.pptx");
|
|
case office_addin_manifest_1.AddInType.TaskPane:
|
|
return path.resolve(__dirname, "../templates/PowerPointPresentationWithTaskPane.pptx");
|
|
}
|
|
break;
|
|
case office_addin_manifest_1.OfficeApp.Word:
|
|
switch (addInType) {
|
|
case office_addin_manifest_1.AddInType.TaskPane:
|
|
return path.resolve(__dirname, "../templates/WordDocumentWithTaskPane.docx");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
exports.getTemplatePath = getTemplatePath;
|
|
/**
|
|
* Returns the web extension path in the sideload document.
|
|
* @param app Specifies the Office app.
|
|
* @param addInType Specifies the type of add-in.
|
|
*/
|
|
function getWebExtensionPath(app, addInType) {
|
|
switch (app) {
|
|
case office_addin_manifest_1.OfficeApp.Excel:
|
|
return "xl/webextensions/webextension.xml";
|
|
case office_addin_manifest_1.OfficeApp.PowerPoint:
|
|
switch (addInType) {
|
|
case office_addin_manifest_1.AddInType.Content:
|
|
return "ppt/slides/udata/data.xml";
|
|
case office_addin_manifest_1.AddInType.TaskPane:
|
|
return "ppt/webextensions/webextension.xml";
|
|
}
|
|
break;
|
|
case office_addin_manifest_1.OfficeApp.Word:
|
|
return "word/webextensions/webextension.xml";
|
|
}
|
|
}
|
|
function isSideloadingSupportedForDesktopHost(app) {
|
|
if (app === office_addin_manifest_1.OfficeApp.Excel ||
|
|
(app === office_addin_manifest_1.OfficeApp.Outlook && process.platform === "win32") ||
|
|
app === office_addin_manifest_1.OfficeApp.PowerPoint ||
|
|
app === office_addin_manifest_1.OfficeApp.Word) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
function isSideloadingSupportedForWebHost(app) {
|
|
if (app === office_addin_manifest_1.OfficeApp.Excel || app === office_addin_manifest_1.OfficeApp.PowerPoint || app === office_addin_manifest_1.OfficeApp.Project || app === office_addin_manifest_1.OfficeApp.Word) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
function hasOfficeVersion(targetVersion, currentVersion) {
|
|
return semver.gte(currentVersion, targetVersion);
|
|
}
|
|
function getOutlookVersion() {
|
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
try {
|
|
const key = new registry.RegistryKey(`HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Office\\ClickToRun\\Configuration`);
|
|
const outlookInstallVersion = yield registry.getStringValue(key, "ClientVersionToReport");
|
|
const outlookSmallerVersion = outlookInstallVersion === null || outlookInstallVersion === void 0 ? void 0 : outlookInstallVersion.split(`.`, 3).join(`.`);
|
|
return outlookSmallerVersion;
|
|
}
|
|
catch (err) {
|
|
return undefined;
|
|
}
|
|
});
|
|
}
|
|
function getOutlookExePath() {
|
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
try {
|
|
const OutlookInstallPathRegistryKey = `HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\OUTLOOK.EXE`;
|
|
const key = new registry.RegistryKey(`${OutlookInstallPathRegistryKey}`);
|
|
const outlookExePath = yield registry.getStringValue(key, "");
|
|
if (!outlookExePath) {
|
|
throw new Error("Outlook.exe registry empty");
|
|
}
|
|
return outlookExePath;
|
|
}
|
|
catch (err) {
|
|
const errorMessage = `Unable to find Outlook install location: \n${err}`;
|
|
throw new Error(errorMessage);
|
|
}
|
|
});
|
|
}
|
|
/**
|
|
* Given a file path, returns a unique file path where the file doesn't exist by
|
|
* appending a period and a numeric suffix, starting from 2.
|
|
* @param tryToDelete If true, first try to delete the file if it exists.
|
|
*/
|
|
function makePathUnique(originalPath, tryToDelete = false) {
|
|
let currentPath = originalPath;
|
|
let parsedPath = null;
|
|
let suffix = 1;
|
|
while (fs.existsSync(currentPath)) {
|
|
let deleted = false;
|
|
if (tryToDelete) {
|
|
try {
|
|
fs.unlinkSync(currentPath);
|
|
deleted = true;
|
|
}
|
|
catch (err) {
|
|
// no error (file is in use)
|
|
}
|
|
}
|
|
if (!deleted) {
|
|
++suffix;
|
|
if (parsedPath == null) {
|
|
parsedPath = path.parse(originalPath);
|
|
}
|
|
currentPath = path.join(parsedPath.dir, `${parsedPath.name}.${suffix}${parsedPath.ext}`);
|
|
}
|
|
}
|
|
return currentPath;
|
|
}
|
|
/**
|
|
* Starts the Office app and loads the Office Add-in.
|
|
* @param manifestPath Path to the manifest file for the Office Add-in.
|
|
* @param app Office app to launch.
|
|
* @param canPrompt
|
|
*/
|
|
function sideloadAddIn(manifestPath, app, canPrompt = false, appType, document, registration) {
|
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
try {
|
|
if (appType === undefined) {
|
|
appType = appType_1.AppType.Desktop;
|
|
}
|
|
const manifest = yield office_addin_manifest_1.OfficeAddinManifest.readManifestFile(manifestPath);
|
|
const appsInManifest = (0, office_addin_manifest_1.getOfficeAppsForManifestHosts)(manifest.hosts);
|
|
if (app) {
|
|
if (appsInManifest.indexOf(app) < 0) {
|
|
throw new office_addin_usage_data_1.ExpectedError(`The Office Add-in manifest does not support ${(0, office_addin_manifest_1.getOfficeAppName)(app)}.`);
|
|
}
|
|
}
|
|
else {
|
|
switch (appsInManifest.length) {
|
|
case 0:
|
|
throw new office_addin_usage_data_1.ExpectedError("The manifest does not support any Office apps.");
|
|
case 1:
|
|
app = appsInManifest[0];
|
|
break;
|
|
default:
|
|
if (canPrompt) {
|
|
app = yield (0, prompt_1.chooseOfficeApp)(appsInManifest);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (!app) {
|
|
throw new office_addin_usage_data_1.ExpectedError("Please specify the Office app.");
|
|
}
|
|
switch (appType) {
|
|
case appType_1.AppType.Desktop:
|
|
yield (0, dev_settings_1.registerAddIn)(manifestPath, registration);
|
|
yield launchDesktopApp(app, manifest, document);
|
|
break;
|
|
case appType_1.AppType.Web: {
|
|
if (!document) {
|
|
throw new office_addin_usage_data_1.ExpectedError(`For sideload to web, you need to specify a document url.`);
|
|
}
|
|
yield launchWebApp(app, manifestPath, manifest, document);
|
|
break;
|
|
}
|
|
default:
|
|
throw new office_addin_usage_data_1.ExpectedError("Sideload is not supported for the specified app type.");
|
|
}
|
|
defaults_1.usageDataObject.reportSuccess("sideloadAddIn()");
|
|
}
|
|
catch (err) {
|
|
defaults_1.usageDataObject.reportException("sideloadAddIn()", err);
|
|
throw err;
|
|
}
|
|
});
|
|
}
|
|
exports.sideloadAddIn = sideloadAddIn;
|
|
function launchDesktopApp(app, manifest, document) {
|
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
if (!isSideloadingSupportedForDesktopHost(app)) {
|
|
throw new office_addin_usage_data_1.ExpectedError(`Sideload to the ${(0, office_addin_manifest_1.getOfficeAppName)(app)} app is not supported.`);
|
|
}
|
|
// for Outlook, open Outlook.exe; for other Office apps, open the document
|
|
if (app == office_addin_manifest_1.OfficeApp.Outlook) {
|
|
const version = yield getOutlookVersion();
|
|
if (version && !hasOfficeVersion("16.0.13709", version)) {
|
|
throw new office_addin_usage_data_1.ExpectedError(`The current version of Outlook does not support sideload. Please use version 16.0.13709 or greater.`);
|
|
}
|
|
yield launchApp(app, yield getOutlookExePath());
|
|
}
|
|
else {
|
|
yield launchApp(app, yield generateSideloadFile(app, manifest, document));
|
|
}
|
|
});
|
|
}
|
|
function launchWebApp(app, manifestPath, manifest, document) {
|
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
if (!isSideloadingSupportedForWebHost(app)) {
|
|
throw new office_addin_usage_data_1.ExpectedError(`Sideload to the ${(0, office_addin_manifest_1.getOfficeAppName)(app)} web app is not supported.`);
|
|
}
|
|
const manifestFileName = path.basename(manifestPath);
|
|
const isTest = process.env.WEB_SIDELOAD_TEST !== undefined;
|
|
yield launchApp(app, yield generateSideloadUrl(manifestFileName, manifest, document, isTest));
|
|
});
|
|
}
|
|
function launchApp(app, sideloadFile) {
|
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
console.log(`Launching ${app} via ${sideloadFile}`);
|
|
if (sideloadFile) {
|
|
if (app === office_addin_manifest_1.OfficeApp.Outlook) {
|
|
// put the Outlook.exe path in quotes if it contains spaces
|
|
if (sideloadFile.indexOf(" ") >= 0) {
|
|
sideloadFile = `"${sideloadFile}"`;
|
|
}
|
|
(0, process_1.startDetachedProcess)(sideloadFile);
|
|
}
|
|
else {
|
|
yield open(sideloadFile, { wait: false });
|
|
}
|
|
}
|
|
});
|
|
}
|
|
//# sourceMappingURL=sideload.js.map
|