diff --git a/.gitignore b/.gitignore index 9f919ad..146bc82 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ dist node_modules +src/**/*.html *lock* \ No newline at end of file diff --git a/package.json b/package.json index 4c32b5a..5b619cc 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "adm-zip": "^0.5.18", "datatables.net": "^3.0.0", "datatables.net-bs5": "^3.0.0", - "dattatable": "^2.11.76", + "dattatable": "^2.11.81", "exceljs": "^4.4.0", "fast-xml-parser": "^5.9.3", "gd-sprest-bs": "^10.15.91", diff --git a/spfx/src/webparts/siteAdmin/SiteAdminWebPart.ts b/spfx/src/webparts/siteAdmin/SiteAdminWebPart.ts index 0c3a039..7abaeaf 100644 --- a/spfx/src/webparts/siteAdmin/SiteAdminWebPart.ts +++ b/spfx/src/webparts/siteAdmin/SiteAdminWebPart.ts @@ -8,28 +8,62 @@ import { BaseClientSideWebPart, WebPartContext } from '@microsoft/sp-webpart-bas import type { IReadonlyTheme } from '@microsoft/sp-component-base'; import * as strings from 'SiteAdminWebPartStrings'; -// Include the asset files for the page generator +// Images to include in the SPFx package const imageReferences = [ - require("./assets/956444907.png"), + require("./assets/4222049231.png"), + require("./assets/34722779.png"), + require("./assets/82759635.png"), + require("./assets/178240801.png"), + require("./assets/394511437.png"), + require("./assets/459558559.png"), require("./assets/704156399.png"), + require("./assets/734992370.png"), + require("./assets/782453716.png"), + require("./assets/860781773.png"), + require("./assets/878621426.png"), + require("./assets/926485291.png"), + require("./assets/956444907.png"), + require("./assets/1004145298.png"), + require("./assets/1034045247.png"), + require("./assets/1036008522.png"), + require("./assets/1051374010.png"), + require("./assets/1366143702.png"), + require("./assets/1392219256.png"), require("./assets/1439995666.png"), - require("./assets/2403381788.png"), + require("./assets/1539840079.png"), + require("./assets/1753774094.png"), + require("./assets/1920538907.png"), + require("./assets/1937731342.png"), + require("./assets/1952315463.png"), require("./assets/2009954068.png"), - require("./assets/394511437.png"), - require("./assets/178240801.png"), + require("./assets/2243122120.png"), + require("./assets/2299432232.png"), + require("./assets/2403381788.png"), require("./assets/2492974796.png"), - require("./assets/878621426.png"), - require("./assets/3639654909.png"), + require("./assets/2498076700.png"), + require("./assets/2892564598.png"), + require("./assets/2897641524.png"), + require("./assets/2928770881.png"), + require("./assets/2930042662.png"), + require("./assets/2978296301.png"), + require("./assets/3004706479.png"), + require("./assets/3007540558.png"), + require("./assets/3039756776.png"), + require("./assets/3194998129.png"), + require("./assets/3249223301.png"), require("./assets/3423846637.png"), - require("./assets/734992370.png"), + require("./assets/3510901274.png"), + require("./assets/3517625209.png"), + require("./assets/3539902807.png"), + require("./assets/3602715702.png"), + require("./assets/3608935117.png"), require("./assets/3614134656.png"), - require("./assets/1004145298.png"), + require("./assets/3639654909.png"), + require("./assets/3672734863.png"), require("./assets/3707825808.png"), - require("./assets/3194998129.png"), - require("./assets/782453716.png"), - require("./assets/926485291.png"), - require("./assets/2498076700.png"), - require("./assets/3608935117.png"), + require("./assets/3772245627.png"), + require("./assets/3795830073.png"), + require("./assets/4007861782.png"), require("./assets/4193189816.png") ] diff --git a/spfx/src/webparts/siteAdmin/assets/1034045247.png b/spfx/src/webparts/siteAdmin/assets/1034045247.png new file mode 100644 index 0000000..5e97715 Binary files /dev/null and b/spfx/src/webparts/siteAdmin/assets/1034045247.png differ diff --git a/spfx/src/webparts/siteAdmin/assets/1036008522.png b/spfx/src/webparts/siteAdmin/assets/1036008522.png new file mode 100644 index 0000000..8bf0ff3 Binary files /dev/null and b/spfx/src/webparts/siteAdmin/assets/1036008522.png differ diff --git a/spfx/src/webparts/siteAdmin/assets/1051374010.png b/spfx/src/webparts/siteAdmin/assets/1051374010.png new file mode 100644 index 0000000..9f12187 Binary files /dev/null and b/spfx/src/webparts/siteAdmin/assets/1051374010.png differ diff --git a/spfx/src/webparts/siteAdmin/assets/1366143702.png b/spfx/src/webparts/siteAdmin/assets/1366143702.png new file mode 100644 index 0000000..2ab0358 Binary files /dev/null and b/spfx/src/webparts/siteAdmin/assets/1366143702.png differ diff --git a/spfx/src/webparts/siteAdmin/assets/1392219256.png b/spfx/src/webparts/siteAdmin/assets/1392219256.png new file mode 100644 index 0000000..0f472eb Binary files /dev/null and b/spfx/src/webparts/siteAdmin/assets/1392219256.png differ diff --git a/spfx/src/webparts/siteAdmin/assets/1539840079.png b/spfx/src/webparts/siteAdmin/assets/1539840079.png new file mode 100644 index 0000000..ca19c1a Binary files /dev/null and b/spfx/src/webparts/siteAdmin/assets/1539840079.png differ diff --git a/spfx/src/webparts/siteAdmin/assets/1753774094.png b/spfx/src/webparts/siteAdmin/assets/1753774094.png new file mode 100644 index 0000000..4ab118c Binary files /dev/null and b/spfx/src/webparts/siteAdmin/assets/1753774094.png differ diff --git a/spfx/src/webparts/siteAdmin/assets/1920538907.png b/spfx/src/webparts/siteAdmin/assets/1920538907.png new file mode 100644 index 0000000..f880315 Binary files /dev/null and b/spfx/src/webparts/siteAdmin/assets/1920538907.png differ diff --git a/spfx/src/webparts/siteAdmin/assets/1937731342.png b/spfx/src/webparts/siteAdmin/assets/1937731342.png new file mode 100644 index 0000000..f6bead2 Binary files /dev/null and b/spfx/src/webparts/siteAdmin/assets/1937731342.png differ diff --git a/spfx/src/webparts/siteAdmin/assets/1952315463.png b/spfx/src/webparts/siteAdmin/assets/1952315463.png new file mode 100644 index 0000000..21f3084 Binary files /dev/null and b/spfx/src/webparts/siteAdmin/assets/1952315463.png differ diff --git a/spfx/src/webparts/siteAdmin/assets/2243122120.png b/spfx/src/webparts/siteAdmin/assets/2243122120.png new file mode 100644 index 0000000..03338e8 Binary files /dev/null and b/spfx/src/webparts/siteAdmin/assets/2243122120.png differ diff --git a/spfx/src/webparts/siteAdmin/assets/2299432232.png b/spfx/src/webparts/siteAdmin/assets/2299432232.png new file mode 100644 index 0000000..1f098d9 Binary files /dev/null and b/spfx/src/webparts/siteAdmin/assets/2299432232.png differ diff --git a/spfx/src/webparts/siteAdmin/assets/2892564598.png b/spfx/src/webparts/siteAdmin/assets/2892564598.png new file mode 100644 index 0000000..e266e14 Binary files /dev/null and b/spfx/src/webparts/siteAdmin/assets/2892564598.png differ diff --git a/spfx/src/webparts/siteAdmin/assets/2897641524.png b/spfx/src/webparts/siteAdmin/assets/2897641524.png new file mode 100644 index 0000000..d92c450 Binary files /dev/null and b/spfx/src/webparts/siteAdmin/assets/2897641524.png differ diff --git a/spfx/src/webparts/siteAdmin/assets/2928770881.png b/spfx/src/webparts/siteAdmin/assets/2928770881.png new file mode 100644 index 0000000..7ef20f2 Binary files /dev/null and b/spfx/src/webparts/siteAdmin/assets/2928770881.png differ diff --git a/spfx/src/webparts/siteAdmin/assets/2930042662.png b/spfx/src/webparts/siteAdmin/assets/2930042662.png new file mode 100644 index 0000000..0c0a2da Binary files /dev/null and b/spfx/src/webparts/siteAdmin/assets/2930042662.png differ diff --git a/spfx/src/webparts/siteAdmin/assets/2978296301.png b/spfx/src/webparts/siteAdmin/assets/2978296301.png new file mode 100644 index 0000000..9acce69 Binary files /dev/null and b/spfx/src/webparts/siteAdmin/assets/2978296301.png differ diff --git a/spfx/src/webparts/siteAdmin/assets/3004706479.png b/spfx/src/webparts/siteAdmin/assets/3004706479.png new file mode 100644 index 0000000..6637c77 Binary files /dev/null and b/spfx/src/webparts/siteAdmin/assets/3004706479.png differ diff --git a/spfx/src/webparts/siteAdmin/assets/3007540558.png b/spfx/src/webparts/siteAdmin/assets/3007540558.png new file mode 100644 index 0000000..b5a9db1 Binary files /dev/null and b/spfx/src/webparts/siteAdmin/assets/3007540558.png differ diff --git a/spfx/src/webparts/siteAdmin/assets/3039756776.png b/spfx/src/webparts/siteAdmin/assets/3039756776.png new file mode 100644 index 0000000..d5bf58c Binary files /dev/null and b/spfx/src/webparts/siteAdmin/assets/3039756776.png differ diff --git a/spfx/src/webparts/siteAdmin/assets/3249223301.png b/spfx/src/webparts/siteAdmin/assets/3249223301.png new file mode 100644 index 0000000..6156dee Binary files /dev/null and b/spfx/src/webparts/siteAdmin/assets/3249223301.png differ diff --git a/spfx/src/webparts/siteAdmin/assets/34722779.png b/spfx/src/webparts/siteAdmin/assets/34722779.png new file mode 100644 index 0000000..4ed5adb Binary files /dev/null and b/spfx/src/webparts/siteAdmin/assets/34722779.png differ diff --git a/spfx/src/webparts/siteAdmin/assets/3510901274.png b/spfx/src/webparts/siteAdmin/assets/3510901274.png new file mode 100644 index 0000000..13bc577 Binary files /dev/null and b/spfx/src/webparts/siteAdmin/assets/3510901274.png differ diff --git a/spfx/src/webparts/siteAdmin/assets/3517625209.png b/spfx/src/webparts/siteAdmin/assets/3517625209.png new file mode 100644 index 0000000..4db1e36 Binary files /dev/null and b/spfx/src/webparts/siteAdmin/assets/3517625209.png differ diff --git a/spfx/src/webparts/siteAdmin/assets/3539902807.png b/spfx/src/webparts/siteAdmin/assets/3539902807.png new file mode 100644 index 0000000..3ff0f8f Binary files /dev/null and b/spfx/src/webparts/siteAdmin/assets/3539902807.png differ diff --git a/spfx/src/webparts/siteAdmin/assets/3602715702.png b/spfx/src/webparts/siteAdmin/assets/3602715702.png new file mode 100644 index 0000000..1e037d1 Binary files /dev/null and b/spfx/src/webparts/siteAdmin/assets/3602715702.png differ diff --git a/spfx/src/webparts/siteAdmin/assets/3672734863.png b/spfx/src/webparts/siteAdmin/assets/3672734863.png new file mode 100644 index 0000000..a3c8575 Binary files /dev/null and b/spfx/src/webparts/siteAdmin/assets/3672734863.png differ diff --git a/spfx/src/webparts/siteAdmin/assets/3772245627.png b/spfx/src/webparts/siteAdmin/assets/3772245627.png new file mode 100644 index 0000000..0e44068 Binary files /dev/null and b/spfx/src/webparts/siteAdmin/assets/3772245627.png differ diff --git a/spfx/src/webparts/siteAdmin/assets/3795830073.png b/spfx/src/webparts/siteAdmin/assets/3795830073.png new file mode 100644 index 0000000..727e087 Binary files /dev/null and b/spfx/src/webparts/siteAdmin/assets/3795830073.png differ diff --git a/spfx/src/webparts/siteAdmin/assets/4007861782.png b/spfx/src/webparts/siteAdmin/assets/4007861782.png new file mode 100644 index 0000000..49f2170 Binary files /dev/null and b/spfx/src/webparts/siteAdmin/assets/4007861782.png differ diff --git a/spfx/src/webparts/siteAdmin/assets/4222049231.png b/spfx/src/webparts/siteAdmin/assets/4222049231.png new file mode 100644 index 0000000..557586d Binary files /dev/null and b/spfx/src/webparts/siteAdmin/assets/4222049231.png differ diff --git a/spfx/src/webparts/siteAdmin/assets/459558559.png b/spfx/src/webparts/siteAdmin/assets/459558559.png new file mode 100644 index 0000000..7b624ce Binary files /dev/null and b/spfx/src/webparts/siteAdmin/assets/459558559.png differ diff --git a/spfx/src/webparts/siteAdmin/assets/82759635.png b/spfx/src/webparts/siteAdmin/assets/82759635.png new file mode 100644 index 0000000..c7b2e23 Binary files /dev/null and b/spfx/src/webparts/siteAdmin/assets/82759635.png differ diff --git a/spfx/src/webparts/siteAdmin/assets/860781773.png b/spfx/src/webparts/siteAdmin/assets/860781773.png new file mode 100644 index 0000000..1914afa Binary files /dev/null and b/spfx/src/webparts/siteAdmin/assets/860781773.png differ diff --git a/src/m365Groups.ts b/src/m365Groups.ts index 4b6e990..3f86be0 100644 --- a/src/m365Groups.ts +++ b/src/m365Groups.ts @@ -84,53 +84,62 @@ export class M365Groups { // Loads the m365 group private static loadGroupById(groupId: string): PromiseLike { + let group: Types.SP.Directory.GroupOData = { id: groupId } as Types.SP.Directory.GroupOData; + // Return a promise - return new Promise((resolve, reject) => { + return new Promise((resolve) => { // Get the group information - let ds = DirectorySession().group(groupId); - ds.query({ - Select: ["calendarUrl", "displayName", "id", "isPublic", "mail"] - }).execute(group => { - // Save the group information - this._groups[group.id] = group; - }, () => { - // Error loading this group - this._errorGroupIds[groupId] = true; + let getGroupInfo = (): PromiseLike => { + return new Promise(resolve => { + DirectorySession().group(groupId).query({ + Select: ["calendarUrl", "displayName", "id", "isPublic", "mail"] + }).execute(group => { + // Update the group information + group.calendarUrl = group.calendarUrl; + group.displayName = group.displayName; + group.id = group.id; + group.isPublic = group.isPublic; + group.mail = group.mail; + + // Resolve the group information + resolve(); + }, () => { + // Error loading this group + this._errorGroupIds[groupId] = true; - // Reject the request - reject(); - }); + // Resolve the group information + resolve(); + }); + }); + } + + // Get the group members/owner information + let getGroupUsers = (member: boolean): PromiseLike => { + return new Promise(resolve => { + DirectorySession().group(groupId)[member ? "members" : "owners"]().query({ + GetAllItems: true, + Top: 5000, + Select: ["principalName", "id", "displayName", "mail"] + }).execute(users => { + // Set the users + group[member ? "members" : "owners"] = users as any; + + // Resolve the users + resolve(); + }, () => { + // Resolve the request + resolve(); + }, true); + }); + } + + // Get the group information, members and users + Promise.all([getGroupInfo(), getGroupUsers(true), getGroupUsers(false)]).then(() => { + // Set the group + this._groups[group.id] = group; - // Get the group members - ds.members().query({ - GetAllItems: true, - Top: 5000, - Select: ["principalName", "id", "displayName", "mail"] - }).execute(members => { - // Add the group information to the mapper - this._groups[groupId].members = members as any; - }, () => { - // Try the next group - resolve(null); - }, true); - - // Get the group owners - ds.owners().query({ - GetAllItems: true, - Top: 5000, - Select: ["principalName", "id", "displayName", "mail"] - }).execute(owners => { - // Add the group information to the mapper - this._groups[groupId].owners = owners as any; - }, () => { - // Try the next group - resolve(null); - }, true); - - // Wait for the requests to complete - ds.done(() => { - // Try the next group - resolve(this._groups[groupId]); + // Resolve the request + resolve(this._groups[group.id]); }); }); } diff --git a/src/page-generator/index.ts b/src/page-generator/index.ts index 6dc8f08..cbc6603 100644 --- a/src/page-generator/index.ts +++ b/src/page-generator/index.ts @@ -1,13 +1,25 @@ import { LoadingDialog, Modal } from "dattatable"; -import { Components, ContextInfo, Helper, SitePages, SPTypes, Web } from "gd-sprest-bs"; +import { Components, ContextInfo, Helper, SitePages, SPTypes, Types, Web } from "gd-sprest-bs"; import Strings from "../strings"; -import { Template } from "./template"; +import * as Templates from "./templates"; +import { } from "./templates/site-configuration"; +import { PageTemplate } from "./template"; + +// Page Mapper +const PageMapper: { [key: string]: { filename: string; title: string; template: PageTemplate; } } = { + "AppPermissions": { filename: "AppPermissions.aspx", title: "App Permissions", template: Templates.AppPermissions }, + "AuditReports": { filename: "AuditReports.aspx", title: "Audit Reports", template: Templates.AuditReports }, + "DataReadiness": { filename: "DataReadiness.aspx", title: "Data Readiness", template: Templates.DataReadiness }, + "Overview": { filename: "Overview.aspx", title: "Overview", template: Templates.Overview }, + "SiteConfiguration": { filename: "SiteConfiguration.aspx", title: "Site Configuration", template: Templates.SiteConfiguration } +} /** * Page Generator */ export class PageGenerator { private static _imageReferences: string[]; + static get ImageReferences() { return this._imageReferences; } static set ImageReferences(values: string[]) { this._imageReferences = values; } // Constructor @@ -17,39 +29,97 @@ export class PageGenerator { } // Creates the page - private createPage(fileName: string, title: string) { + private createFolder(folderName: string): PromiseLike { // Show a loading dialog - LoadingDialog.setHeader("Creating Page"); - LoadingDialog.setBody("The page is being created..."); + LoadingDialog.setHeader("Creating Folder"); + LoadingDialog.setBody("The folder is being created..."); LoadingDialog.show(); - // Create the page - SitePages.createPage(fileName, title, SPTypes.ClientSidePageLayout.Article, Strings.SourceUrl).then(page => { - // Get the folder url of the page - let pageFolderName = page.file.Name.replace('.aspx', ""); + // Return a promise + return new Promise(resolve => { + // Get the site pages + Web().Lists().getByTitle("Site Pages").RootFolder().Folders().execute(folders => { + // Parse the folders + for (let i = 0; i < folders.results.length; i++) { + let folder = folders.results[i]; - // Upload the images for the page - this.uploadImages(pageFolderName, page.item.Id).then(template => { - // Update the loading dialog - LoadingDialog.setBody("Configuring the page..."); - - // Set the content for the page - page.item.update(template.CanvasContent1).execute(() => { - // Show the page in a new tab - window.open(page.page.AbsoluteUrl, "_blank"); - - // Hide the dialogs - LoadingDialog.hide(); - Modal.hide(); - }, () => { - // Error updating the page - console.error("There was an error configuring the page."); - LoadingDialog.hide(); - - // Show an error modal in the modal - // TODO + // See if the target folder exists + if (folder.Name.toLowerCase() == folderName.toLowerCase()) { + // Resolve the request + resolve(folder); + return; + } + } + + // Create the folder + Web().Lists().getByTitle("Site Pages").RootFolder().Folders().add(folderName).execute(resolve); + }); + }); + } + + // Creates the pages + private createPages(folder: Types.SP.Folder) { + // Show a loading dialog + LoadingDialog.setHeader("Creating Pages"); + LoadingDialog.setBody("The pages are being created..."); + LoadingDialog.show(); + + // Parse the files to create + let counter = 0; + let pages = Object.keys(PageMapper); + Helper.Executor(pages, key => { + let pageInfo = PageMapper[key]; + + // Update the dialog + LoadingDialog.setHeader(`Creating Page ${++counter} of ${pages.length}`); + + // Return a promise + return new Promise(resolve => { + // Create the page + SitePages.createPage(`${folder.Name}/${pageInfo.filename}`, pageInfo.title, SPTypes.ClientSidePageLayout.Article, Strings.SourceUrl).then(page => { + // Get the folder url of the page + let pageFolderName = page.file.Name.replace('.aspx', ""); + + // Update the loading dialog + LoadingDialog.setBody("Configuring the page..."); + + // Upload the images + this.uploadImages(pageFolderName, page.item.Id, pageInfo.template).then(pageTemplate => { + // See if this is the main page + let content = pageTemplate.Content; + if (pageInfo.template == Templates.Overview) { + // Update the link references + content = content.replace(/href="\/sites\/demo\/site-admin/g, 'href="' + Strings.SourceUrl); + } + + // Set the content for the page + page.item.update({ CanvasContent1: content }).execute(() => { + // See if this is the main page + if (pageInfo.template == Templates.Overview) { + // Show the page in a new tab + window.open(page.page.AbsoluteUrl, "_blank"); + } + + // Create the next page + resolve(null); + }, () => { + // Error updating the page + console.error("There was an error configuring the page."); + LoadingDialog.hide(); + + // Show an error modal in the modal + // TODO + + // Create the next page + resolve(null); + }); + }); }); }); + }).then(() => { + // Hide the dialogs + LoadingDialog.hide(); + Modal.hide(); }); } @@ -81,30 +151,22 @@ export class PageGenerator { // Set the header Modal.setHeader("Page Generator"); + Modal.setBody(`

