diff --git a/package-lock.json b/package-lock.json index 6def9074..85939503 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1885,7 +1885,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -1906,12 +1907,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1926,17 +1929,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -2053,7 +2059,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -2065,6 +2072,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -2079,6 +2087,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -2086,12 +2095,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -2110,6 +2121,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -2190,7 +2202,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -2202,6 +2215,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -2287,7 +2301,8 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -2323,6 +2338,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -2342,6 +2358,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -2385,12 +2402,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, @@ -5483,9 +5502,9 @@ "integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=" }, "tree-kill": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.1.tgz", - "integrity": "sha512-4hjqbObwlh2dLyW4tcz0Ymw0ggoaVDMveUB9w8kFSQScdRLo0gxO9J7WFcUBo+W3C1TLdFIEwNOWebgZZ0RH9Q==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", "dev": true }, "ts-node": { diff --git a/readme.md b/readme.md index 040eadff..47d47fbf 100755 --- a/readme.md +++ b/readme.md @@ -51,6 +51,7 @@ taskpane | Task Pane add-in using HTML angular | Task Pane add-in using the Angular framework react | Task Pane add-in using the React framework excel-functions | Task Pane add-in with Excel Custom Functions +single-sign-on | Taskpane add-in supporting single-sign-on manifest | Manifest and related files for an Office Add-in - Type: String - Optional diff --git a/src/app/config/projectProperties.json b/src/app/config/projectProperties.json index 5c51ae97..13682e78 100644 --- a/src/app/config/projectProperties.json +++ b/src/app/config/projectProperties.json @@ -45,6 +45,21 @@ } } }, + "single-sign-on": { + "displayname": "Office Add-in Task Pane project supporting single sign-on", + "templates": { + "javascript": { + "repository": "https://github.com/OfficeDev/Office-Addin-TaskPane-SSO-JS", + "branch": "yo-office", + "prerelease": "yo-office-prerelease" + }, + "typescript": { + "repository": "https://github.com/OfficeDev/Office-Addin-Taskpane-SSO", + "branch": "yo-office", + "prerelease": "yo-office-prerelease" + } + } + }, "manifest": { "displayname": "Office Add-in project containing the manifest only", "templates": { @@ -94,5 +109,5 @@ "displayname": "Word", "repository": "" } - } + } } \ No newline at end of file diff --git a/src/app/config/projectsJsonData.ts b/src/app/config/projectsJsonData.ts index 9e5551f7..ab8e55cf 100644 --- a/src/app/config/projectsJsonData.ts +++ b/src/app/config/projectsJsonData.ts @@ -1,128 +1,116 @@ import * as fs from 'fs'; import * as _ from 'lodash'; -export default class projectsJsonData{ - m_projectJsonDataFile:string = '/projectProperties.json'; - m_projectJsonData; +export default class projectsJsonData { + m_projectJsonDataFile: string = '/projectProperties.json'; + m_projectJsonData; - constructor(templatePath:string){ - let jsonData = fs.readFileSync(templatePath + this.m_projectJsonDataFile); - this.m_projectJsonData = JSON.parse(jsonData.toString()); - } + constructor(templatePath: string) { + let jsonData = fs.readFileSync(templatePath + this.m_projectJsonDataFile); + this.m_projectJsonData = JSON.parse(jsonData.toString()); + } - isValidInput(input: string, isHostParam: boolean) - { - if (isHostParam) - { - for (let key in this.m_projectJsonData.hostTypes) - { - if (_.toLower(input) == key){ - return true; - } - } - return false; + isValidInput(input: string, isHostParam: boolean) { + if (isHostParam) { + for (let key in this.m_projectJsonData.hostTypes) { + if (_.toLower(input) == key) { + return true; } - else{ - for (let key in this.m_projectJsonData.projectTypes) - { - if (_.toLower(input) == key){ - return true; - } - } - return false; - } - } - - getProjectDisplayName(projectType: string){ - return this.m_projectJsonData.projectTypes[_.toLower(projectType)].displayname; - } - - getParsedProjectJsonData() - { - return this.m_projectJsonData; + } + return false; } - - getProjectTemplateNames() - { - let projectTemplates : string[] = []; - for (let key in this.m_projectJsonData.projectTypes) - { - projectTemplates.push(key); + else { + for (let key in this.m_projectJsonData.projectTypes) { + if (_.toLower(input) == key) { + return true; + } } - return projectTemplates; + return false; } - - projectBothScriptTypes (projectType: string) - { - return this.m_projectJsonData.projectTypes[_.toLower(projectType)].templates.javascript != undefined && this.m_projectJsonData.projectTypes[_.toLower(projectType)].templates.typescript != undefined; + } + + getProjectDisplayName(projectType: string) { + return this.m_projectJsonData.projectTypes[_.toLower(projectType)].displayname; + } + + getParsedProjectJsonData() { + return this.m_projectJsonData; + } + + getProjectTemplateNames() { + let projectTemplates: string[] = []; + for (let key in this.m_projectJsonData.projectTypes) { + projectTemplates.push(key); } + return projectTemplates; + } + + projectBothScriptTypes(projectType: string) { + return this.m_projectJsonData.projectTypes[_.toLower(projectType)].templates.javascript != undefined && this.m_projectJsonData.projectTypes[_.toLower(projectType)].templates.typescript != undefined; + } - getHostTemplateNames() - { - let hosts : string[] = []; - for (let key in this.m_projectJsonData.hostTypes) - { + getHostTemplateNames(isSsoProject: boolean) { + let hosts: string[] = []; + for (let key in this.m_projectJsonData.hostTypes) { + if (isSsoProject) { + if (key === 'excel' || key === 'word' || key === 'powerpoint') { + hosts.push(this.m_projectJsonData.hostTypes[key].displayname); + } + } else { hosts.push(this.m_projectJsonData.hostTypes[key].displayname); } - return hosts; } + return hosts; + } - getHostDisplayName(hostKey: string) - { - for (let key in this.m_projectJsonData.hostTypes) - { - if (_.toLower(hostKey) == key){ - return this.m_projectJsonData.hostTypes[key].displayname; - } + getHostDisplayName(hostKey: string) { + for (let key in this.m_projectJsonData.hostTypes) { + if (_.toLower(hostKey) == key) { + return this.m_projectJsonData.hostTypes[key].displayname; } - return undefined; } + return undefined; + } - getProjectTemplateRepository(projectTypeKey: string, scriptType: string) - { - for (let key in this.m_projectJsonData.projectTypes) - { - if (_.toLower(projectTypeKey) == key){ - if (projectTypeKey == 'manifest'){ - return this.m_projectJsonData.projectTypes[key].templates.manifestonly.repository; - } - else{ - return this.m_projectJsonData.projectTypes[key].templates[scriptType].repository; - } + getProjectTemplateRepository(projectTypeKey: string, scriptType: string) { + for (let key in this.m_projectJsonData.projectTypes) { + if (_.toLower(projectTypeKey) == key) { + if (projectTypeKey == 'manifest') { + return this.m_projectJsonData.projectTypes[key].templates.manifestonly.repository; + } + else { + return this.m_projectJsonData.projectTypes[key].templates[scriptType].repository; } } - return undefined; } + return undefined; + } - getProjectTemplateBranchName(projectTypeKey: string, scriptType: string, prerelease: boolean) - { - for (let key in this.m_projectJsonData.projectTypes) - { - if (_.toLower(projectTypeKey) == key){ - if (projectTypeKey == 'manifest') - { - return this.m_projectJsonData.projectTypes.manifest.templates.branch; + getProjectTemplateBranchName(projectTypeKey: string, scriptType: string, prerelease: boolean) { + for (let key in this.m_projectJsonData.projectTypes) { + if (_.toLower(projectTypeKey) == key) { + if (projectTypeKey == 'manifest') { + return this.m_projectJsonData.projectTypes.manifest.templates.branch; + } + else { + if (prerelease) { + return this.m_projectJsonData.projectTypes[key].templates[scriptType].prerelease + } else { + return this.m_projectJsonData.projectTypes[key].templates[scriptType].branch; } - else{ - if (prerelease) { - return this.m_projectJsonData.projectTypes[key].templates[scriptType].prerelease - } else { - return this.m_projectJsonData.projectTypes[key].templates[scriptType].branch; - } - } } } - return undefined; } + return undefined; + } - getProjectRepoAndBranch(projectTypeKey: string, scriptType: string, prerelease: boolean) - { - scriptType = scriptType === 'ts' ? 'typescript' : 'javascript'; - let repoBranchInfo = { repo: null, branch: null }; + getProjectRepoAndBranch(projectTypeKey: string, scriptType: string, prerelease: boolean) { + scriptType = scriptType === 'ts' ? 'typescript' : 'javascript'; + let repoBranchInfo = { repo: null, branch: null }; - repoBranchInfo.repo = this.getProjectTemplateRepository(projectTypeKey, scriptType); - repoBranchInfo.branch = (repoBranchInfo.repo) ? this.getProjectTemplateBranchName(projectTypeKey, scriptType, prerelease) : undefined; - - return repoBranchInfo; - } - } \ No newline at end of file + repoBranchInfo.repo = this.getProjectTemplateRepository(projectTypeKey, scriptType); + repoBranchInfo.branch = (repoBranchInfo.repo) ? this.getProjectTemplateBranchName(projectTypeKey, scriptType, prerelease) : undefined; + + return repoBranchInfo; + } +} \ No newline at end of file diff --git a/src/app/index.ts b/src/app/index.ts index 24133657..e2c13afc 100755 --- a/src/app/index.ts +++ b/src/app/index.ts @@ -17,9 +17,12 @@ import * as yo from 'yeoman-generator'; const childProcessExec = promisify(childProcess.exec); const excelCustomFunctions = `excel-functions`; +let isSsoProject = false; +const javascript = `JavaScript`; const manifest = 'manifest'; +const sso = 'single-sign-on'; const typescript = `TypeScript`; -const javascript = `JavaScript`; + let language; let usageDataObject: usageData.OfficeAddinUsageData; @@ -157,12 +160,18 @@ module.exports = yo.extend({ isManifestProject = true; } - /* Set isExcelFunctionsProject to true if ExcelexcelFunctions project type selected from prompt or Excel Functions was specified via the command prompt */ + /* Set isExcelFunctionsProject to true if ExcelFunctions project type selected from prompt or Excel Functions was specified via the command prompt */ if ((answerForProjectType.projectType != null && answerForProjectType.projectType) === excelCustomFunctions || (this.options.projectType != null && _.toLower(this.options.projectType) === excelCustomFunctions)) { isExcelFunctionsProject = true; } + /* Set isSsoProject to true if SSO project type selected from prompt or Single Sign-On was specified via the command prompt */ + if ((answerForProjectType.projectType != null && answerForProjectType.projectType) === sso + || (this.options.projectType != null && _.toLower(this.options.projectType) === sso)) { + isSsoProject = true; + } + let askForScriptType = [ { name: 'scriptType', @@ -193,7 +202,7 @@ module.exports = yo.extend({ message: 'Which Office client application would you like to support?', type: 'list', default: 'Excel', - choices: jsonData.getHostTemplateNames().map(host => ({ name: host, value: host })), + choices: jsonData.getHostTemplateNames(isSsoProject).map(host => ({ name: host, value: host })), when: (this.options.host == null || this.options.host != null && !jsonData.isValidInput(this.options.host, true /* isHostParam */)) && !isExcelFunctionsProject }]; @@ -218,8 +227,8 @@ module.exports = yo.extend({ usageDataObject.reportError(defaults.promptSelectionsErrorEventName, new Error('Prompting Error: ' + err)); } }, - - writing: function () { + + writing: function () { const done = this.async(); this._copyProjectFiles() .then(() => { @@ -336,10 +345,19 @@ module.exports = yo.extend({ this.log(` ${chalk.green('Congratulations!')} Your add-in has been created! Your next steps:\n`); this.log(` 1. Go the directory where your project was created:\n`); this.log(` ${chalk.bold('cd ' + this._destinationRoot)}\n`); - this.log(` 2. Start the local web server and sideload the add-in:\n`); - this.log(` ${chalk.bold('npm start')}\n`); - this.log(` 3. Open the project in VS Code:\n`); - this.log(` ${chalk.bold('code .')}\n`); + if (isSsoProject) { + this.log(` 2. Configure your SSO taskpane add-in:\n`); + this.log(` ${chalk.bold('npm run configure-sso')}\n`); + this.log(` 3. Start the local web server and sideload the add-in:\n`); + this.log(` ${chalk.bold('npm start')}\n`); + this.log(` 4. Open the project in VS Code:\n`); + this.log(` ${chalk.bold('code .')}\n`); + } else { + this.log(` 2. Start the local web server and sideload the add-in:\n`); + this.log(` ${chalk.bold('npm start')}\n`); + this.log(` 3. Open the project in VS Code:\n`); + this.log(` ${chalk.bold('code .')}\n`); + } this.log(` For more information, visit http://code.visualstudio.com.\n`); this.log(` Please visit https://docs.microsoft.com/office/dev/add-ins for more information about Office Add-ins.\n`); this.log('----------------------------------------------------------------------------------------------------------\n'); diff --git a/src/test/convert-to-single-host.ts b/src/test/convert-to-single-host.ts index 9263ff40..020b3686 100644 --- a/src/test/convert-to-single-host.ts +++ b/src/test/convert-to-single-host.ts @@ -40,7 +40,7 @@ describe('Office-Add-Taskpane-Ts projects', () => { ] let answers = { projectType: "taskpane", - scriptType: "TypeScript", + scriptType: "TypeScript", name: testProjectName, host: hosts[0] }; @@ -205,3 +205,94 @@ describe('Office-Add-Taskpane-React-Ts project', () => { }); }); }); + +// Test to verify converting a project to a single host +// for SSO Typescript project using Excel host +describe('Office-Add-Taskpane-SSO-TS project', () => { + const expectedFiles = [ + packageJsonFile, + manifestFile, + '.ENV', + 'src/taskpane/taskpane.ts', + 'src/taskpane/taskpane.html', + 'src/taskpane/taskpane.css', + 'src/helpers/fallbackauthdialog.html', + 'src/helpers/fallbackauthdialog.ts', + 'src/helpers/fallbackauthhelper.ts', + 'src/helpers/ssoauthhelper.ts' + + ] + const unexpectedFiles = [ + 'src/taskpane/excel.ts', + 'src/taskpane/word.ts', + 'src/taskpane/powerpoint.ts', + 'manifest.excel.xml', + 'manifest.word.xml', + 'manifest.powerpoint.xml' + ] + let answers = { + projectType: "single-sign-on", + scriptType: "TypeScript", + name: "SSOTypeScriptProject", + host: hosts[0] + }; + + describe('Office-Add-Taskpane-SSO-TS project', () => { + before((done) => { + helpers.run(path.join(__dirname, '../app')).withOptions({ 'test': true }).withPrompts(answers).on('end', done); + }); + + it('creates expected files', (done) => { + assert.file(expectedFiles); + assert.noFile(unexpectedFiles); + assert.noFile(unexpectedManifestFiles); + done(); + }); + }); +}); + +// Test to verify converting a project to a single host +// for SSO JavaScript project using PowerPoint host +describe('Office-Add-Taskpane-SSO-JS project', () => { + const expectedFiles = [ + packageJsonFile, + manifestFile, + '.ENV', + 'src/taskpane/taskpane.js', + 'src/taskpane/taskpane.html', + 'src/taskpane/taskpane.css', + 'src/helpers/documenthelper.js', + 'src/helpers/fallbackauthdialog.html', + 'src/helpers/fallbackauthdialog.js', + 'src/helpers/fallbackauthhelper.js', + 'src/helpers/ssoauthhelper.js' + + ] + const unexpectedFiles = [ + 'src/taskpane/excel.js', + 'src/taskpane/word.js', + 'src/taskpane/powerpoint.js', + 'manifest.excel.xml', + 'manifest.word.xml', + 'manifest.powerpoint.xml' + ] + let answers = { + projectType: "single-sign-on", + scriptType: "JavaScript", + name: "SSOJavaScriptProject", + host: hosts[3] + }; + + describe('Office-Add-Taskpane-SSO-JS project', () => { + before((done) => { + helpers.run(path.join(__dirname, '../app')).withOptions({ 'test': true }).withPrompts(answers).on('end', done); + }); + + it('creates expected files', (done) => { + assert.file(expectedFiles); + assert.noFile(unexpectedFiles); + assert.noFile(unexpectedManifestFiles); + done(); + }); + }); +});