@pnp/cli-microsoft365 11.6.0-beta.358a4f1 → 11.6.0-beta.bc374af

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.
Files changed (31) hide show
  1. package/.devproxy/api-specs/sharepoint-admin.yaml +40 -0
  2. package/.devproxy/api-specs/sharepoint.yaml +48 -0
  3. package/allCommands.json +1 -1
  4. package/allCommandsFull.json +1 -1
  5. package/dist/m365/entra/commands/user/user-recyclebinitem-list.js +5 -0
  6. package/dist/m365/outlook/commands/calendargroup/calendargroup-list.js +87 -0
  7. package/dist/m365/outlook/commands.js +1 -0
  8. package/dist/m365/spo/commands/agent/agent-add.js +172 -0
  9. package/dist/m365/spo/commands/list/list-set.js +186 -378
  10. package/dist/m365/spo/commands/navigation/navigation-node-get.js +18 -28
  11. package/dist/m365/spo/commands/navigation/navigation-node-list.js +14 -39
  12. package/dist/m365/spo/commands/site/site-accessrequest-setting-set.js +102 -0
  13. package/dist/m365/spo/commands/site/site-add.js +38 -8
  14. package/dist/m365/spo/commands/tenant/tenant-site-get.js +112 -0
  15. package/dist/m365/spo/commands.js +3 -0
  16. package/dist/utils/accessToken.js +21 -0
  17. package/dist/utils/brandCenter.js +29 -0
  18. package/docs/docs/cmd/entra/app/app-role-add.mdx +21 -0
  19. package/docs/docs/cmd/entra/app/app-role-list.mdx +20 -1
  20. package/docs/docs/cmd/entra/app/app-role-remove.mdx +22 -1
  21. package/docs/docs/cmd/entra/policy/policy-list.mdx +19 -0
  22. package/docs/docs/cmd/entra/resourcenamespace/resourcenamespace-list.mdx +19 -0
  23. package/docs/docs/cmd/outlook/calendargroup/calendargroup-list.mdx +120 -0
  24. package/docs/docs/cmd/spo/agent/agent-add.mdx +190 -0
  25. package/docs/docs/cmd/spo/list/list-set.mdx +19 -1
  26. package/docs/docs/cmd/spo/navigation/navigation-node-get.mdx +43 -10
  27. package/docs/docs/cmd/spo/navigation/navigation-node-list.mdx +36 -3
  28. package/docs/docs/cmd/spo/site/site-accessrequest-setting-set.mdx +77 -0
  29. package/docs/docs/cmd/spo/site/site-add.mdx +14 -5
  30. package/docs/docs/cmd/spo/tenant/tenant-site-get.mdx +479 -0
  31. package/package.json +1 -1
@@ -1,13 +1,18 @@
1
- var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
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 _SpoNavigationNodeGetCommand_instances, _SpoNavigationNodeGetCommand_initOptions, _SpoNavigationNodeGetCommand_initValidators;
1
+ import { globalOptionsZod } from '../../../../Command.js';
2
+ import { z } from 'zod';
7
3
  import request from '../../../../request.js';
8
4
  import { validation } from '../../../../utils/validation.js';
9
5
  import SpoCommand from '../../../base/SpoCommand.js';
10
6
  import commands from '../../commands.js';