This tool will generate pages to help with data readiness guidance. Enter the folder name and click 'Generate'.

`); + // Render the form let form = Components.Form({ el: Modal.BodyElement, controls: [ { name: "Name", - title: "Page Name", - description: "The file name of the page to create.", - type: Components.FormControlTypes.TextField, - isPlainText: true, - required: true, - errorMessage: "A file name is required.", - value: "Copilot-Data-Readiness" - }, - { - name: "Title", - title: "Page Title", - description: "The title displayed.", + title: "Folder Name", + description: "The folder name create in the Site Pages library.", type: Components.FormControlTypes.TextField, isPlainText: true, required: true, - errorMessage: "A title is required.", - value: "Copilot Data Readiness" - }, + errorMessage: "A folder name is required.", + value: "Site Admin Tool" + } ] }); @@ -114,7 +176,7 @@ export class PageGenerator { buttonType: Components.ButtonTypes.OutlinePrimary, tooltips: [ { - content: "Click to create the page.", + content: "Click to create the pages.", btnProps: { text: "Create", onClick: () => { @@ -122,8 +184,11 @@ export class PageGenerator { if (form.isValid()) { let values = form.getValues(); - // Create the page - this.createPage(values["Name"].split('.aspx')[0] + ".aspx", values["Title"]); + // Create the folder + this.createFolder(values["Name"]).then(folder => { + // Create the pages + this.createPages(folder); + }); } } } @@ -146,38 +211,50 @@ export class PageGenerator { } // Uploads the images for the site page - private uploadImages(pageFolderName: string, pageItemId: number): PromiseLike