@pnp/cli-microsoft365 10.9.0 → 10.10.0-beta.1bb5ba0

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 (84) hide show
  1. package/.devproxy/api-specs/sharepoint-admin.yaml +67 -0
  2. package/.devproxy/api-specs/sharepoint.yaml +230 -0
  3. package/.devproxy/devproxyrc.json +48 -0
  4. package/.devproxy/generate-openapi-spec.json +24 -0
  5. package/.devproxy/spo-csom-types.json +54 -0
  6. package/.eslintrc.cjs +8 -0
  7. package/allCommands.json +1 -1
  8. package/allCommandsFull.json +1 -1
  9. package/dist/cli/cli.js +2 -2
  10. package/dist/m365/app/commands/permission/permission-add.js +8 -1
  11. package/dist/m365/base/SpoCommand.js +1 -0
  12. package/dist/m365/entra/commands/app/app-permission-add.js +5 -18
  13. package/dist/m365/entra/commands/organization/organization-set.js +104 -0
  14. package/dist/m365/entra/commands.js +1 -0
  15. package/dist/m365/file/commands/file-copy.js +2 -2
  16. package/dist/m365/file/commands/file-move.js +1 -1
  17. package/dist/m365/planner/commands/tenant/tenant-settings-list.js +0 -3
  18. package/dist/m365/spo/commands/contenttype/contenttype-add.js +3 -65
  19. package/dist/m365/spo/commands/contenttype/contenttype-field-remove.js +5 -50
  20. package/dist/m365/spo/commands/contenttype/contenttype-field-set.js +4 -48
  21. package/dist/m365/spo/commands/contenttype/contenttype-set.js +8 -58
  22. package/dist/m365/spo/commands/contenttype/contenttype-sync.js +2 -2
  23. package/dist/m365/spo/commands/folder/folder-sharinglink-add.js +1 -1
  24. package/dist/m365/spo/commands/folder/folder-sharinglink-clear.js +1 -1
  25. package/dist/m365/spo/commands/folder/folder-sharinglink-get.js +1 -1
  26. package/dist/m365/spo/commands/folder/folder-sharinglink-list.js +1 -1
  27. package/dist/m365/spo/commands/folder/folder-sharinglink-remove.js +1 -1
  28. package/dist/m365/spo/commands/folder/folder-sharinglink-set.js +1 -1
  29. package/dist/m365/spo/commands/list/list-view-add.js +140 -87
  30. package/dist/m365/spo/commands/listitem/listitem-batch-set.js +3 -27
  31. package/dist/m365/spo/commands/page/Page.js +16 -1
  32. package/dist/m365/spo/commands/page/clientsidepages.js +4 -0
  33. package/dist/m365/spo/commands/page/page-add.js +1 -1
  34. package/dist/m365/spo/commands/page/page-clientsidewebpart-add.js +2 -8
  35. package/dist/m365/spo/commands/page/page-control-remove.js +99 -0
  36. package/dist/m365/spo/commands/page/page-control-set.js +1 -1
  37. package/dist/m365/spo/commands/page/page-header-set.js +9 -40
  38. package/dist/m365/spo/commands/page/page-publish.js +2 -10
  39. package/dist/m365/spo/commands/page/page-section-add.js +75 -21
  40. package/dist/m365/spo/commands/page/page-set.js +1 -1
  41. package/dist/m365/spo/commands/site/site-admin-add.js +1 -12
  42. package/dist/m365/spo/commands/site/site-set.js +2 -2
  43. package/dist/m365/spo/commands.js +1 -0
  44. package/dist/utils/entraServicePrincipal.js +21 -0
  45. package/dist/utils/md.js +3 -3
  46. package/dist/utils/spo.js +87 -3
  47. package/docs/docs/cmd/_global.mdx +1 -1
  48. package/docs/docs/cmd/entra/organization/organization-set.mdx +84 -0
  49. package/docs/docs/cmd/planner/bucket/bucket-add.mdx +19 -0
  50. package/docs/docs/cmd/planner/bucket/bucket-get.mdx +19 -0
  51. package/docs/docs/cmd/planner/bucket/bucket-list.mdx +19 -0
  52. package/docs/docs/cmd/planner/bucket/bucket-remove.mdx +21 -0
  53. package/docs/docs/cmd/planner/bucket/bucket-set.mdx +21 -0
  54. package/docs/docs/cmd/planner/plan/plan-add.mdx +19 -0
  55. package/docs/docs/cmd/planner/plan/plan-get.mdx +19 -0
  56. package/docs/docs/cmd/planner/plan/plan-list.mdx +19 -0
  57. package/docs/docs/cmd/planner/plan/plan-remove.mdx +21 -0
  58. package/docs/docs/cmd/planner/plan/plan-set.mdx +19 -0
  59. package/docs/docs/cmd/planner/roster/roster-add.mdx +19 -0
  60. package/docs/docs/cmd/planner/roster/roster-get.mdx +19 -0
  61. package/docs/docs/cmd/planner/roster/roster-member-add.mdx +19 -0
  62. package/docs/docs/cmd/planner/roster/roster-member-get.mdx +19 -0
  63. package/docs/docs/cmd/planner/roster/roster-member-list.mdx +19 -0
  64. package/docs/docs/cmd/planner/roster/roster-member-remove.mdx +21 -0
  65. package/docs/docs/cmd/planner/roster/roster-plan-list.mdx +19 -0
  66. package/docs/docs/cmd/planner/roster/roster-remove.mdx +21 -0
  67. package/docs/docs/cmd/planner/task/task-add.mdx +19 -0
  68. package/docs/docs/cmd/planner/task/task-checklistitem-add.mdx +19 -0
  69. package/docs/docs/cmd/planner/task/task-checklistitem-list.mdx +19 -0
  70. package/docs/docs/cmd/planner/task/task-checklistitem-remove.mdx +21 -0
  71. package/docs/docs/cmd/planner/task/task-get.mdx +27 -0
  72. package/docs/docs/cmd/planner/task/task-list.mdx +19 -0
  73. package/docs/docs/cmd/planner/task/task-reference-add.mdx +19 -0
  74. package/docs/docs/cmd/planner/task/task-reference-list.mdx +19 -0
  75. package/docs/docs/cmd/planner/task/task-reference-remove.mdx +21 -0
  76. package/docs/docs/cmd/planner/task/task-remove.mdx +21 -0
  77. package/docs/docs/cmd/planner/task/task-set.mdx +19 -0
  78. package/docs/docs/cmd/planner/tenant/tenant-settings-list.mdx +44 -7
  79. package/docs/docs/cmd/planner/tenant/tenant-settings-set.mdx +43 -8
  80. package/docs/docs/cmd/spo/list/list-view-add.mdx +53 -10
  81. package/docs/docs/cmd/spo/page/page-control-remove.mdx +56 -0
  82. package/docs/docs/cmd/spo/page/page-section-add.mdx +22 -2
  83. package/npm-shrinkwrap.json +2324 -536
  84. package/package.json +17 -14