7
+ export const options = z.strictObject({
8
+ ...globalOptionsZod.shape,
9
+ webUrl: z.string()
10
+ .refine(url => validation.isValidSharePointUrl(url) === true, {
11
+ error: e => `'${e.input}' is not a valid SharePoint Online site URL.`
12
+ })
13
+ .alias('u'),
14
+ id: z.int().positive()
15
+ });
11
16
  class SpoNavigationNodeGetCommand extends SpoCommand {
12
17
  get name() {
13
18
  return commands.NAVIGATION_NODE_GET;
@@ -15,25 +20,25 @@ class SpoNavigationNodeGetCommand extends SpoCommand {
15
20
  get description() {
16
21
  return 'Retrieve information about a specific navigation node';
17
22
  }
18
- constructor() {
19
- super();
20
- _SpoNavigationNodeGetCommand_instances.add(this);
21
- __classPrivateFieldGet(this, _SpoNavigationNodeGetCommand_instances, "m", _SpoNavigationNodeGetCommand_initOptions).call(this);
22
- __classPrivateFieldGet(this, _SpoNavigationNodeGetCommand_instances, "m", _SpoNavigationNodeGetCommand_initValidators).call(this);
23
+ get schema() {
24
+ return options;
23
25
  }
24
26
  async commandAction(logger, args) {
25
27
  if (this.verbose) {
26
28
  await logger.logToStderr(`Retrieving information about navigation node with id ${args.options.id}`);
27
29
  }
28
30
  const requestOptions = {
29
- url: `${args.options.webUrl}/_api/web/navigation/GetNodeById(${args.options.id})`,
31
+ url: `${args.options.webUrl}/_api/web/navigation/GetNodeById(${args.options.id})?$expand=Children,Children/Children,Children/Children/Children`,
30
32
  headers: {
31
- 'accept': 'application/json;odata=nometadata'
33
+ accept: 'application/json;odata=nometadata'
32
34
  },
33
35
  responseType: 'json'
34
36
  };
35
37
  try {
36
38
  const listInstance = await request.get(requestOptions);
39
+ if (listInstance['odata.null']) {
40
+ throw `No navigation node found with id ${args.options.id}.`;
41
+ }
37
42
  await logger.log(listInstance);
38
43
  }
39
44
  catch (err) {
@@ -41,20 +46,5 @@ class SpoNavigationNodeGetCommand extends SpoCommand {
41
46
  }
42
47
  }
43
48
  }
44
- _SpoNavigationNodeGetCommand_instances = new WeakSet(), _SpoNavigationNodeGetCommand_initOptions = function _SpoNavigationNodeGetCommand_initOptions() {
45
- this.options.unshift({
46
- option: '-u, --webUrl <webUrl>'
47
- }, {
48
- option: '--id <id>'
49
- });
50
- }, _SpoNavigationNodeGetCommand_initValidators = function _SpoNavigationNodeGetCommand_initValidators() {
51
- this.validators.push(async (args) => {
52
- const id = parseInt(args.options.id);
53
- if (isNaN(id)) {
54
- return `${args.options.id} is not a valid number`;
55
- }
56
- return validation.isValidSharePointUrl(args.options.webUrl);
57
- });
58
- };
59
49
  export default new SpoNavigationNodeGetCommand();
60
50
  //# sourceMappingURL=navigation-node-get.js.map
@@ -1,13 +1,18 @@
1
- var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
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 _SpoNavigationNodeListCommand_instances, _SpoNavigationNodeListCommand_initTelemetry, _SpoNavigationNodeListCommand_initOptions, _SpoNavigationNodeListCommand_initValidators;
1
+ import { globalOptionsZod } from '../../../../Command.js';
2
+ import { z } from 'zod';
7
3
  import { odata } from '../../../../utils/odata.js';
8
4
  import { validation } from '../../../../utils/validation.js';
9
5
  import SpoCommand from '../../../base/SpoCommand.js';
10
6
  import commands from '../../commands.js';
7
+ export const options = z.strictObject({
8
+ ...globalOptionsZod.shape,
9
+ webUrl: z.string()
10
+ .refine(url => validation.isValidSharePointUrl(url) === true, {
11
+ error: e => `'${e.input}' is not a valid SharePoint Online site URL.`
12
+ })
13
+ .alias('u'),
14
+ location: z.enum(['QuickLaunch', 'TopNavigationBar']).alias('l')
15
+ });
11
16
  class SpoNavigationNodeListCommand extends SpoCommand {
12
17
  get name() {
13
18
  return commands.NAVIGATION_NODE_LIST;
@@ -18,19 +23,15 @@ class SpoNavigationNodeListCommand extends SpoCommand {
18
23
  defaultProperties() {
19
24
  return ['Id', 'Title', 'Url'];
20
25
  }
21
- constructor() {
22
- super();
23
- _SpoNavigationNodeListCommand_instances.add(this);
24
- __classPrivateFieldGet(this, _SpoNavigationNodeListCommand_instances, "m", _SpoNavigationNodeListCommand_initTelemetry).call(this);
25
- __classPrivateFieldGet(this, _SpoNavigationNodeListCommand_instances, "m", _SpoNavigationNodeListCommand_initOptions).call(this);
26
- __classPrivateFieldGet(this, _SpoNavigationNodeListCommand_instances, "m", _SpoNavigationNodeListCommand_initValidators).call(this);
26
+ get schema() {
27
+ return options;
27
28
  }
28
29
  async commandAction(logger, args) {
29
30
  if (this.verbose) {
30
31
  await logger.logToStderr(`Retrieving navigation nodes...`);
31
32
  }
32
33
  try {
33
- const res = await odata.getAllItems(`${args.options.webUrl}/_api/web/navigation/${args.options.location.toLowerCase()}`);
34
+ const res = await odata.getAllItems(`${args.options.webUrl}/_api/web/navigation/${args.options.location.toLowerCase()}?$expand=Children,Children/Children,Children/Children/Children`);
34
35
  await logger.log(res);
35
36
  }
36
37
  catch (err) {
@@ -38,31 +39,5 @@ class SpoNavigationNodeListCommand extends SpoCommand {
38
39
  }
39
40
  }
40
41
  }
41
- _SpoNavigationNodeListCommand_instances = new WeakSet(), _SpoNavigationNodeListCommand_initTelemetry = function _SpoNavigationNodeListCommand_initTelemetry() {
42
- this.telemetry.push((args) => {
43
- Object.assign(this.telemetryProperties, {
44
- location: args.options.location
45
- });
46
- });
47
- }, _SpoNavigationNodeListCommand_initOptions = function _SpoNavigationNodeListCommand_initOptions() {
48
- this.options.unshift({
49
- option: '-u, --webUrl <webUrl>'
50
- }, {
51
- option: '-l, --location <location>',
52
- autocomplete: ['QuickLaunch', 'TopNavigationBar']
53
- });
54
- }, _SpoNavigationNodeListCommand_initValidators = function _SpoNavigationNodeListCommand_initValidators() {
55
- this.validators.push(async (args) => {
56
- const isValidSharePointUrl = validation.isValidSharePointUrl(args.options.webUrl);
57
- if (isValidSharePointUrl !== true) {
58
- return isValidSharePointUrl;
59
- }
60
- if (args.options.location !== 'QuickLaunch' &&
61
- args.options.location !== 'TopNavigationBar') {
62
- return `${args.options.location} is not a valid value for the location option. Allowed values are QuickLaunch|TopNavigationBar`;
63
- }
64
- return true;
65
- });
66
- };
67
42
  export default new SpoNavigationNodeListCommand();
68
43
  //# sourceMappingURL=navigation-node-list.js.map
@@ -0,0 +1,102 @@
1
+ import { z } from 'zod';
2
+ import { globalOptionsZod } from '../../../../Command.js';
3
+ import request from '../../../../request.js';
4
+ import { validation } from '../../../../utils/validation.js';
5
+ import SpoCommand from '../../../base/SpoCommand.js';
6
+ import commands from '../../commands.js';
7
+ export const options = z.strictObject({
8
+ ...globalOptionsZod.shape,
9
+ siteUrl: z.string()
10
+ .refine(url => validation.isValidSharePointUrl(url) === true, {
11
+ error: e => `'${e.input}' is not a valid SharePoint Online site URL.`
12
+ })
13
+ .alias('u'),
14
+ disabled: z.boolean().optional(),
15
+ ownerGroup: z.boolean().optional(),
16
+ email: z.string()
17
+ .refine(email => validation.isValidUserPrincipalName(email), {
18
+ error: e => `'${e.input}' is not a valid email address.`
19
+ }).optional(),
20
+ message: z.string().optional()
21
+ });
22
+ class SpoSiteAccessRequestSettingSetCommand extends SpoCommand {
23
+ get name() {
24
+ return commands.SITE_ACCESSREQUEST_SETTING_SET;
25
+ }
26
+ get description() {
27
+ return 'Update access requests for a specific site';
28
+ }
29
+ get schema() {
30
+ return options;
31
+ }
32
+ getRefinedSchema(schema) {
33
+ return schema
34
+ .refine(o => [o.disabled, o.ownerGroup, o.email].filter(v => v !== undefined).length === 1, {
35
+ error: 'Specify exactly one of disabled, ownerGroup, or email'
36
+ })
37
+ .refine(o => !(o.disabled && typeof o.message !== 'undefined'), {
38
+ error: 'The message option cannot be used when disabled is specified'
39
+ });
40
+ }
41
+ async commandAction(logger, args) {
42
+ if (this.verbose) {
43
+ await logger.logToStderr(`Updating access requests for site '${args.options.siteUrl}'...`);
44
+ }
45
+ try {
46
+ const { siteUrl, ownerGroup, email, message } = args.options;
47
+ const requestAccessEmail = email || '';
48
+ if (this.verbose) {
49
+ await logger.logToStderr(`Updating RequestAccessEmail to '${requestAccessEmail}'...`);
50
+ }
51
+ const requestPatchWeb = {
52
+ url: `${siteUrl}/_api/Web`,
53
+ headers: {
54
+ accept: 'application/json;odata=nometadata'
55
+ },
56
+ responseType: 'json',
57
+ data: {
58
+ RequestAccessEmail: requestAccessEmail
59
+ }
60
+ };
61
+ await request.patch(requestPatchWeb);
62
+ const useAccessRequestDefault = !!ownerGroup;
63
+ if (this.verbose) {
64
+ await logger.logToStderr(`Updating UseAccessRequestDefault to '${useAccessRequestDefault}'...`);
65
+ }
66
+ const requestUseDefault = {
67
+ url: `${siteUrl}/_api/Web/SetUseAccessRequestDefaultAndUpdate`,
68
+ headers: {
69
+ accept: 'application/json;odata=nometadata',
70
+ 'content-type': 'application/json;odata=nometadata'
71
+ },
72
+ responseType: 'json',
73
+ data: {
74
+ useAccessRequestDefault: useAccessRequestDefault
75
+ }
76
+ };
77
+ await request.post(requestUseDefault);
78
+ if (message !== undefined) {
79
+ if (this.verbose) {
80
+ await logger.logToStderr(`Updating access request message to '${message}'...`);
81
+ }
82
+ const requestSetMessage = {
83
+ url: `${siteUrl}/_api/Web/SetAccessRequestSiteDescriptionAndUpdate`,
84
+ headers: {
85
+ accept: 'application/json;odata=nometadata',
86
+ 'content-type': 'application/json;odata=nometadata'
87
+ },
88
+ responseType: 'json',
89
+ data: {
90
+ description: message
91
+ }
92
+ };
93
+ await request.post(requestSetMessage);
94
+ }
95
+ }
96
+ catch (err) {
97
+ this.handleRejectedODataJsonPromise(err);
98
+ }
99
+ }
100
+ }
101
+ export default new SpoSiteAccessRequestSettingSetCommand();
102
+ //# sourceMappingURL=site-accessrequest-setting-set.js.map
@@ -5,6 +5,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
5
5
  };
6
6
  var _SpoSiteAddCommand_instances, _SpoSiteAddCommand_initTelemetry, _SpoSiteAddCommand_initOptions, _SpoSiteAddCommand_initValidators;
7
7
  import { setTimeout } from 'timers/promises';
8
+ import { cli } from '../../../../cli/cli.js';
8
9
  import config from '../../../../config.js';
9
10
  import request from '../../../../request.js';
10
11
  import { formatting } from '../../../../utils/formatting.js';
@@ -12,6 +13,7 @@ import { spo } from '../../../../utils/spo.js';
12
13
  import { validation } from '../../../../utils/validation.js';
13
14
  import SpoCommand from '../../../base/SpoCommand.js';
14
15
  import commands from '../../commands.js';
16
+ import { brandCenter } from '../../../../utils/brandCenter.js';
15
17
  class SpoSiteAddCommand extends SpoCommand {
16
18
  get supportedLcids() {
17
19
  // Languages supported by SharePoint
@@ -43,7 +45,7 @@ class SpoSiteAddCommand extends SpoCommand {
43
45
  await logger.log(siteUrl);
44
46
  }
45
47
  async createModernSite(logger, args) {
46
- const isTeamSite = args.options.type !== 'CommunicationSite';
48
+ const isTeamSite = args.options.type !== 'CommunicationSite' && args.options.type !== 'BrandCenter';
47
49
  try {
48
50
  const spoUrl = await spo.getSpoUrl(logger, this.debug);
49
51
  if (this.verbose) {
@@ -128,6 +130,9 @@ class SpoSiteAddCommand extends SpoCommand {
128
130
  if (args.options.owners) {
129
131
  requestOptions.data.request.Owner = args.options.owners;
130
132
  }
133
+ if (args.options.type === 'BrandCenter') {
134
+ await this.addBrandCenter(requestOptions.data.request, logger, args.options.force || false);
135
+ }
131
136
  }
132
137
  const response = await request.post(requestOptions);
133
138
  if (isTeamSite) {
@@ -325,6 +330,27 @@ class SpoSiteAddCommand extends SpoCommand {
325
330
  this.handleRejectedPromise(err);
326
331
  }
327
332
  }
333
+ async addBrandCenter(requestData, logger, force) {
334
+ const brandingCenterConfiguration = await brandCenter.getBrandCenterConfiguration(logger, this.debug);
335
+ if (brandingCenterConfiguration.IsBrandCenterSiteFeatureEnabled) {
336
+ throw Error('Brand center site is already created in the tenant.');
337
+ }
338
+ const warningMessage = `You agree to activate this site as your official brand center site and turn on the brand center app for use in your organization. Storage locations will be created for uploading files to brand center and managing them. Any uploaded files will be stored in the cloud and managed in a public content delivery network (CDN). The files will be accessible to anyone who is able to extract the URLs that point to them.
339
+ Don't use this feature if your files contain proprietary information, or if you don't have the necessary cloud hosting rights to use them. After creation, that site cannot be deleted.`;
340
+ if (force) {
341
+ await logger.logToStderr(warningMessage);
342
+ }
343
+ else {
344
+ const result = await cli.promptForConfirmation({
345
+ message: `${warningMessage}\n\nDo you want to proceed?`
346
+ });
347
+ if (!result) {
348
+ throw Error('Operation cancelled by the user.');
349
+ }
350
+ }
351
+ const brandCenterFeatureId = '99cd6e8b-189b-4611-ae89-f89105876e43';
352
+ requestData.AdditionalSiteFeatureIds = [brandCenterFeatureId];
353
+ }
328
354
  }
329
355
  _SpoSiteAddCommand_instances = new WeakSet(), _SpoSiteAddCommand_initTelemetry = function _SpoSiteAddCommand_initTelemetry() {
330
356
  this.telemetry.push((args) => {
@@ -338,6 +364,7 @@ _SpoSiteAddCommand_instances = new WeakSet(), _SpoSiteAddCommand_initTelemetry =
338
364
  telemetryProps.lcid = args.options.lcid;
339
365
  telemetryProps.owners = typeof args.options.owners !== 'undefined';
340
366
  telemetryProps.withAppCatalog = args.options.withAppCatalog || false;
367
+ telemetryProps.force = args.options.force || false;
341
368
  if (isCommunicationSite) {
342
369
  telemetryProps.shareByEmailEnabled = args.options.shareByEmailEnabled || false;
343
370
  telemetryProps.siteDesign = args.options.siteDesign;
@@ -357,7 +384,7 @@ _SpoSiteAddCommand_instances = new WeakSet(), _SpoSiteAddCommand_initTelemetry =
357
384
  }, _SpoSiteAddCommand_initOptions = function _SpoSiteAddCommand_initOptions() {
358
385
  this.options.unshift({
359
386
  option: '--type [type]',
360
- autocomplete: ['TeamSite', 'CommunicationSite', 'ClassicSite']
387
+ autocomplete: ['TeamSite', 'CommunicationSite', 'ClassicSite', 'BrandCenter']
361
388
  }, {
362
389
  option: '-t, --title <title>'
363
390
  }, {
@@ -399,24 +426,27 @@ _SpoSiteAddCommand_instances = new WeakSet(), _SpoSiteAddCommand_initTelemetry =
399
426
  option: '--withAppCatalog'
400
427
  }, {
401
428
  option: '--wait'
429
+ }, {
430
+ option: '--force'
402
431
  });
403
432
  }, _SpoSiteAddCommand_initValidators = function _SpoSiteAddCommand_initValidators() {
404
433
  this.validators.push(async (args) => {
405
434
  const isClassicSite = args.options.type === 'ClassicSite';
406
- const isCommunicationSite = args.options.type === 'CommunicationSite';
435
+ const isCommunicationSite = args.options.type === 'CommunicationSite' || args.options.type === 'BrandCenter';
407
436
  const isTeamSite = isCommunicationSite === false && isClassicSite === false;
408
437
  if (args.options.type) {
409
438
  if (args.options.type !== 'TeamSite' &&
410
439
  args.options.type !== 'CommunicationSite' &&
411
- args.options.type !== 'ClassicSite') {
412
- return `${args.options.type} is not a valid site type. Allowed types are TeamSite, CommunicationSite, and ClassicSite`;
440
+ args.options.type !== 'ClassicSite' &&
441
+ args.options.type !== 'BrandCenter') {
442
+ return `${args.options.type} is not a valid site type. Allowed types are TeamSite, CommunicationSite, ClassicSite, and BrandCenter`;
413
443
  }
414
444
  }
415
445
  if (isTeamSite) {
416
446
  if (!args.options.alias) {
417
447
  return 'Required option alias missing';
418
448
  }
419
- if (args.options.url || args.options.siteDesign || args.options.removeDeletedSite || args.options.wait || args.options.shareByEmailEnabled || args.options.siteDesignId || args.options.timeZone || args.options.resourceQuota || args.options.resourceQuotaWarningLevel || args.options.storageQuota || args.options.storageQuotaWarningLevel || args.options.webTemplate) {
449
+ if (args.options.url || args.options.siteDesign || args.options.removeDeletedSite || args.options.wait || args.options.shareByEmailEnabled || args.options.siteDesignId || args.options.timeZone || args.options.resourceQuota || args.options.resourceQuotaWarningLevel || args.options.storageQuota || args.options.storageQuotaWarningLevel || args.options.webTemplate || args.options.force) {
420
450
  return "Type TeamSite supports only the parameters title, lcid, alias, owners, classification, isPublic, and description";
421
451
  }
422
452
  }
@@ -447,7 +477,7 @@ _SpoSiteAddCommand_instances = new WeakSet(), _SpoSiteAddCommand_initTelemetry =
447
477
  return 'Specify siteDesign or siteDesignId but not both';
448
478
  }
449
479
  if (args.options.timeZone || args.options.isPublic || args.options.removeDeletedSite || args.options.wait || args.options.alias || args.options.resourceQuota || args.options.resourceQuotaWarningLevel || args.options.storageQuota || args.options.storageQuotaWarningLevel || args.options.webTemplate) {
450
- return "Type CommunicationSite supports only the parameters url, title, lcid, classification, siteDesign, shareByEmailEnabled, siteDesignId, owners, and description";
480
+ return "Type CommunicationSite supports only the parameters url, title, lcid, classification, siteDesign, shareByEmailEnabled, siteDesignId, owners, description, and force";
451
481
  }
452
482
  }
453
483
  else {
@@ -500,7 +530,7 @@ _SpoSiteAddCommand_instances = new WeakSet(), _SpoSiteAddCommand_initTelemetry =
500
530
  if (args.options.storageQuotaWarningLevel > args.options.storageQuota) {
501
531
  return `storageQuotaWarningLevel cannot exceed storageQuota`;
502
532
  }
503
- if (args.options.classification || args.options.shareByEmailEnabled || args.options.siteDesignId || args.options.siteDesignId || args.options.alias || args.options.isPublic) {
533
+ if (args.options.classification || args.options.shareByEmailEnabled || args.options.siteDesignId || args.options.siteDesignId || args.options.alias || args.options.isPublic || args.options.force) {
504
534
  return "Type ClassicSite supports only the parameters url, title, lcid, storageQuota, storageQuotaWarningLevel, resourceQuota, resourceQuotaWarningLevel, webTemplate, owners, and description";
505
535
  }
506
536
  }
@@ -0,0 +1,112 @@
1
+ import { z } from 'zod';
2
+ import { globalOptionsZod } from '../../../../Command.js';
3
+ import request from '../../../../request.js';
4
+ import { formatting } from '../../../../utils/formatting.js';
5
+ import { spo } from '../../../../utils/spo.js';
6
+ import { validation } from '../../../../utils/validation.js';
7
+ import SpoCommand from '../../../base/SpoCommand.js';
8
+ import commands from '../../commands.js';
9
+ import { cli } from '../../../../cli/cli.js';
10
+ export const options = z.strictObject({
11
+ ...globalOptionsZod.shape,
12
+ id: z.uuid().optional().alias('i'),
13
+ title: z.string().optional().alias('t'),
14
+ url: z.string().refine(url => validation.isValidSharePointUrl(url) === true, {
15
+ message: 'Specify a valid SharePoint site URL'
16
+ }).optional().alias('u')
17
+ });
18
+ class SpoTenantSiteGetCommand extends SpoCommand {
19
+ get name() {
20
+ return commands.TENANT_SITE_GET;
21
+ }
22
+ get description() {
23
+ return 'Retrieves the tenant site information';
24
+ }
25
+ get schema() {
26
+ return options;
27
+ }
28
+ getRefinedSchema(schema) {
29
+ return schema.refine(o => [o.id, o.title, o.url].filter(v => v !== undefined).length === 1, {
30
+ error: `Specify exactly one of the following options: 'id', 'title', or 'url'.`
31
+ });
32
+ }
33
+ async commandAction(logger, args) {
34
+ if (this.verbose) {
35
+ await logger.logToStderr(`Retrieving tenant site information for site '${args.options.url || args.options.id || args.options.title}'...`);
36
+ }
37
+ try {
38
+ let siteUrl;
39
+ if (args.options.url) {
40
+ siteUrl = args.options.url;
41
+ }
42
+ else if (args.options.id) {
43
+ siteUrl = await this.getSiteUrlById(args.options.id, logger);
44
+ if (this.verbose) {
45
+ await logger.logToStderr(`Retrieved tenant site URL for site '${args.options.id}'...`);
46
+ }
47
+ }
48
+ else {
49
+ siteUrl = await this.getSiteUrlByTitle(args.options.title, logger);
50
+ if (this.verbose) {
51
+ await logger.logToStderr(`Retrieved tenant site URL for site '${args.options.title}'...`);
52
+ }
53
+ }
54
+ const site = await spo.getSiteAdminPropertiesByUrl(siteUrl, false, logger, this.verbose);
55
+ await logger.log(site);
56
+ }
57
+ catch (err) {
58
+ this.handleRejectedODataJsonPromise(err);
59
+ }
60
+ }
61
+ async getSiteUrlById(id, logger) {
62
+ if (this.verbose) {
63
+ await logger.logToStderr(`Retrieving tenant site URL for site '${id}'...`);
64
+ }
65
+ const adminUrl = await spo.getSpoAdminUrl(logger, this.debug);
66
+ const requestOptions = {
67
+ url: `${adminUrl}/_api/SPO.Tenant/sites('${id}')?$select=Url`,
68
+ headers: {
69
+ accept: 'application/json;odata=nometadata'
70
+ },
71
+ responseType: 'json'
72
+ };
73
+ const res = await request.get(requestOptions);
74
+ return res.Url;
75
+ }
76
+ async getSiteUrlByTitle(title, logger) {
77
+ if (this.verbose) {
78
+ await logger.logToStderr(`Retrieving tenant site URL for site '${title}'...`);
79
+ }
80
+ const adminUrl = await spo.getSpoAdminUrl(logger, this.debug);
81
+ const viewXml = `<View><Query><Where><And><IsNull><FieldRef Name="TimeDeleted"/></IsNull><Eq><FieldRef Name="Title"/><Value Type='Text'>${formatting.escapeXml(title)}</Value></Eq></And></Where></Query><ViewFields><FieldRef Name="Title"/><FieldRef Name="SiteUrl"/><FieldRef Name="SiteId"/></ViewFields></View>`;
82
+ const requestOptions = {
83
+ url: `${adminUrl}/_api/web/lists/GetByTitle('DO_NOT_DELETE_SPLIST_TENANTADMIN_AGGREGATED_SITECOLLECTIONS')/RenderListDataAsStream`,
84
+ headers: {
85
+ accept: 'application/json;odata=nometadata'
86
+ },
87
+ responseType: 'json',
88
+ data: {
89
+ parameters: {
90
+ ViewXml: viewXml,
91
+ DatesInUtc: true
92
+ }
93
+ }
94
+ };
95
+ const res = await request.post(requestOptions);
96
+ const rows = res.Row;
97
+ if (rows.length === 0) {
98
+ throw `The specified site '${title}' does not exist.`;
99
+ }
100
+ if (rows.length > 1) {
101
+ const resultAsKeyValuePair = rows.reduce((acc, cur) => {
102
+ acc[cur.SiteUrl] = { url: cur.SiteUrl };
103
+ return acc;
104
+ }, {});
105
+ const selection = await cli.handleMultipleResultsFound(`Multiple sites with title '${title}' found.`, resultAsKeyValuePair);
106
+ return selection.url;
107
+ }
108
+ return rows[0].SiteUrl;
109
+ }
110
+ }
111
+ export default new SpoTenantSiteGetCommand();
112
+ //# sourceMappingURL=tenant-site-get.js.map
@@ -1,5 +1,6 @@
1
1
  const prefix = 'spo';
2
2
  export default {
3
+ AGENT_ADD: `${prefix} agent add`,
3
4
  APP_ADD: `${prefix} app add`,
4
5
  APP_DEPLOY: `${prefix} app deploy`,
5
6
  APP_GET: `${prefix} app get`,
@@ -255,6 +256,7 @@ export default {
255
256
  SERVICEPRINCIPAL_PERMISSIONREQUEST_LIST: `${prefix} serviceprincipal permissionrequest list`,
256
257
  SERVICEPRINCIPAL_SET: `${prefix} serviceprincipal set`,
257
258
  SET: `${prefix} set`,
259
+ SITE_ACCESSREQUEST_SETTING_SET: `${prefix} site accessrequest setting set`,
258
260
  SITE_ADD: `${prefix} site add`,
259
261
  SITE_ADMIN_ADD: `${prefix} site admin add`,
260
262
  SITE_ADMIN_LIST: `${prefix} site admin list`,
@@ -337,6 +339,7 @@ export default {
337
339
  TENANT_RECYCLEBINITEM_RESTORE: `${prefix} tenant recyclebinitem restore`,
338
340
  TENANT_SETTINGS_LIST: `${prefix} tenant settings list`,
339
341
  TENANT_SETTINGS_SET: `${prefix} tenant settings set`,
342
+ TENANT_SITE_GET: `${prefix} tenant site get`,
340
343
  TERM_ADD: `${prefix} term add`,
341
344
  TERM_GET: `${prefix} term get`,
342
345
  TERM_LIST: `${prefix} term list`,
@@ -87,6 +87,27 @@ export const accessToken = {
87
87
  const payload = JSON.parse(payloadString);
88
88
  return { header, payload };
89
89
  },
90
+ getScopesFromAccessToken(accessToken) {
91
+ let scopes = [];
92
+ if (!accessToken || accessToken.length === 0) {
93
+ return scopes;
94
+ }
95
+ const chunks = accessToken.split('.');
96
+ if (chunks.length !== 3) {
97
+ return scopes;
98
+ }
99
+ const tokenString = Buffer.from(chunks[1], 'base64').toString();
100
+ try {
101
+ const token = JSON.parse(tokenString);
102
+ if (token.scp?.length > 0) {
103
+ scopes = token.scp.split(' ');
104
+ }
105
+ }
106
+ catch {
107
+ // Do nothing
108
+ }
109
+ return scopes;
110
+ },
90
111
  /**
91
112
  * Asserts the presence of a delegated or application-only access token.
92
113
  * @throws {CommandError} Will throw an error if the access token is not available.
@@ -0,0 +1,29 @@
1
+ import request from '../request.js';
2
+ import { spo } from './spo.js';
3
+ export const brandCenter = {
4
+ /**
5
+ * Gets the brand center configuration for the specified site
6
+ * @param logger Logger instance for verbose output
7
+ * @param debug Debug flag for detailed logging
8
+ * @returns Promise<BrandCenterConfiguration> Brand center configuration object
9
+ */
10
+ async getBrandCenterConfiguration(logger, debug = false) {
11
+ if (debug) {
12
+ await logger.logToStderr(`Retrieving brand center configuration...`);
13
+ }
14
+ const spoAdminUrl = await spo.getSpoAdminUrl(logger, debug);
15
+ const brandConfigRequestOptions = {
16
+ url: `${spoAdminUrl}/_api/SPO.Tenant/GetBrandCenterConfiguration`,
17
+ headers: {
18
+ 'accept': 'application/json;odata=nometadata'
19
+ },
20
+ responseType: 'json'
21
+ };
22
+ const brandConfig = await request.get(brandConfigRequestOptions);
23
+ if (debug) {
24
+ await logger.logToStderr(`Successfully retrieved brand center configuration`);
25
+ }
26
+ return brandConfig;
27
+ }
28
+ };
29
+ //# sourceMappingURL=brandCenter.js.map
@@ -1,4 +1,6 @@
1
1
  import Global from '../../_global.mdx';
2
+ import Tabs from '@theme/Tabs';
3
+ import TabItem from '@theme/TabItem';
2
4
 
3
5
  # entra app role add
4
6
 
@@ -49,6 +51,25 @@ For best performance use the `appObjectId` option to reference the Entra applica
49
51
 
50
52
  If the command finds multiple Entra application registrations with the specified app name, it will prompt you to disambiguate which app it should use, listing the discovered object IDs.
51
53
 
54
+ ## Permissions
55
+
56
+ <Tabs>
57
+ <TabItem value="Delegated">
58
+
59
+ | Resource | Permissions |
60
+ | --------------- | ------------------------- |
61
+ | Microsoft Graph | Application.ReadWrite.All |
62
+
63
+ </TabItem>
64
+ <TabItem value="Application">
65
+
66
+ | Resource | Permissions |
67
+ | --------------- | ------------------------- |
68
+ | Microsoft Graph | Application.ReadWrite.All |
69
+
70
+ </TabItem>
71
+ </Tabs>
72
+
52
73
  ## Examples
53
74
 
54
75
  Add role to the Entra application registration specified by its object ID
@@ -39,6 +39,25 @@ For best performance use the `appObjectId` option to reference the Entra applica
39
39
 
40
40
  If the command finds multiple Entra application registrations with the specified app name, it will prompt you to disambiguate which app it should use, listing the discovered object IDs.
41
41
 
42
+ ## Permissions
43
+
44
+ <Tabs>
45
+ <TabItem value="Delegated">
46
+
47
+ | Resource | Permissions |
48
+ | --------------- | -------------------- |
49
+ | Microsoft Graph | Application.Read.All |
50
+
51
+ </TabItem>
52
+ <TabItem value="Application">
53
+
54
+ | Resource | Permissions |
55
+ | --------------- | -------------------- |
56
+ | Microsoft Graph | Application.Read.All |
57
+
58
+ </TabItem>
59
+ </Tabs>
60
+
42
61
  ## Examples
43
62
 
44
63
  Get roles for the Entra application registration specified by its object ID
@@ -118,4 +137,4 @@ m365 entra app role list --appName "My app"
118
137
  ```
119
138
 
120
139
  </TabItem>
121
- </Tabs>
140
+ </Tabs>