@pnp/cli-microsoft365 10.10.0-beta.eb74779 → 10.11.0-beta.5e075e5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/allCommands.json +1 -1
- package/allCommandsFull.json +1 -1
- package/dist/m365/cli/commands/app/app-add.js +95 -0
- package/dist/m365/cli/commands.js +1 -0
- package/dist/m365/entra/commands/organization/organization-set.js +104 -0
- package/dist/m365/entra/commands.js +1 -0
- package/dist/m365/spo/commands/homesite/homesite-remove.js +57 -37
- package/dist/m365/spo/commands/list/list-view-add.js +140 -87
- package/dist/m365/spo/commands/page/Page.js +10 -3
- package/dist/m365/spo/commands/page/clientsidepages.js +4 -0
- package/dist/m365/spo/commands/page/page-section-add.js +73 -13
- package/dist/m365/spo/commands/page/page-section-list.js +5 -1
- package/dist/m365/spo/commands/serviceprincipal/serviceprincipal-permissionrequest-list.js +13 -7
- package/dist/m365/spo/commands/site/site-appcatalog-list.js +38 -2
- package/docs/docs/cmd/cli/app/app-add.mdx +50 -0
- package/docs/docs/cmd/entra/organization/organization-set.mdx +84 -0
- package/docs/docs/cmd/spo/homesite/homesite-remove.mdx +6 -3
- package/docs/docs/cmd/spo/list/list-view-add.mdx +53 -10
- package/docs/docs/cmd/spo/page/page-section-add.mdx +22 -2
- package/docs/docs/cmd/spo/site/site-appcatalog-list.mdx +11 -0
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { globalOptionsZod } from '../../../../Command.js';
|
|
3
|
+
import { zod } from '../../../../utils/zod.js';
|
|
4
|
+
import config from '../../../../config.js';
|
|
5
|
+
import GraphCommand from '../../../base/GraphCommand.js';
|
|
6
|
+
import commands from '../../commands.js';
|
|
7
|
+
import { cli } from '../../../../cli/cli.js';
|
|
8
|
+
import { entraApp } from '../../../../utils/entraApp.js';
|
|
9
|
+
import { accessToken } from '../../../../utils/accessToken.js';
|
|
10
|
+
import auth from '../../../../Auth.js';
|
|
11
|
+
const options = globalOptionsZod
|
|
12
|
+
.extend({
|
|
13
|
+
name: zod.alias('n', z.string().optional().default('CLI for M365')),
|
|
14
|
+
scopes: zod.alias('s', z.string().optional().default('minimal')),
|
|
15
|
+
saveToConfig: z.boolean().optional()
|
|
16
|
+
})
|
|
17
|
+
.strict();
|
|
18
|
+
class CliAppAddCommand extends GraphCommand {
|
|
19
|
+
get name() {
|
|
20
|
+
return commands.APP_ADD;
|
|
21
|
+
}
|
|
22
|
+
get description() {
|
|
23
|
+
return 'Creates a Microsoft Entra application registration for CLI for Microsoft 365';
|
|
24
|
+
}
|
|
25
|
+
get schema() {
|
|
26
|
+
return options;
|
|
27
|
+
}
|
|
28
|
+
getRefinedSchema(schema) {
|
|
29
|
+
return schema
|
|
30
|
+
.refine(options => {
|
|
31
|
+
const scopes = options.scopes;
|
|
32
|
+
if (!scopes.includes(',')) {
|
|
33
|
+
return scopes === 'minimal' || scopes === 'all';
|
|
34
|
+
}
|
|
35
|
+
const scopeList = scopes.split(',').map(s => s.trim());
|
|
36
|
+
return scopeList.every(scope => scope.startsWith('https'));
|
|
37
|
+
}, {
|
|
38
|
+
message: "Scopes must be 'minimal', 'all', or comma-separated list of URLs starting with 'https'. 'minimal' and 'all' cannot be combined with other scopes.",
|
|
39
|
+
path: ['scopes']
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
async commandAction(logger, args) {
|
|
43
|
+
try {
|
|
44
|
+
const options = {
|
|
45
|
+
allowPublicClientFlows: true,
|
|
46
|
+
apisDelegated: this.getScopes(args.options),
|
|
47
|
+
implicitFlow: false,
|
|
48
|
+
multitenant: false,
|
|
49
|
+
name: args.options.name,
|
|
50
|
+
platform: 'publicClient',
|
|
51
|
+
redirectUris: 'http://localhost,https://localhost,https://login.microsoftonline.com/common/oauth2/nativeclient'
|
|
52
|
+
};
|
|
53
|
+
const apis = await entraApp.resolveApis({
|
|
54
|
+
options,
|
|
55
|
+
logger,
|
|
56
|
+
verbose: this.verbose,
|
|
57
|
+
debug: this.debug
|
|
58
|
+
});
|
|
59
|
+
const appInfo = await entraApp.createAppRegistration({
|
|
60
|
+
options,
|
|
61
|
+
unknownOptions: {},
|
|
62
|
+
apis,
|
|
63
|
+
logger,
|
|
64
|
+
verbose: this.verbose,
|
|
65
|
+
debug: this.debug
|
|
66
|
+
});
|
|
67
|
+
appInfo.tenantId = accessToken.getTenantIdFromAccessToken(auth.connection.accessTokens[auth.defaultResource].accessToken);
|
|
68
|
+
await entraApp.grantAdminConsent({
|
|
69
|
+
appInfo,
|
|
70
|
+
appPermissions: entraApp.appPermissions,
|
|
71
|
+
adminConsent: true,
|
|
72
|
+
logger,
|
|
73
|
+
debug: this.debug
|
|
74
|
+
});
|
|
75
|
+
if (args.options.saveToConfig) {
|
|
76
|
+
cli.getConfig().set('clientId', appInfo.appId);
|
|
77
|
+
cli.getConfig().set('tenantId', appInfo.tenantId);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
this.handleRejectedODataJsonPromise(err);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
getScopes(options) {
|
|
85
|
+
if (options.scopes === 'all') {
|
|
86
|
+
return config.allScopes.join(',');
|
|
87
|
+
}
|
|
88
|
+
else if (options.scopes === 'minimal') {
|
|
89
|
+
return config.minimalScopes.join(',');
|
|
90
|
+
}
|
|
91
|
+
return options.scopes;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
export default new CliAppAddCommand();
|
|
95
|
+
//# sourceMappingURL=app-add.js.map
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { globalOptionsZod } from '../../../../Command.js';
|
|
3
|
+
import { zod } from '../../../../utils/zod.js';
|
|
4
|
+
import GraphCommand from '../../../base/GraphCommand.js';
|
|
5
|
+
import commands from '../../commands.js';
|
|
6
|
+
import { validation } from '../../../../utils/validation.js';
|
|
7
|
+
import request from '../../../../request.js';
|
|
8
|
+
import { odata } from '../../../../utils/odata.js';
|
|
9
|
+
const options = globalOptionsZod
|
|
10
|
+
.extend({
|
|
11
|
+
id: zod.alias('i', z.string().refine(id => validation.isValidGuid(id), id => ({
|
|
12
|
+
message: `'${id}' is not a valid GUID.`
|
|
13
|
+
})).optional()),
|
|
14
|
+
displayName: zod.alias('d', z.string().optional()),
|
|
15
|
+
marketingNotificationEmails: z.string().refine(emails => validation.isValidUserPrincipalNameArray(emails) === true, invalidEmails => ({
|
|
16
|
+
message: `The following marketing notification emails are invalid: ${invalidEmails}.`
|
|
17
|
+
})).transform((value) => value.split(',')).optional(),
|
|
18
|
+
securityComplianceNotificationMails: z.string().refine(emails => validation.isValidUserPrincipalNameArray(emails) === true, invalidEmails => ({
|
|
19
|
+
message: `The following security compliance notification emails are invalid: ${invalidEmails}.`
|
|
20
|
+
})).transform((value) => value.split(',')).optional(),
|
|
21
|
+
securityComplianceNotificationPhones: z.string().transform((value) => value.split(',')).optional(),
|
|
22
|
+
technicalNotificationMails: z.string().refine(emails => validation.isValidUserPrincipalNameArray(emails) === true, invalidEmails => ({
|
|
23
|
+
message: `The following technical notification emails are invalid: ${invalidEmails}.`
|
|
24
|
+
})).transform((value) => value.split(',')).optional(),
|
|
25
|
+
contactEmail: z.string().refine(id => validation.isValidUserPrincipalName(id), id => ({
|
|
26
|
+
message: `'${id}' is not a valid email.`
|
|
27
|
+
})).optional(),
|
|
28
|
+
statementUrl: z.string().optional()
|
|
29
|
+
})
|
|
30
|
+
.strict();
|
|
31
|
+
class EntraOrganizationSetCommand extends GraphCommand {
|
|
32
|
+
get name() {
|
|
33
|
+
return commands.ORGANIZATION_SET;
|
|
34
|
+
}
|
|
35
|
+
get description() {
|
|
36
|
+
return 'Updates info about the organization';
|
|
37
|
+
}
|
|
38
|
+
get schema() {
|
|
39
|
+
return options;
|
|
40
|
+
}
|
|
41
|
+
getRefinedSchema(schema) {
|
|
42
|
+
return schema
|
|
43
|
+
.refine(options => !(options.id && options.displayName), {
|
|
44
|
+
message: 'Specify either id or displayName, but not both'
|
|
45
|
+
})
|
|
46
|
+
.refine(options => options.id || options.displayName, {
|
|
47
|
+
message: 'Specify either id or displayName'
|
|
48
|
+
})
|
|
49
|
+
.refine(options => [options.contactEmail, options.marketingNotificationEmails, options.securityComplianceNotificationMails, options.securityComplianceNotificationPhones,
|
|
50
|
+
options.statementUrl, options.technicalNotificationMails].filter(o => o !== undefined).length > 0, {
|
|
51
|
+
message: 'Specify at least one of the following options: contactEmail, marketingNotificationEmails, securityComplianceNotificationMails, securityComplianceNotificationPhones, statementUrl, or technicalNotificationMails'
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
async commandAction(logger, args) {
|
|
55
|
+
try {
|
|
56
|
+
let organizationId = args.options.id;
|
|
57
|
+
if (args.options.displayName) {
|
|
58
|
+
organizationId = await this.getOrganizationIdByDisplayName(args.options.displayName);
|
|
59
|
+
}
|
|
60
|
+
if (args.options.verbose) {
|
|
61
|
+
await logger.logToStderr(`Updating organization with ID ${organizationId}...`);
|
|
62
|
+
}
|
|
63
|
+
const data = {
|
|
64
|
+
marketingNotificationEmails: args.options.marketingNotificationEmails,
|
|
65
|
+
securityComplianceNotificationMails: args.options.securityComplianceNotificationMails,
|
|
66
|
+
securityComplianceNotificationPhones: args.options.securityComplianceNotificationPhones,
|
|
67
|
+
technicalNotificationMails: args.options.technicalNotificationMails
|
|
68
|
+
};
|
|
69
|
+
if (args.options.contactEmail || args.options.statementUrl) {
|
|
70
|
+
data.privacyProfile = {};
|
|
71
|
+
}
|
|
72
|
+
if (args.options.contactEmail) {
|
|
73
|
+
data.privacyProfile.contactEmail = args.options.contactEmail;
|
|
74
|
+
}
|
|
75
|
+
if (args.options.statementUrl) {
|
|
76
|
+
data.privacyProfile.statementUrl = args.options.statementUrl;
|
|
77
|
+
}
|
|
78
|
+
const requestOptions = {
|
|
79
|
+
url: `${this.resource}/v1.0/organization/${organizationId}`,
|
|
80
|
+
headers: {
|
|
81
|
+
accept: 'application/json;odata.metadata=none',
|
|
82
|
+
'content-type': 'application/json'
|
|
83
|
+
},
|
|
84
|
+
data: data,
|
|
85
|
+
responseType: 'json'
|
|
86
|
+
};
|
|
87
|
+
await request.patch(requestOptions);
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
this.handleRejectedODataJsonPromise(err);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
async getOrganizationIdByDisplayName(displayName) {
|
|
94
|
+
const url = `${this.resource}/v1.0/organization?$select=id,displayName`;
|
|
95
|
+
// the endpoint always returns one item
|
|
96
|
+
const organizations = await odata.getAllItems(url);
|
|
97
|
+
if (organizations[0].displayName !== displayName) {
|
|
98
|
+
throw `The specified organization '${displayName}' does not exist.`;
|
|
99
|
+
}
|
|
100
|
+
return organizations[0].id;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
export default new EntraOrganizationSetCommand();
|
|
104
|
+
//# sourceMappingURL=organization-set.js.map
|
|
@@ -84,6 +84,7 @@ export default {
|
|
|
84
84
|
OAUTH2GRANT_REMOVE: `${prefix} oauth2grant remove`,
|
|
85
85
|
OAUTH2GRANT_SET: `${prefix} oauth2grant set`,
|
|
86
86
|
ORGANIZATION_LIST: `${prefix} organization list`,
|
|
87
|
+
ORGANIZATION_SET: `${prefix} organization set`,
|
|
87
88
|
PIM_ROLE_ASSIGNMENT_ADD: `${prefix} pim role assignment add`,
|
|
88
89
|
PIM_ROLE_ASSIGNMENT_LIST: `${prefix} pim role assignment list`,
|
|
89
90
|
PIM_ROLE_ASSIGNMENT_REMOVE: `${prefix} pim role assignment remove`,
|
|
@@ -1,48 +1,62 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
};
|
|
6
|
-
var _SpoHomeSiteRemoveCommand_instances, _SpoHomeSiteRemoveCommand_initTelemetry, _SpoHomeSiteRemoveCommand_initOptions;
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { zod } from '../../../../utils/zod.js';
|
|
3
|
+
import { globalOptionsZod } from '../../../../Command.js';
|
|
4
|
+
import { validation } from '../../../../utils/validation.js';
|
|
7
5
|
import { cli } from '../../../../cli/cli.js';
|
|
8
6
|
import config from '../../../../config.js';
|
|
9
7
|
import request from '../../../../request.js';
|
|
10
8
|
import { spo } from '../../../../utils/spo.js';
|
|
11
9
|
import SpoCommand from '../../../base/SpoCommand.js';
|
|
12
10
|
import commands from '../../commands.js';
|
|
11
|
+
const options = globalOptionsZod
|
|
12
|
+
.extend({
|
|
13
|
+
url: zod.alias('u', z.string()
|
|
14
|
+
.refine(url => validation.isValidSharePointUrl(url) === true, url => ({
|
|
15
|
+
message: `'${url}' is not a valid SharePoint Online site URL.`
|
|
16
|
+
})).optional()),
|
|
17
|
+
force: zod.alias('f', z.boolean().optional())
|
|
18
|
+
})
|
|
19
|
+
.strict();
|
|
13
20
|
class SpoHomeSiteRemoveCommand extends SpoCommand {
|
|
14
21
|
get name() {
|
|
15
22
|
return commands.HOMESITE_REMOVE;
|
|
16
23
|
}
|
|
17
24
|
get description() {
|
|
18
|
-
return 'Removes
|
|
25
|
+
return 'Removes a Home Site';
|
|
19
26
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
_SpoHomeSiteRemoveCommand_instances.add(this);
|
|
23
|
-
__classPrivateFieldGet(this, _SpoHomeSiteRemoveCommand_instances, "m", _SpoHomeSiteRemoveCommand_initTelemetry).call(this);
|
|
24
|
-
__classPrivateFieldGet(this, _SpoHomeSiteRemoveCommand_instances, "m", _SpoHomeSiteRemoveCommand_initOptions).call(this);
|
|
27
|
+
get schema() {
|
|
28
|
+
return options;
|
|
25
29
|
}
|
|
26
30
|
async commandAction(logger, args) {
|
|
27
31
|
const removeHomeSite = async () => {
|
|
28
32
|
try {
|
|
33
|
+
if (this.verbose) {
|
|
34
|
+
await logger.logToStderr(`Removing ${args.options.url ? `'${args.options.url}' as home site` : 'the current home site'}...`);
|
|
35
|
+
}
|
|
29
36
|
const spoAdminUrl = await spo.getSpoAdminUrl(logger, this.debug);
|
|
30
37
|
const reqDigest = await spo.getRequestDigest(spoAdminUrl);
|
|
31
|
-
|
|
32
|
-
url
|
|
33
|
-
|
|
34
|
-
'X-RequestDigest': reqDigest.FormDigestValue
|
|
35
|
-
},
|
|
36
|
-
data: `<Request AddExpandoFieldTypeSuffix="true" SchemaVersion="15.0.0.0" LibraryVersion="16.0.0.0" ApplicationName="${config.applicationName}" xmlns="http://schemas.microsoft.com/sharepoint/clientquery/2009"><Actions><ObjectPath Id="28" ObjectPathId="27" /><Method Name="RemoveSPHSite" Id="29" ObjectPathId="27" /></Actions><ObjectPaths><Constructor Id="27" TypeId="{268004ae-ef6b-4e9b-8425-127220d84719}" /></ObjectPaths></Request>`
|
|
37
|
-
};
|
|
38
|
-
const res = await request.post(requestOptions);
|
|
39
|
-
const json = JSON.parse(res);
|
|
40
|
-
const response = json[0];
|
|
41
|
-
if (response.ErrorInfo) {
|
|
42
|
-
throw response.ErrorInfo.ErrorMessage;
|
|
38
|
+
if (args.options.url) {
|
|
39
|
+
await this.removeHomeSiteByUrl(args.options.url, spoAdminUrl, logger);
|
|
40
|
+
await logger.log(`${args.options.url} has been removed as a Home Site. It may take some time for the change to apply. Check aka.ms/homesites for details.`);
|
|
43
41
|
}
|
|
44
42
|
else {
|
|
45
|
-
await
|
|
43
|
+
await this.warn(logger, `The current way this command works is deprecated and will change in the next major release. The '--url' option will become required.`);
|
|
44
|
+
const requestOptions = {
|
|
45
|
+
url: `${spoAdminUrl}/_vti_bin/client.svc/ProcessQuery`,
|
|
46
|
+
headers: {
|
|
47
|
+
'X-RequestDigest': reqDigest.FormDigestValue
|
|
48
|
+
},
|
|
49
|
+
data: `<Request AddExpandoFieldTypeSuffix="true" SchemaVersion="15.0.0.0" LibraryVersion="16.0.0.0" ApplicationName="${config.applicationName}" xmlns="http://schemas.microsoft.com/sharepoint/clientquery/2009"><Actions><ObjectPath Id="28" ObjectPathId="27" /><Method Name="RemoveSPHSite" Id="29" ObjectPathId="27" /></Actions><ObjectPaths><Constructor Id="27" TypeId="{268004ae-ef6b-4e9b-8425-127220d84719}" /></ObjectPaths></Request>`
|
|
50
|
+
};
|
|
51
|
+
const res = await request.post(requestOptions);
|
|
52
|
+
const json = JSON.parse(res);
|
|
53
|
+
const response = json[0];
|
|
54
|
+
if (response.ErrorInfo) {
|
|
55
|
+
throw response.ErrorInfo.ErrorMessage;
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
await logger.log(json[json.length - 1]);
|
|
59
|
+
}
|
|
46
60
|
}
|
|
47
61
|
}
|
|
48
62
|
catch (err) {
|
|
@@ -53,23 +67,29 @@ class SpoHomeSiteRemoveCommand extends SpoCommand {
|
|
|
53
67
|
await removeHomeSite();
|
|
54
68
|
}
|
|
55
69
|
else {
|
|
56
|
-
const result = await cli.promptForConfirmation({
|
|
70
|
+
const result = await cli.promptForConfirmation({
|
|
71
|
+
message: args.options.url
|
|
72
|
+
? `Are you sure you want to remove '${args.options.url}' as home site?`
|
|
73
|
+
: `Are you sure you want to remove the current home site?`
|
|
74
|
+
});
|
|
57
75
|
if (result) {
|
|
58
76
|
await removeHomeSite();
|
|
59
77
|
}
|
|
60
78
|
}
|
|
61
79
|
}
|
|
80
|
+
async removeHomeSiteByUrl(siteUrl, spoAdminUrl, logger) {
|
|
81
|
+
const siteAdminProperties = await spo.getSiteAdminPropertiesByUrl(siteUrl, false, logger, this.verbose);
|
|
82
|
+
const requestOptions = {
|
|
83
|
+
url: `${spoAdminUrl}/_api/SPO.Tenant/RemoveTargetedSite`,
|
|
84
|
+
headers: {
|
|
85
|
+
accept: 'application/json;odata=nometadata'
|
|
86
|
+
},
|
|
87
|
+
data: {
|
|
88
|
+
siteId: siteAdminProperties.SiteId
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
await request.post(requestOptions);
|
|
92
|
+
}
|
|
62
93
|
}
|
|
63
|
-
_SpoHomeSiteRemoveCommand_instances = new WeakSet(), _SpoHomeSiteRemoveCommand_initTelemetry = function _SpoHomeSiteRemoveCommand_initTelemetry() {
|
|
64
|
-
this.telemetry.push((args) => {
|
|
65
|
-
Object.assign(this.telemetryProperties, {
|
|
66
|
-
force: args.options.force || false
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
}, _SpoHomeSiteRemoveCommand_initOptions = function _SpoHomeSiteRemoveCommand_initOptions() {
|
|
70
|
-
this.options.unshift({
|
|
71
|
-
option: '-f, --force'
|
|
72
|
-
});
|
|
73
|
-
};
|
|
74
94
|
export default new SpoHomeSiteRemoveCommand();
|
|
75
95
|
//# sourceMappingURL=homesite-remove.js.map
|
|
@@ -1,53 +1,126 @@
|
|
|
1
|
-
|
|
2
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
3
|
-
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");
|
|
4
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
5
|
-
};
|
|
6
|
-
var _SpoListViewAddCommand_instances, _SpoListViewAddCommand_initTelemetry, _SpoListViewAddCommand_initOptions, _SpoListViewAddCommand_initValidators, _SpoListViewAddCommand_initOptionSets;
|
|
1
|
+
import { globalOptionsZod } from '../../../../Command.js';
|
|
7
2
|
import request from '../../../../request.js';
|
|
8
3
|
import { formatting } from '../../../../utils/formatting.js';
|
|
9
4
|
import { urlUtil } from '../../../../utils/urlUtil.js';
|
|
10
5
|
import { validation } from '../../../../utils/validation.js';
|
|
11
6
|
import SpoCommand from '../../../base/SpoCommand.js';
|
|
12
7
|
import commands from '../../commands.js';
|
|
8
|
+
import { z } from 'zod';
|
|
9
|
+
import { zod } from '../../../../utils/zod.js';
|
|
10
|
+
const options = globalOptionsZod
|
|
11
|
+
.extend({
|
|
12
|
+
webUrl: zod.alias('u', z.string()
|
|
13
|
+
.refine(url => validation.isValidSharePointUrl(url) === true, {
|
|
14
|
+
message: 'webUrl is not a valid SharePoint site URL.'
|
|
15
|
+
})),
|
|
16
|
+
listId: z.string()
|
|
17
|
+
.refine(id => validation.isValidGuid(id), id => ({
|
|
18
|
+
message: `'${id}' is not a valid GUID.`
|
|
19
|
+
})).optional(),
|
|
20
|
+
listTitle: z.string().optional(),
|
|
21
|
+
listUrl: z.string().optional(),
|
|
22
|
+
title: z.string().min(1, 'Cannot be empty.'),
|
|
23
|
+
fields: z.string().optional(),
|
|
24
|
+
viewQuery: z.string().optional(),
|
|
25
|
+
personal: z.boolean().optional(),
|
|
26
|
+
default: z.boolean().optional(),
|
|
27
|
+
paged: z.boolean().optional(),
|
|
28
|
+
rowLimit: z.number().int().positive().optional(),
|
|
29
|
+
customFormatter: z.string()
|
|
30
|
+
.refine(formatter => {
|
|
31
|
+
try {
|
|
32
|
+
JSON.parse(formatter);
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
}, {
|
|
39
|
+
message: 'Custom formatter must be a valid JSON string.'
|
|
40
|
+
})
|
|
41
|
+
.optional(),
|
|
42
|
+
type: z.enum(['list', 'calendar', 'gallery', 'kanban']).optional(),
|
|
43
|
+
calendarStartDateField: z.string().min(1, 'Cannot be empty.').optional(),
|
|
44
|
+
calendarEndDateField: z.string().min(1, 'Cannot be empty.').optional(),
|
|
45
|
+
calendarTitleField: z.string().min(1, 'Cannot be empty.').optional(),
|
|
46
|
+
calendarSubTitleField: z.string().min(1, 'Cannot be empty.').optional(),
|
|
47
|
+
calendarDefaultLayout: z.enum(['month', 'week', 'workWeek', 'day']).optional(),
|
|
48
|
+
kanbanBucketField: z.string().min(1, 'Cannot be empty.').optional()
|
|
49
|
+
})
|
|
50
|
+
.strict();
|
|
13
51
|
class SpoListViewAddCommand extends SpoCommand {
|
|
14
52
|
get name() {
|
|
15
53
|
return commands.LIST_VIEW_ADD;
|
|
16
54
|
}
|
|
17
55
|
get description() {
|
|
18
|
-
return 'Adds a new view to a SharePoint list
|
|
56
|
+
return 'Adds a new view to a SharePoint list';
|
|
19
57
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
58
|
+
get schema() {
|
|
59
|
+
return options;
|
|
60
|
+
}
|
|
61
|
+
getRefinedSchema(schema) {
|
|
62
|
+
return schema
|
|
63
|
+
.refine((options) => [options.listId, options.listTitle, options.listUrl].filter(o => o !== undefined).length === 1, {
|
|
64
|
+
message: 'Use one of the following options: listId, listTitle, or listUrl.'
|
|
65
|
+
})
|
|
66
|
+
.refine((options) => !options.personal || !options.default, {
|
|
67
|
+
message: 'Default view cannot be a personal view.'
|
|
68
|
+
})
|
|
69
|
+
.refine((options) => options.type !== 'calendar' || [options.calendarStartDateField, options.calendarEndDateField, options.calendarTitleField].filter(o => o === undefined).length === 0, {
|
|
70
|
+
message: 'When type is calendar, do specify calendarStartDateField, calendarEndDateField, and calendarTitleField.'
|
|
71
|
+
})
|
|
72
|
+
.refine((options) => options.type === 'calendar' || [options.calendarStartDateField, options.calendarEndDateField, options.calendarTitleField].filter(o => o === undefined).length === 3, {
|
|
73
|
+
message: 'When type is not calendar, do not specify calendarStartDateField, calendarEndDateField, and calendarTitleField.'
|
|
74
|
+
})
|
|
75
|
+
.refine((options) => options.type !== 'kanban' || options.kanbanBucketField !== undefined, {
|
|
76
|
+
message: 'When type is kanban, do specify kanbanBucketField.'
|
|
77
|
+
})
|
|
78
|
+
.refine((options) => options.type === 'kanban' || options.kanbanBucketField === undefined, {
|
|
79
|
+
message: 'When type is not kanban, do not specify kanbanBucketField.'
|
|
80
|
+
})
|
|
81
|
+
.refine((options) => options.type === 'calendar' || options.fields !== undefined, {
|
|
82
|
+
message: 'When type is not calendar, do specify fields.'
|
|
83
|
+
});
|
|
27
84
|
}
|
|
28
85
|
async commandAction(logger, args) {
|
|
29
|
-
const requestOptions = {
|
|
30
|
-
url: this.getRestUrl(args.options),
|
|
31
|
-
headers: {
|
|
32
|
-
'content-type': 'application/json;odata=verbose',
|
|
33
|
-
accept: 'application/json;odata=nometadata'
|
|
34
|
-
},
|
|
35
|
-
responseType: 'json',
|
|
36
|
-
data: {
|
|
37
|
-
parameters: {
|
|
38
|
-
Title: args.options.title,
|
|
39
|
-
ViewFields: {
|
|
40
|
-
results: args.options.fields.split(',')
|
|
41
|
-
},
|
|
42
|
-
Query: args.options.viewQuery,
|
|
43
|
-
PersonalView: !!args.options.personal,
|
|
44
|
-
SetAsDefaultView: !!args.options.default,
|
|
45
|
-
Paged: !!args.options.paged,
|
|
46
|
-
RowLimit: args.options.rowLimit ? +args.options.rowLimit : 30
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
86
|
try {
|
|
87
|
+
if (this.verbose) {
|
|
88
|
+
await logger.logToStderr(`Adding view '${args.options.title}' to list...`);
|
|
89
|
+
}
|
|
90
|
+
let apiUrl = `${args.options.webUrl}/_api/web/`;
|
|
91
|
+
if (args.options.listId) {
|
|
92
|
+
apiUrl += `lists(guid'${formatting.encodeQueryParameter(args.options.listId)}')`;
|
|
93
|
+
}
|
|
94
|
+
else if (args.options.listTitle) {
|
|
95
|
+
apiUrl += `lists/getByTitle('${formatting.encodeQueryParameter(args.options.listTitle)}')`;
|
|
96
|
+
}
|
|
97
|
+
else if (args.options.listUrl) {
|
|
98
|
+
apiUrl += `GetList('${formatting.encodeQueryParameter(urlUtil.getServerRelativePath(args.options.webUrl, args.options.listUrl))}')`;
|
|
99
|
+
}
|
|
100
|
+
apiUrl += '/views/add';
|
|
101
|
+
const requestOptions = {
|
|
102
|
+
url: apiUrl,
|
|
103
|
+
headers: {
|
|
104
|
+
'content-type': 'application/json;odata=verbose',
|
|
105
|
+
accept: 'application/json;odata=nometadata'
|
|
106
|
+
},
|
|
107
|
+
responseType: 'json',
|
|
108
|
+
data: {
|
|
109
|
+
parameters: {
|
|
110
|
+
Title: args.options.title,
|
|
111
|
+
ViewFields: {
|
|
112
|
+
results: args.options.fields?.split(',').map(f => f.trim()) ?? []
|
|
113
|
+
},
|
|
114
|
+
Query: args.options.viewQuery,
|
|
115
|
+
PersonalView: !!args.options.personal,
|
|
116
|
+
SetAsDefaultView: !!args.options.default,
|
|
117
|
+
Paged: !!args.options.paged,
|
|
118
|
+
RowLimit: args.options.rowLimit ?? 30,
|
|
119
|
+
CustomFormatter: args.options.customFormatter
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
this.setViewTypeSpecificParameters(args.options, requestOptions.data.parameters);
|
|
51
124
|
const result = await request.post(requestOptions);
|
|
52
125
|
await logger.log(result);
|
|
53
126
|
}
|
|
@@ -55,62 +128,42 @@ class SpoListViewAddCommand extends SpoCommand {
|
|
|
55
128
|
this.handleRejectedODataJsonPromise(err);
|
|
56
129
|
}
|
|
57
130
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
this.telemetry.push((args) => {
|
|
75
|
-
Object.assign(this.telemetryProperties, {
|
|
76
|
-
listId: typeof args.options.listId !== 'undefined',
|
|
77
|
-
listTitle: typeof args.options.listTitle !== 'undefined',
|
|
78
|
-
listUrl: typeof args.options.listUrl !== 'undefined',
|
|
79
|
-
title: typeof args.options.title !== 'undefined',
|
|
80
|
-
viewQuery: typeof args.options.viewQuery !== 'undefined',
|
|
81
|
-
personal: !!args.options.personal,
|
|
82
|
-
default: !!args.options.default,
|
|
83
|
-
orderedView: !!args.options.orderedView,
|
|
84
|
-
paged: !!args.options.paged,
|
|
85
|
-
rowLimit: typeof args.options.rowLimit !== 'undefined'
|
|
86
|
-
});
|
|
87
|
-
});
|
|
88
|
-
}, _SpoListViewAddCommand_initOptions = function _SpoListViewAddCommand_initOptions() {
|
|
89
|
-
this.options.unshift({ option: '-u, --webUrl <webUrl>' }, { option: '--listId [listId]' }, { option: '--listTitle [listTitle]' }, { option: '--listUrl [listUrl]' }, { option: '--title <title>' }, { option: '--fields <fields>' }, { option: '--viewQuery [viewQuery]' }, { option: '--personal' }, { option: '--default' }, { option: '--paged' }, { option: '--rowLimit [rowLimit]' });
|
|
90
|
-
}, _SpoListViewAddCommand_initValidators = function _SpoListViewAddCommand_initValidators() {
|
|
91
|
-
this.validators.push(async (args) => {
|
|
92
|
-
const webUrlValidation = validation.isValidSharePointUrl(args.options.webUrl);
|
|
93
|
-
if (webUrlValidation !== true) {
|
|
94
|
-
return webUrlValidation;
|
|
131
|
+
setViewTypeSpecificParameters(options, requestBody) {
|
|
132
|
+
if (options.type === 'calendar') {
|
|
133
|
+
const defaultView = options.calendarDefaultLayout ?? 'month';
|
|
134
|
+
const titleField = options.calendarTitleField;
|
|
135
|
+
const subTitleField = options.calendarSubTitleField ?? '';
|
|
136
|
+
// Following fields are required for calendar view, order is important
|
|
137
|
+
const viewFields = [options.calendarStartDateField, options.calendarEndDateField, titleField, subTitleField].filter(field => field !== '');
|
|
138
|
+
// Add any additional fields specified by the user
|
|
139
|
+
const extraViewFields = requestBody.ViewFields.results.filter((field) => !viewFields.includes(field.trim()));
|
|
140
|
+
viewFields.push(...extraViewFields);
|
|
141
|
+
requestBody.CalendarViewStyles = `<CalendarViewStyle Title="Day" Type="day" Template="CalendarViewdayChrome" Sequence="1" Default="${String(defaultView === 'day').toUpperCase()}" /><CalendarViewStyle Title="Week" Type="week" Template="CalendarViewweekChrome" Sequence="2" Default="${String(defaultView === 'week').toUpperCase()}" /><CalendarViewStyle Title="Month" Type="month" Template="CalendarViewmonthChrome" Sequence="3" Default="${String(defaultView === 'month').toUpperCase()}" /><CalendarViewStyle Title="Work week" Type="workweek" Template="CalendarViewweekChrome" Sequence="4" Default="${String(defaultView === 'workWeek').toUpperCase()}" />`;
|
|
142
|
+
requestBody.Query = `<Where><DateRangesOverlap><FieldRef Name='${options.calendarStartDateField}' /><FieldRef Name='${options.calendarEndDateField}' /><Value Type='DateTime'><Month /></Value></DateRangesOverlap></Where>`;
|
|
143
|
+
requestBody.ViewData = `<FieldRef Name="${titleField}" Type="CalendarMonthTitle" /><FieldRef Name="${titleField}" Type="CalendarWeekTitle" /><FieldRef Name="${subTitleField}" Type="CalendarWeekLocation" /><FieldRef Name="${titleField}" Type="CalendarDayTitle" /><FieldRef Name="${subTitleField}" Type="CalendarDayLocation" />`;
|
|
144
|
+
requestBody.ViewFields.results = viewFields;
|
|
145
|
+
requestBody.ViewType2 = 'MODERNCALENDAR';
|
|
146
|
+
return;
|
|
95
147
|
}
|
|
96
|
-
if (
|
|
97
|
-
|
|
148
|
+
if (options.type === 'gallery') {
|
|
149
|
+
requestBody.ViewType2 = 'TILES';
|
|
150
|
+
return;
|
|
98
151
|
}
|
|
99
|
-
if (
|
|
100
|
-
if
|
|
101
|
-
|
|
152
|
+
if (options.type === 'kanban') {
|
|
153
|
+
// Add the bucket field to the view fields if it is not already included
|
|
154
|
+
const viewFields = requestBody.ViewFields.results;
|
|
155
|
+
if (!viewFields.includes(options.kanbanBucketField)) {
|
|
156
|
+
viewFields.push(options.kanbanBucketField);
|
|
102
157
|
}
|
|
103
|
-
if (
|
|
104
|
-
|
|
158
|
+
if (!options.customFormatter) {
|
|
159
|
+
requestBody.CustomFormatter = '{}';
|
|
105
160
|
}
|
|
161
|
+
requestBody.ViewData = `<FieldRef Name="${options.kanbanBucketField}" Type="KanbanPivotColumn" />`;
|
|
162
|
+
requestBody.ViewType2 = 'KANBAN';
|
|
163
|
+
return;
|
|
106
164
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
return true;
|
|
111
|
-
});
|
|
112
|
-
}, _SpoListViewAddCommand_initOptionSets = function _SpoListViewAddCommand_initOptionSets() {
|
|
113
|
-
this.optionSets.push({ options: ['listId', 'listTitle', 'listUrl'] });
|
|
114
|
-
};
|
|
165
|
+
}
|
|
166
|
+
;
|
|
167
|
+
}
|
|
115
168
|
export default new SpoListViewAddCommand();
|
|
116
169
|
//# sourceMappingURL=list-view-add.js.map
|
|
@@ -72,10 +72,14 @@ export class Page {
|
|
|
72
72
|
return output;
|
|
73
73
|
}
|
|
74
74
|
static getSectionInformation(section, isJSONOutput) {
|
|
75
|
-
|
|
76
|
-
order: section.order
|
|
77
|
-
columns: section.columns.map(column => this.getColumnsInformation(column, isJSONOutput))
|
|
75
|
+
const sectionOutput = {
|
|
76
|
+
order: section.order
|
|
78
77
|
};
|
|
78
|
+
if (this.isVerticalSection(section)) {
|
|
79
|
+
sectionOutput.isVertical = true;
|
|
80
|
+
}
|
|
81
|
+
sectionOutput.columns = section.columns.map(column => this.getColumnsInformation(column, isJSONOutput));
|
|
82
|
+
return sectionOutput;
|
|
79
83
|
}
|
|
80
84
|
/**
|
|
81
85
|
* Publish a modern page in SharePoint Online
|
|
@@ -99,5 +103,8 @@ export class Page {
|
|
|
99
103
|
}
|
|
100
104
|
return pageName;
|
|
101
105
|
}
|
|
106
|
+
static isVerticalSection(section) {
|
|
107
|
+
return section.layoutIndex === 2 && section?.controlData?.position?.sectionFactor === 12;
|
|
108
|
+
}
|
|
102
109
|
}
|
|
103
110
|
//# sourceMappingURL=Page.js.map
|
|
@@ -30,6 +30,10 @@ export var CanvasSectionTemplate;
|
|
|
30
30
|
/// Vertical
|
|
31
31
|
/// </summary>
|
|
32
32
|
CanvasSectionTemplate[CanvasSectionTemplate["Vertical"] = 6] = "Vertical";
|
|
33
|
+
/// <summary>
|
|
34
|
+
/// Flexible
|
|
35
|
+
/// </summary>
|
|
36
|
+
CanvasSectionTemplate[CanvasSectionTemplate["Flexible"] = 7] = "Flexible";
|
|
33
37
|
})(CanvasSectionTemplate || (CanvasSectionTemplate = {}));
|
|
34
38
|
/**
|
|
35
39
|
* Section background shading
|