@@ -35,7 +35,7 @@ class SpoFolderSharingLinkGetCommand extends SpoCommand {
35
35
  const relFolderUrl = await spo.getFolderServerRelativeUrl(args.options.webUrl, args.options.folderUrl, args.options.folderId);
36
36
  const absoluteFolderUrl = urlUtil.getAbsoluteUrl(args.options.webUrl, relFolderUrl);
37
37
  const folderUrl = new URL(absoluteFolderUrl);
38
- const siteId = await spo.getSiteId(args.options.webUrl);
38
+ const siteId = await spo.getSiteIdByMSGraph(args.options.webUrl);
39
39
  const drive = await driveItem.getDriveByUrl(siteId, folderUrl);
40
40
  const itemId = await driveItem.getDriveItemId(drive, folderUrl);
41
41
  const requestUrl = `https://graph.microsoft.com/v1.0/drives/${drive.id}/items/${itemId}/permissions/${args.options.id}`;
@@ -40,7 +40,7 @@ class SpoFolderSharingLinkListCommand extends SpoCommand {
40
40
  const relFolderUrl = await spo.getFolderServerRelativeUrl(args.options.webUrl, args.options.folderUrl, args.options.folderId, logger, args.options.verbose);
41
41
  const absoluteFolderUrl = urlUtil.getAbsoluteUrl(args.options.webUrl, relFolderUrl);
42
42
  const folderUrl = new URL(absoluteFolderUrl);
43
- const siteId = await spo.getSiteId(args.options.webUrl);
43
+ const siteId = await spo.getSiteIdByMSGraph(args.options.webUrl);
44
44
  const driveDetails = await drive.getDriveByUrl(siteId, folderUrl, logger, args.options.verbose);
45
45
  const itemId = await drive.getDriveItemId(driveDetails, folderUrl, logger, args.options.verbose);
46
46
  let requestUrl = `https://graph.microsoft.com/v1.0/drives/${driveDetails.id}/items/${itemId}/permissions?$filter=Link ne null`;
@@ -37,7 +37,7 @@ class SpoFolderSharingLinkRemoveCommand extends SpoCommand {
37
37
  const relFolderUrl = await spo.getFolderServerRelativeUrl(args.options.webUrl, args.options.folderUrl, args.options.folderId, logger, args.options.verbose);
38
38
  const absoluteFolderUrl = urlUtil.getAbsoluteUrl(args.options.webUrl, relFolderUrl);
39
39
  const folderUrl = new URL(absoluteFolderUrl);
40
- const siteId = await spo.getSiteId(args.options.webUrl);
40
+ const siteId = await spo.getSiteIdByMSGraph(args.options.webUrl);
41
41
  const driveDetails = await drive.getDriveByUrl(siteId, folderUrl, logger, args.options.verbose);
42
42
  const itemId = await drive.getDriveItemId(driveDetails, folderUrl, logger, args.options.verbose);
43
43
  const requestOptions = {
@@ -35,7 +35,7 @@ class SpoFolderSharingLinkSetCommand extends SpoCommand {
35
35
  const relFolderUrl = await spo.getFolderServerRelativeUrl(args.options.webUrl, args.options.folderUrl, args.options.folderId, logger, args.options.verbose);
36
36
  const absoluteFolderUrl = urlUtil.getAbsoluteUrl(args.options.webUrl, relFolderUrl);
37
37
  const folderUrl = new URL(absoluteFolderUrl);
38
- const siteId = await spo.getSiteId(args.options.webUrl);
38
+ const siteId = await spo.getSiteIdByMSGraph(args.options.webUrl);
39
39
  const driveDetails = await drive.getDriveByUrl(siteId, folderUrl, logger, args.options.verbose);
40
40
  const itemId = await drive.getDriveItemId(driveDetails, folderUrl, logger, args.options.verbose);
41
41
  const requestOptions = {
@@ -1,53 +1,126 @@
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 _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
- constructor() {
21
- super();
22
- _SpoListViewAddCommand_instances.add(this);
23
- __classPrivateFieldGet(this, _SpoListViewAddCommand_instances, "m", _SpoListViewAddCommand_initTelemetry).call(this);
24
- __classPrivateFieldGet(this, _SpoListViewAddCommand_instances, "m", _SpoListViewAddCommand_initOptions).call(this);
25
- __classPrivateFieldGet(this, _SpoListViewAddCommand_instances, "m", _SpoListViewAddCommand_initValidators).call(this);
26
- __classPrivateFieldGet(this, _SpoListViewAddCommand_instances, "m", _SpoListViewAddCommand_initOptionSets).call(this);
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
- getRestUrl(options) {
59
- let result = `${options.webUrl}/_api/web/`;
60
- if (options.listId) {
61
- result += `lists(guid'${formatting.encodeQueryParameter(options.listId)}')`;
62
- }
63
- else if (options.listTitle) {
64
- result += `lists/getByTitle('${formatting.encodeQueryParameter(options.listTitle)}')`;
65
- }
66
- else if (options.listUrl) {
67
- result += `GetList('${formatting.encodeQueryParameter(urlUtil.getServerRelativePath(options.webUrl, options.listUrl))}')`;
68
- }
69
- result += '/views/add';
70
- return result;
71
- }
72
- }
73
- _SpoListViewAddCommand_instances = new WeakSet(), _SpoListViewAddCommand_initTelemetry = function _SpoListViewAddCommand_initTelemetry() {
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 (args.options.listId && !validation.isValidGuid(args.options.listId)) {
97
- return `${args.options.listId} in option listId is not a valid GUID`;
148
+ if (options.type === 'gallery') {
149
+ requestBody.ViewType2 = 'TILES';
150
+ return;
98
151
  }
99
- if (args.options.rowLimit !== undefined) {
100
- if (isNaN(args.options.rowLimit)) {
101
- return `${args.options.rowLimit} is not a number`;
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 (+args.options.rowLimit <= 0) {
104
- return 'rowLimit option must be greater than 0.';
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
- if (args.options.personal && args.options.default) {
108
- return 'Default view cannot be a personal view.';
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
@@ -10,7 +10,6 @@ import request from '../../../../request.js';
10
10
  import { formatting } from '../../../../utils/formatting.js';
11
11
  import { odata } from '../../../../utils/odata.js';
12
12
  import { spo } from '../../../../utils/spo.js';
13
- import { urlUtil } from '../../../../utils/urlUtil.js';
14
13
  import { validation } from '../../../../utils/validation.js';
15
14
  import SpoCommand from '../../../base/SpoCommand.js';
16
15
  import commands from '../../commands.js';
@@ -41,7 +40,9 @@ class SpoListItemBatchSetCommand extends SpoCommand {
41
40
  if (!jsonContent[0].hasOwnProperty(idColumn)) {
42
41
  throw `The specified value for idColumn does not exist in the array. Specified idColumn is '${args.options.idColumn || 'ID'}'. Please specify the correct value.`;
43
42
  }
44
- const listId = await this.getListId(args.options, logger);
43
+ const listId = args.options.listId ?
44
+ args.options.listId :
45
+ await spo.getListId(args.options.webUrl, args.options.listTitle, args.options.listUrl, logger, this.verbose);
45
46
  const fields = await this.getListFields(args.options, listId, jsonContent, idColumn, logger);
46
47
  const userFields = fields.filter(field => field.TypeAsString === 'UserMulti' || field.TypeAsString === 'User');
47
48
  const resolvedUsers = await this.getUsersFromCsv(args.options.webUrl, jsonContent, userFields);
@@ -151,31 +152,6 @@ class SpoListItemBatchSetCommand extends SpoCommand {
151
152
  }
152
153
  return fields;
153
154
  }
154
- async getListId(options, logger) {
155
- if (options.listId) {
156
- return options.listId;
157
- }
158
- if (this.verbose) {
159
- await logger.logToStderr('Retrieving list id');
160
- }
161
- let listUrl = `${options.webUrl}/_api/web`;
162
- if (options.listTitle) {
163
- listUrl += `/lists/getByTitle('${formatting.encodeQueryParameter(options.listTitle)}')`;
164
- }
165
- else {
166
- const listServerRelativeUrl = urlUtil.getServerRelativePath(options.webUrl, options.listUrl);
167
- listUrl += `/GetList('${formatting.encodeQueryParameter(listServerRelativeUrl)}')`;
168
- }
169
- const requestOptions = {
170
- url: `${listUrl}?$select=Id`,
171
- headers: {
172
- 'accept': 'application/json;odata=nometadata'
173
- },
174
- responseType: 'json'
175
- };
176
- const listResult = await request.get(requestOptions);
177
- return listResult.Id;
178
- }
179
155
  async getUsersFromCsv(webUrl, jsonContent, userFields) {
180
156
  if (userFields.length === 0) {
181
157
  return [];
@@ -25,7 +25,7 @@ export class Page {
25
25
  }
26
26
  return ClientSidePage.fromHtml(res.ListItemAllFields.CanvasContent1);
27
27
  }
28
- static async checkout(name, webUrl, logger, debug, verbose) {
28
+ static async checkout(name, webUrl, logger, verbose) {
29
29
  if (verbose) {
30
30
  await logger.log(`Checking out ${name} page...`);
31
31
  }
@@ -77,6 +77,21 @@ export class Page {
77
77
  columns: section.columns.map(column => this.getColumnsInformation(column, isJSONOutput))
78
78
  };
79
79
  }
80
+ /**
81
+ * Publish a modern page in SharePoint Online
82
+ * @param webUrl Absolute URL of the SharePoint site where the page is located
83
+ * @param pageName List relative url of the page to publish
84
+ */
85
+ static async publishPage(webUrl, pageName) {
86
+ const filePath = `${urlUtil.getServerRelativeSiteUrl(webUrl)}/SitePages/${pageName}`;
87
+ const requestOptions = {
88
+ url: `${webUrl}/_api/web/GetFileByServerRelativePath(DecodedUrl='${formatting.encodeQueryParameter(filePath)}')/Publish()`,
89
+ headers: {
90
+ accept: 'application/json;odata=nometadata'
91
+ }
92
+ };
93
+ await request.post(requestOptions);
94
+ }
80
95
  static getPageNameWithExtension(name) {
81
96
  let pageName = name;
82
97
  if (pageName.indexOf('.aspx') < 0) {
@@ -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
@@ -63,7 +63,7 @@ class SpoPageAddCommand extends SpoCommand {
63
63
  itemId = template.UniqueId;
64
64
  const file = await spo.getFileAsListItemByUrl(args.options.webUrl, serverRelativeFileUrl, logger, this.verbose);
65
65
  const listItemId = file.Id;
66
- const pageProps = await Page.checkout(pageName, args.options.webUrl, logger, this.debug, this.verbose);
66
+ const pageProps = await Page.checkout(pageName, args.options.webUrl, logger, this.verbose);
67
67
  if (pageProps) {
68
68
  pageId = pageProps.Id;
69
69
  bannerImageUrl = pageProps.BannerImageUrl;
@@ -11,6 +11,7 @@ import { validation } from '../../../../utils/validation.js';
11
11
  import SpoCommand from '../../../base/SpoCommand.js';
12
12
  import commands from '../../commands.js';
13
13
  import { StandardWebPartUtils } from '../../StandardWebPartTypes.js';
14
+ import { Page } from './Page.js';
14
15
  class SpoPageClientSideWebPartAddCommand extends SpoCommand {
15
16
  get name() {
16
17
  return commands.PAGE_CLIENTSIDEWEBPART_ADD;
@@ -52,14 +53,7 @@ class SpoPageClientSideWebPartAddCommand extends SpoCommand {
52
53
  };
53
54
  let page = await request.get(requestOptions);
54
55
  if (!page.IsPageCheckedOutToCurrentUser) {
55
- const requestOptions = {
56
- url: `${args.options.webUrl}/_api/sitepages/pages/GetByUrl('sitepages/${formatting.encodeQueryParameter(pageFullName)}')/checkoutpage`,
57
- headers: {
58
- 'accept': 'application/json;odata=nometadata'
59
- },
60
- responseType: 'json'
61
- };
62
- page = await request.post(requestOptions);
56
+ page = await Page.checkout(pageFullName, args.options.webUrl, logger, this.verbose);
63
57
  }
64
58
  if (page) {
65
59
  layoutWebpartsContent = page.LayoutWebpartsContent;
@@ -0,0 +1,99 @@
1
+ import { globalOptionsZod } from '../../../../Command.js';
2
+ import { z } from 'zod';
3
+ import commands from '../../commands.js';
4
+ import { validation } from '../../../../utils/validation.js';
5
+ import SpoCommand from '../../../base/SpoCommand.js';
6
+ import { zod } from '../../../../utils/zod.js';
7
+ import { cli } from '../../../../cli/cli.js';
8
+ import request from '../../../../request.js';
9
+ import { formatting } from '../../../../utils/formatting.js';
10
+ import { Page } from './Page.js';
11
+ import { urlUtil } from '../../../../utils/urlUtil.js';
12
+ const options = globalOptionsZod
13
+ .extend({
14
+ webUrl: zod.alias('u', z.string())
15
+ .refine(url => validation.isValidSharePointUrl(url) === true, url => ({
16
+ message: `'${url}' is not a valid SharePoint URL.`
17
+ })),
18
+ pageName: zod.alias('n', z.string()),
19
+ id: zod.alias('i', z.string())
20
+ .refine(id => validation.isValidGuid(id), id => ({
21
+ message: `'${id}' is not a valid GUID.`
22
+ })),
23
+ draft: z.boolean().optional(),
24
+ force: zod.alias('f', z.boolean().optional())
25
+ })
26
+ .strict();
27
+ class SpoPageControlRemoveCommand extends SpoCommand {
28
+ get name() {
29
+ return commands.PAGE_CONTROL_REMOVE;
30
+ }
31
+ get description() {
32
+ return 'Removes a control from a modern page';
33
+ }
34
+ get schema() {
35
+ return options;
36
+ }
37
+ async commandAction(logger, args) {
38
+ if (!args.options.force) {
39
+ const result = await cli.promptForConfirmation({ message: `Are you sure you want to delete control '${args.options.id}' on page '${args.options.pageName}'?` });
40
+ if (!result) {
41
+ return;
42
+ }
43
+ }
44
+ try {
45
+ if (this.verbose) {
46
+ await logger.logToStderr(`Getting page properties for page '${args.options.pageName}'...`);
47
+ }
48
+ const pageName = urlUtil.removeLeadingSlashes(args.options.pageName.toLowerCase().endsWith('.aspx') ? args.options.pageName : `${args.options.pageName}.aspx`);
49
+ let requestOptions = {
50
+ url: `${args.options.webUrl}/_api/SitePages/Pages/GetByUrl('SitePages/${formatting.encodeQueryParameter(pageName)}')?$select=CanvasContent1`,
51
+ headers: {
52
+ accept: 'application/json;odata=nometadata'
53
+ },
54
+ responseType: 'json'
55
+ };
56
+ const pageProps = await request.get(requestOptions);
57
+ if (!pageProps.CanvasContent1) {
58
+ throw `Page '${pageName}' doesn't contain canvas control '${args.options.id}'.`;
59
+ }
60
+ const pageControls = JSON.parse(pageProps.CanvasContent1);
61
+ const hasControl = pageControls.some(control => control.id?.toLowerCase() === args.options.id.toLowerCase());
62
+ if (!hasControl) {
63
+ throw `Control with ID '${args.options.id}' was not found on page '${pageName}'.`;
64
+ }
65
+ if (this.verbose) {
66
+ await logger.logToStderr('Checking out page...');
67
+ }
68
+ const page = await Page.checkout(pageName, args.options.webUrl, logger, this.verbose);
69
+ const canvasContent = JSON.parse(page.CanvasContent1);
70
+ if (this.verbose) {
71
+ await logger.logToStderr(`Removing control with ID '${args.options.id}' from page...`);
72
+ }
73
+ const pageContent = canvasContent.filter(control => !control.id || control.id.toLowerCase() !== args.options.id.toLowerCase());
74
+ requestOptions = {
75
+ url: `${args.options.webUrl}/_api/SitePages/Pages/GetByUrl('SitePages/${formatting.encodeQueryParameter(pageName)}')/SavePageAsDraft`,
76
+ headers: {
77
+ 'content-type': 'application/json;odata=nometadata',
78
+ accept: 'application/json;odata=nometadata'
79
+ },
80
+ responseType: 'json',
81
+ data: {
82
+ CanvasContent1: JSON.stringify(pageContent)
83
+ }
84
+ };
85
+ await request.patch(requestOptions);
86
+ if (!args.options.draft) {
87
+ if (this.verbose) {
88
+ await logger.logToStderr(`Republishing page...`);
89
+ }
90
+ await Page.publishPage(args.options.webUrl, pageName);
91
+ }
92
+ }
93
+ catch (err) {
94
+ this.handleRejectedODataJsonPromise(err);
95
+ }
96
+ }
97
+ }
98
+ export default new SpoPageControlRemoveCommand();
99
+ //# sourceMappingURL=page-control-remove.js.map
@@ -50,7 +50,7 @@ class SpoPageControlSetCommand extends SpoCommand {
50
50
  await logger.logToStderr(`Control with ID ${args.options.id} found on the page`);
51
51
  }
52
52
  // Check out the page
53
- const page = await Page.checkout(pageName, args.options.webUrl, logger, this.debug, this.verbose);
53
+ const page = await Page.checkout(pageName, args.options.webUrl, logger, this.verbose);
54
54
  // Update the web part data
55
55
  const canvasContent = JSON.parse(page.CanvasContent1);
56
56
  if (this.debug) {
@@ -6,9 +6,11 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
6
6
  var _SpoPageHeaderSetCommand_instances, _SpoPageHeaderSetCommand_initTelemetry, _SpoPageHeaderSetCommand_initOptions, _SpoPageHeaderSetCommand_initValidators;
7
7
  import request from '../../../../request.js';
8
8
  import { formatting } from '../../../../utils/formatting.js';
9
+ import { spo } from '../../../../utils/spo.js';
9
10
  import { validation } from '../../../../utils/validation.js';
10
11
  import SpoCommand from '../../../base/SpoCommand.js';
11
12
  import commands from '../../commands.js';
13
+ import { Page } from './Page.js';
12
14
  const BannerWebPartId = 'cbe7b0a9-3504-44dd-a3a3-0e5cacd07788';
13
15
  class SpoPageHeaderSetCommand extends SpoCommand {
14
16
  get name() {
@@ -148,14 +150,7 @@ class SpoPageHeaderSetCommand extends SpoCommand {
148
150
  pageData = await request.get(requestOptions);
149
151
  }
150
152
  else {
151
- const requestOptions = {
152
- url: `${args.options.webUrl}/_api/sitepages/pages/GetByUrl('sitepages/${formatting.encodeQueryParameter(pageFullName)}')/checkoutpage`,
153
- headers: {
154
- 'accept': 'application/json;odata=nometadata'
155
- },
156
- responseType: 'json'
157
- };
158
- pageData = await request.post(requestOptions);
153
+ pageData = await Page.checkout(pageFullName, args.options.webUrl, logger, this.verbose);
159
154
  }
160
155
  switch (args.options.type) {
161
156
  case 'None':
@@ -220,23 +215,23 @@ class SpoPageHeaderSetCommand extends SpoCommand {
220
215
  }
221
216
  else {
222
217
  const res = await Promise.all([
223
- this.getSiteId(args.options.webUrl, this.verbose, logger),
224
- this.getWebId(args.options.webUrl, this.verbose, logger),
218
+ spo.getSiteIdBySPApi(args.options.webUrl, logger, this.verbose),
219
+ spo.getWebId(args.options.webUrl, logger, this.verbose),
225
220
  this.getImageInfo(args.options.webUrl, args.options.imageUrl, this.verbose, logger)
226
221
  ]);
227
222
  header.serverProcessedContent.customMetadata = {
228
223
  imageSource: {
229
- siteId: res[0].Id,
230
- webId: res[1].Id,
224
+ siteId: res[0],
225
+ webId: res[1],
231
226
  listId: res[2].ListId,
232
227
  uniqueId: res[2].UniqueId
233
228
  }
234
229
  };
235
230
  const properties = header.properties;
236
231
  properties.listId = res[2].ListId;
237
- properties.siteId = res[0].Id;
232
+ properties.siteId = res[0];
238
233
  properties.uniqueId = res[2].UniqueId;
239
- properties.webId = res[1].Id;
234
+ properties.webId = res[1];
240
235
  header.properties = properties;
241
236
  }
242
237
  }
@@ -306,32 +301,6 @@ class SpoPageHeaderSetCommand extends SpoCommand {
306
301
  this.handleRejectedODataJsonPromise(err);
307
302
  }
308
303
  }
309
- async getSiteId(siteUrl, verbose, logger) {
310
- if (verbose) {
311
- await logger.logToStderr(`Retrieving information about the site collection...`);
312
- }
313
- const requestOptions = {
314
- url: `${siteUrl}/_api/site?$select=Id`,
315
- headers: {
316
- accept: 'application/json;odata=nometadata'
317
- },
318
- responseType: 'json'
319
- };
320
- return request.get(requestOptions);
321
- }
322
- async getWebId(siteUrl, verbose, logger) {
323
- if (verbose) {
324
- await logger.logToStderr(`Retrieving information about the site...`);
325
- }
326
- const requestOptions = {
327
- url: `${siteUrl}/_api/web?$select=Id`,
328
- headers: {
329
- accept: 'application/json;odata=nometadata'
330
- },
331
- responseType: 'json'
332
- };
333
- return request.get(requestOptions);
334
- }
335
304
  async getImageInfo(siteUrl, imageUrl, verbose, logger) {
336
305
  if (verbose) {
337
306
  await logger.logToStderr(`Retrieving information about the header image...`);