@pnp/cli-microsoft365
Version:
Manage Microsoft 365 and SharePoint Framework projects on any platform
422 lines • 19.7 kB
JavaScript
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var _SpoPageClientSideWebPartAddCommand_instances, _SpoPageClientSideWebPartAddCommand_initTelemetry, _SpoPageClientSideWebPartAddCommand_initOptions, _SpoPageClientSideWebPartAddCommand_initValidators, _SpoPageClientSideWebPartAddCommand_initOptionSets;
import { v4 } from 'uuid';
import request from '../../../../request.js';
import { formatting } from '../../../../utils/formatting.js';
import { validation } from '../../../../utils/validation.js';
import SpoCommand from '../../../base/SpoCommand.js';
import commands from '../../commands.js';
import { StandardWebPartUtils } from '../../StandardWebPartTypes.js';
class SpoPageClientSideWebPartAddCommand extends SpoCommand {
get name() {
return commands.PAGE_CLIENTSIDEWEBPART_ADD;
}
get description() {
return 'Adds a client-side web part to a modern page';
}
constructor() {
super();
_SpoPageClientSideWebPartAddCommand_instances.add(this);
__classPrivateFieldGet(this, _SpoPageClientSideWebPartAddCommand_instances, "m", _SpoPageClientSideWebPartAddCommand_initTelemetry).call(this);
__classPrivateFieldGet(this, _SpoPageClientSideWebPartAddCommand_instances, "m", _SpoPageClientSideWebPartAddCommand_initOptions).call(this);
__classPrivateFieldGet(this, _SpoPageClientSideWebPartAddCommand_instances, "m", _SpoPageClientSideWebPartAddCommand_initValidators).call(this);
__classPrivateFieldGet(this, _SpoPageClientSideWebPartAddCommand_instances, "m", _SpoPageClientSideWebPartAddCommand_initOptionSets).call(this);
}
async commandAction(logger, args) {
let canvasContent;
let layoutWebpartsContent = "";
let authorByline = [""];
let bannerImageUrl = "";
let description = "";
let title = "";
let topicHeader = "";
const pageData = {};
let pageFullName = args.options.pageName;
if (args.options.pageName.indexOf('.aspx') < 0) {
pageFullName += '.aspx';
}
if (this.verbose) {
await logger.logToStderr(`Retrieving page information...`);
}
try {
let requestOptions = {
url: `${args.options.webUrl}/_api/sitepages/pages/GetByUrl('sitepages/${formatting.encodeQueryParameter(pageFullName)}')`,
headers: {
'accept': 'application/json;odata=nometadata'
},
responseType: 'json'
};
let page = await request.get(requestOptions);
if (!page.IsPageCheckedOutToCurrentUser) {
const requestOptions = {
url: `${args.options.webUrl}/_api/sitepages/pages/GetByUrl('sitepages/${formatting.encodeQueryParameter(pageFullName)}')/checkoutpage`,
headers: {
'accept': 'application/json;odata=nometadata'
},
responseType: 'json'
};
page = await request.post(requestOptions);
}
if (page) {
layoutWebpartsContent = page.LayoutWebpartsContent;
authorByline = page.AuthorByline;
bannerImageUrl = page.BannerImageUrl;
description = page.Description;
title = page.Title;
topicHeader = page.TopicHeader;
}
if (this.verbose) {
await logger.logToStderr(`Retrieving definition for web part ${args.options.webPartId ||
args.options.standardWebPart}...`);
}
canvasContent = JSON.parse(page.CanvasContent1 || "[{\"controlType\":0,\"pageSettingsSlice\":{\"isDefaultDescription\":true,\"isDefaultThumbnail\":true}}]");
// Get the WebPart according to arguments
const webPart = await this.getWebPart(logger, args);
if (this.verbose) {
await logger.logToStderr(`Setting client-side web part layout and properties...`);
}
await this.setWebPartProperties(webPart, logger, args);
const control = this.getCorrectControl(canvasContent, args);
const controlIndex = canvasContent.indexOf(control);
const zoneIndex = control.position.zoneIndex;
const column = control.position.sectionIndex;
const webPartControl = this.extend({
controlType: 3,
displayMode: 2,
id: webPart.id,
position: Object.assign({}, control.position),
webPartId: webPart.webPartId,
emphasis: {},
zoneGroupMetadata: control.zoneGroupMetadata
}, webPart);
if (!control.controlType) {
// it's an empty column so we need to replace it with the web part
// ignore the specified order
webPartControl.position.controlIndex = 1;
canvasContent.splice(controlIndex, 1, webPartControl);
}
else {
// it's a web part so we should find out where to put the web part in
// the array of page controls
// get web part index values to determine where to add the current
// web part
const controlIndices = canvasContent
.filter(c => c.position &&
c.position.zoneIndex === zoneIndex &&
c.position.sectionIndex === column)
.map(c => c.position.controlIndex)
.sort((a, b) => a - b);
// get the controlIndex of the web part before each the new web part
// should be added
if (!args.options.order ||
args.options.order > controlIndices.length) {
const controlIndex = controlIndices.pop();
const webPartIndex = canvasContent
.findIndex(c => c.position &&
c.position.zoneIndex === zoneIndex &&
c.position.sectionIndex === column &&
c.position.controlIndex === controlIndex);
canvasContent.splice(webPartIndex + 1, 0, webPartControl);
}
else {
const controlIndex = controlIndices[args.options.order - 1];
const webPartIndex = canvasContent
.findIndex(c => c.position &&
c.position.zoneIndex === zoneIndex &&
c.position.sectionIndex === column &&
c.position.controlIndex === controlIndex);
canvasContent.splice(webPartIndex, 0, webPartControl);
}
// reset order to ensure there are no gaps
const webPartsInColumn = canvasContent
.filter(c => c.position &&
c.position.zoneIndex === zoneIndex &&
c.position.sectionIndex === column);
let i = 1;
webPartsInColumn.forEach(w => {
w.position.controlIndex = i++;
});
}
if (authorByline) {
pageData.AuthorByline = authorByline;
}
if (bannerImageUrl) {
pageData.BannerImageUrl = bannerImageUrl;
}
if (description) {
pageData.Description = description;
}
if (title) {
pageData.Title = title;
}
if (topicHeader) {
pageData.TopicHeader = topicHeader;
}
if (layoutWebpartsContent) {
pageData.LayoutWebpartsContent = layoutWebpartsContent;
}
if (canvasContent) {
pageData.CanvasContent1 = JSON.stringify(canvasContent);
}
requestOptions = {
url: `${args.options.webUrl}/_api/sitepages/pages/GetByUrl('sitepages/${formatting.encodeQueryParameter(pageFullName)}')/SavePageAsDraft`,
headers: {
'X-HTTP-Method': 'MERGE',
'IF-MATCH': '*',
'content-type': 'application/json;odata=nometadata',
accept: 'application/json;odata=nometadata'
},
data: pageData,
responseType: 'json'
};
await request.post(requestOptions);
}
catch (err) {
this.handleRejectedODataJsonPromise(err);
}
}
async getWebPart(logger, args) {
const standardWebPart = args.options.standardWebPart;
const webPartId = standardWebPart
? StandardWebPartUtils.getWebPartId(standardWebPart)
: args.options.webPartId;
if (this.debug) {
await logger.logToStderr(`StandardWebPart: ${standardWebPart}`);
await logger.logToStderr(`WebPartId: ${webPartId}`);
}
const requestOptions = {
url: `${args.options.webUrl}/_api/web/getclientsidewebparts()`,
headers: {
accept: 'application/json;odata=nometadata'
},
responseType: 'json'
};
const response = await request.get(requestOptions);
const webPartDefinition = response.value.filter((c) => c.Id.toLowerCase() === webPartId.toLowerCase() || c.Id.toLowerCase() === `{${webPartId.toLowerCase()}}`);
if (webPartDefinition.length === 0) {
throw new Error(`There is no available WebPart with Id ${webPartId}.`);
}
if (this.debug) {
await logger.logToStderr('WebPart definition:');
await logger.logToStderr(webPartDefinition);
await logger.logToStderr('');
}
if (this.verbose) {
await logger.logToStderr(`Creating instance from definition of WebPart ${webPartId}...`);
}
const component = webPartDefinition[0];
const id = v4();
const componentId = component.Id.replace(/^\{|\}$/g, "").toLowerCase();
const manifest = JSON.parse(component.Manifest);
const preconfiguredEntries = manifest.preconfiguredEntries[0];
const webPart = {
id,
webPartData: {
dataVersion: "1.0",
description: preconfiguredEntries.description.default,
id: componentId,
instanceId: id,
properties: preconfiguredEntries.properties,
title: preconfiguredEntries.title.default
},
webPartId: componentId
};
return webPart;
}
async setWebPartProperties(webPart, logger, args) {
if (args.options.webPartProperties) {
if (this.debug) {
await logger.logToStderr('WebPart properties: ');
await logger.logToStderr(args.options.webPartProperties);
await logger.logToStderr('');
}
try {
const properties = JSON.parse(args.options.webPartProperties);
webPart.webPartData.properties = this.extend(webPart.webPartData.properties, properties);
}
catch {
}
}
if (args.options.webPartData) {
if (this.debug) {
await logger.logToStderr('WebPart data:');
await logger.logToStderr(args.options.webPartData);
await logger.logToStderr('');
}
const webPartData = JSON.parse(args.options.webPartData);
webPart.webPartData = this.extend(webPart.webPartData, webPartData);
webPart.id = webPart.webPartData.instanceId;
}
}
getCorrectControl(canvasContent, args) {
// get Vertical section
if (args.options.verticalSection) {
let verticalSection = canvasContent
.find(c => c.position?.layoutIndex === 2);
//if vertical section does not exist, create it
if (!verticalSection) {
verticalSection = {
position: {
controlIndex: 1,
sectionIndex: 1,
zoneIndex: 1,
sectionFactor: 12,
layoutIndex: 2
},
emphasis: {},
displayMode: 2
};
canvasContent.unshift(verticalSection);
}
return verticalSection;
}
// if no section exists (canvasContent array only has 1 default object), add a default section (1 col)
if (canvasContent.length === 1) {
const defaultSection = {
position: {
controlIndex: 1,
sectionIndex: 1,
zoneIndex: 1,
sectionFactor: 12,
layoutIndex: 1
},
emphasis: {},
displayMode: 2
};
canvasContent.unshift(defaultSection);
}
// get unique zoneIndex values given each section can have 1 or more
// columns each assigned to the zoneIndex of the corresponding section
const zoneIndices = canvasContent
// Exclude the vertical section
.filter(c => c.position)
.map(c => c.position.zoneIndex)
.filter((value, index, array) => {
return array.indexOf(value) === index;
})
.sort((a, b) => a - b);
// get section number. if not specified, get the last section
const section = args.options.section || zoneIndices.length;
if (section > zoneIndices.length) {
throw `Invalid section '${section}'`;
}
// zoneIndex that represents the section where the web part should be added
const zoneIndex = zoneIndices[section - 1];
const column = args.options.column || 1;
// we need the index of the control in the array so that we know which
// item to replace or where to add the web part
const controlIndex = canvasContent
.findIndex(c => c.position &&
c.position.zoneIndex === zoneIndex &&
c.position.sectionIndex === column);
if (controlIndex === -1) {
throw `Invalid column '${args.options.column}'`;
}
// get the first control that matches section and column
// if it's a empty column, it should be replaced with the web part
// if it's a web part, then we need to determine if there are other
// web parts and where in the array the new web part should be put
return canvasContent[controlIndex];
}
/**
* Provides functionality to extend the given object by doing a shallow copy
*
* @param target The object to which properties will be copied
* @param source The source object from which properties will be copied
*
*/
extend(target, source) {
return Object.getOwnPropertyNames(source)
.reduce((t, v) => {
t[v] = source[v];
return t;
}, target);
}
}
_SpoPageClientSideWebPartAddCommand_instances = new WeakSet(), _SpoPageClientSideWebPartAddCommand_initTelemetry = function _SpoPageClientSideWebPartAddCommand_initTelemetry() {
this.telemetry.push((args) => {
Object.assign(this.telemetryProperties, {
standardWebPart: args.options.standardWebPart,
webPartData: typeof args.options.webPartData !== 'undefined',
webPartId: typeof args.options.webPartId !== 'undefined',
webPartProperties: typeof args.options.webPartProperties !== 'undefined',
section: typeof args.options.section !== 'undefined',
column: typeof args.options.column !== 'undefined',
order: typeof args.options.order !== 'undefined',
verticalSection: !!args.options.verticalSection
});
});
}, _SpoPageClientSideWebPartAddCommand_initOptions = function _SpoPageClientSideWebPartAddCommand_initOptions() {
this.options.unshift({
option: '-u, --webUrl <webUrl>'
}, {
option: '-n, --pageName <pageName>'
}, {
option: '--standardWebPart [standardWebPart]'
}, {
option: '--webPartId [webPartId]'
}, {
option: '--webPartProperties [webPartProperties]'
}, {
option: '--webPartData [webPartData]'
}, {
option: '--section [section]'
}, {
option: '--column [column]'
}, {
option: '--order [order]'
}, {
option: '--verticalSection'
});
}, _SpoPageClientSideWebPartAddCommand_initValidators = function _SpoPageClientSideWebPartAddCommand_initValidators() {
this.validators.push(async (args) => {
if (args.options.webPartId && !validation.isValidGuid(args.options.webPartId)) {
return `The webPartId '${args.options.webPartId}' is not a valid GUID`;
}
if (args.options.standardWebPart && !StandardWebPartUtils.isValidStandardWebPartType(args.options.standardWebPart)) {
return `${args.options.standardWebPart} is not a valid standard web part type`;
}
if (args.options.webPartProperties && args.options.webPartData) {
return 'Specify webPartProperties or webPartData but not both';
}
if (args.options.webPartProperties) {
try {
JSON.parse(args.options.webPartProperties);
}
catch (e) {
return `Specified webPartProperties is not a valid JSON string. Input: ${args.options
.webPartProperties}. Error: ${e}`;
}
}
if (args.options.webPartData) {
try {
JSON.parse(args.options.webPartData);
}
catch (e) {
return `Specified webPartData is not a valid JSON string. Input: ${args.options
.webPartData}. Error: ${e}`;
}
}
if (args.options.section && (!Number.isInteger(args.options.section) || args.options.section < 1)) {
return 'The value of parameter section must be 1 or higher';
}
if (args.options.column && (!Number.isInteger(args.options.column) || args.options.column < 1)) {
return 'The value of parameter column must be 1 or higher';
}
if (args.options.section && args.options.verticalSection) {
return 'Specify section or verticalSection but not both';
}
if (args.options.column && args.options.verticalSection) {
return 'Use column in combination with section, not with verticalSection';
}
return validation.isValidSharePointUrl(args.options.webUrl);
});
}, _SpoPageClientSideWebPartAddCommand_initOptionSets = function _SpoPageClientSideWebPartAddCommand_initOptionSets() {
this.optionSets.push({ options: ['standardWebPart', 'webPartId'] });
};
export default new SpoPageClientSideWebPartAddCommand();
//# sourceMappingURL=page-clientsidewebpart-add.js.map