@kwiz/common 1.0.126 → 1.0.128

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 (101) hide show
  1. package/.github/workflows/npm-publish.yml +24 -24
  2. package/.madgerc +2 -2
  3. package/LICENSE +21 -21
  4. package/fix-folder-imports.js +26 -26
  5. package/lib/cjs/types/libs/msal.types.js +26 -26
  6. package/lib/cjs/utils/auth/common.js +84 -37
  7. package/lib/cjs/utils/auth/common.js.map +1 -1
  8. package/lib/cjs/utils/sharepoint.rest/user.js +11 -11
  9. package/lib/esm/types/libs/msal.types.js +26 -26
  10. package/lib/esm/utils/auth/common.js +84 -38
  11. package/lib/esm/utils/auth/common.js.map +1 -1
  12. package/lib/esm/utils/sharepoint.rest/user.js +11 -11
  13. package/lib/types/types/auth.d.ts +7 -0
  14. package/lib/types/utils/auth/common.d.ts +2 -0
  15. package/package.json +81 -81
  16. package/readme.md +17 -17
  17. package/src/_dependencies.ts +12 -12
  18. package/src/config.ts +17 -17
  19. package/src/helpers/Guid.ts +181 -181
  20. package/src/helpers/base64.ts +173 -173
  21. package/src/helpers/browser.test.js +13 -13
  22. package/src/helpers/browser.ts +1498 -1498
  23. package/src/helpers/browserinfo.ts +292 -292
  24. package/src/helpers/collections.base.test.js +25 -25
  25. package/src/helpers/collections.base.ts +437 -437
  26. package/src/helpers/collections.ts +107 -107
  27. package/src/helpers/color.ts +54 -54
  28. package/src/helpers/cookies.ts +59 -59
  29. package/src/helpers/date.test.js +119 -119
  30. package/src/helpers/date.ts +188 -188
  31. package/src/helpers/debug.ts +186 -186
  32. package/src/helpers/diagrams.ts +43 -43
  33. package/src/helpers/emails.ts +6 -6
  34. package/src/helpers/eval.ts +5 -5
  35. package/src/helpers/file.test.js +50 -50
  36. package/src/helpers/file.ts +63 -63
  37. package/src/helpers/flatted.ts +149 -149
  38. package/src/helpers/functions.ts +16 -16
  39. package/src/helpers/graph/calendar.types.ts +10 -10
  40. package/src/helpers/http.ts +69 -69
  41. package/src/helpers/images.ts +22 -22
  42. package/src/helpers/json.ts +44 -44
  43. package/src/helpers/md5.ts +189 -189
  44. package/src/helpers/objects.test.js +33 -33
  45. package/src/helpers/objects.ts +274 -274
  46. package/src/helpers/promises.test.js +37 -37
  47. package/src/helpers/promises.ts +165 -165
  48. package/src/helpers/random.ts +27 -27
  49. package/src/helpers/scheduler/scheduler.test.js +103 -103
  50. package/src/helpers/scheduler/scheduler.ts +131 -131
  51. package/src/helpers/sharepoint.ts +796 -796
  52. package/src/helpers/strings.test.js +122 -122
  53. package/src/helpers/strings.ts +337 -337
  54. package/src/helpers/typecheckers.test.js +34 -34
  55. package/src/helpers/typecheckers.ts +266 -266
  56. package/src/helpers/url.test.js +43 -43
  57. package/src/helpers/url.ts +207 -207
  58. package/src/helpers/urlhelper.ts +111 -111
  59. package/src/index.ts +6 -6
  60. package/src/types/auth.ts +62 -54
  61. package/src/types/common.types.ts +15 -15
  62. package/src/types/flatted.types.ts +59 -59
  63. package/src/types/globals.types.ts +6 -6
  64. package/src/types/graph/calendar.types.ts +80 -80
  65. package/src/types/knownscript.types.ts +18 -18
  66. package/src/types/libs/datajs.types.ts +28 -28
  67. package/src/types/libs/ics.types.ts +30 -30
  68. package/src/types/libs/msal.types.ts +57 -57
  69. package/src/types/locales.ts +125 -125
  70. package/src/types/localstoragecache.types.ts +8 -8
  71. package/src/types/location.types.ts +27 -27
  72. package/src/types/moment.ts +11 -11
  73. package/src/types/regex.types.ts +16 -16
  74. package/src/types/rest.types.ts +95 -95
  75. package/src/types/sharepoint.types.ts +1466 -1466
  76. package/src/types/sharepoint.utils.types.ts +306 -306
  77. package/src/utils/auth/common.ts +119 -74
  78. package/src/utils/auth/discovery.test.js +12 -12
  79. package/src/utils/auth/discovery.ts +132 -132
  80. package/src/utils/base64.ts +27 -27
  81. package/src/utils/consolelogger.ts +333 -333
  82. package/src/utils/date.ts +172 -172
  83. package/src/utils/emails.ts +24 -24
  84. package/src/utils/knownscript.ts +286 -286
  85. package/src/utils/localstoragecache.ts +446 -446
  86. package/src/utils/rest.ts +501 -501
  87. package/src/utils/script.ts +170 -170
  88. package/src/utils/sharepoint.rest/common.ts +159 -159
  89. package/src/utils/sharepoint.rest/date.ts +62 -62
  90. package/src/utils/sharepoint.rest/file.folder.ts +685 -685
  91. package/src/utils/sharepoint.rest/item.ts +547 -547
  92. package/src/utils/sharepoint.rest/list.ts +1572 -1572
  93. package/src/utils/sharepoint.rest/listutils/GetListItemsByCaml.ts +774 -774
  94. package/src/utils/sharepoint.rest/listutils/GetListItemsById.ts +275 -275
  95. package/src/utils/sharepoint.rest/listutils/common.ts +206 -206
  96. package/src/utils/sharepoint.rest/location.ts +141 -141
  97. package/src/utils/sharepoint.rest/navigation-links.ts +86 -86
  98. package/src/utils/sharepoint.rest/user-search.ts +252 -252
  99. package/src/utils/sharepoint.rest/user.ts +558 -558
  100. package/src/utils/sharepoint.rest/web.ts +1384 -1384
  101. package/src/utils/sod.ts +194 -194
@@ -1,1572 +1,1572 @@
1
- import { jsonClone } from "../../exports-index";
2
- import { PushNoDuplicate, firstOrNull, makeUniqueArray, toHash } from "../../helpers/collections.base";
3
- import { jsonStringify } from "../../helpers/json";
4
- import { NormalizeListName, SPBasePermissions, SchemaJsonToXml, SchemaXmlToJson, extendFieldInfos } from "../../helpers/sharepoint";
5
- import { normalizeGuid } from "../../helpers/strings";
6
- import { SafeIfElse, isBoolean, isNotEmptyArray, isNullOrEmptyArray, isNullOrEmptyString, isNullOrUndefined, isNumber, isPromise, isString, isValidGuid } from "../../helpers/typecheckers";
7
- import { makeServerRelativeUrl, normalizeUrl } from "../../helpers/url";
8
- import { IDictionary } from "../../types/common.types";
9
- import { IRestOptions, contentTypes, jsonTypes } from "../../types/rest.types";
10
- import { BaseTypes, FieldTypeAsString, FieldTypes, IFieldInfo, IFieldInfoEX, IFieldInfoExHash, IFieldJsonSchema, IFieldLookupInfo, ISPEventReceiver, ListTemplateTypes, PageType, SPBasePermissionKind } from "../../types/sharepoint.types";
11
- import { GeListItemsFoldersBehaviour, IListWorkflowAssociation, IRestItem, ListExperienceOptions, iContentType, iList, iListVersionSettings, iListView } from "../../types/sharepoint.utils.types";
12
- import { ConsoleLogger } from "../consolelogger";
13
- import { GetJson, GetJsonSync, longLocalCache, shortLocalCache } from "../rest";
14
- import { GetRestBaseUrl, GetSiteUrl, LIST_EXPAND, LIST_SELECT } from "./common";
15
- import { __fixGetListItemsResults } from "./listutils/common";
16
- import { GetContentTypes, GetContentTypesSync, GetListsSync, IGetContentTypesOptions } from "./web";
17
-
18
- const logger = ConsoleLogger.get("SharePoint.Rest.List");
19
-
20
- /** returns /_api/web/lists/getById() or /_api/web/lists/getByTitle() */
21
- export function GetListRestUrl(siteUrl: string, listIdOrTitle: string): string {
22
- siteUrl = GetSiteUrl(siteUrl);
23
-
24
- let listId = GetListId(siteUrl, listIdOrTitle);
25
-
26
- let listPart = isValidGuid(listId) ? `getById('${normalizeGuid(listId)}')` : `getByTitle('${encodeURIComponent(listIdOrTitle)}')`;
27
- return GetRestBaseUrl(siteUrl) + `/web/lists/${listPart}`;
28
- }
29
-
30
- export function GetListId(siteUrl: string, listIdOrTitle: string): string {
31
- if (isNullOrEmptyString(listIdOrTitle)) return null;
32
- if (isValidGuid(listIdOrTitle)) return listIdOrTitle;
33
- //Issue 7508
34
- //When translation is enabled, and user changes list title but he is not on the same language as the site
35
- //he translates the list, but not changing its title
36
- //so REST api /lists/getByTitle will not work
37
- //instead, we need to get the list id from the web's lists collection.
38
- let lists = GetListsSync(siteUrl);
39
- var lower = listIdOrTitle.toLowerCase();
40
- var list = firstOrNull(lists, l => l.Title.toLowerCase() === lower);
41
- return list && list.Id || null;
42
- }
43
-
44
- /** get the list ID from a list page, such as a list view or an item form */
45
- export function GetListIdFromPageSync(siteUrl: string, listPageUrl: string): string {
46
- let url = `${GetRestBaseUrl(siteUrl)}/web/getlist('${makeServerRelativeUrl(listPageUrl.split('?')[0].split('#')[0])}')?$select=id`;
47
- let response = GetJsonSync<{ Id: string; }>(url, null, {
48
- ...longLocalCache,
49
- jsonMetadata: jsonTypes.nometadata
50
- });
51
- if (!isNullOrUndefined(response) && response.success) {
52
- let listId = response.result.Id;
53
- return normalizeGuid(listId);
54
- }
55
- return null;
56
- }
57
-
58
- interface IGetSiteAssetLibraryResult { Id: string, Name: string, ServerRelativeUrl: string }
59
- interface IGetSiteAssetLibraryReturnValue {
60
- value: {
61
- Id: string;
62
- RootFolder: {
63
- Name: string;
64
- ServerRelativeUrl: string;
65
- Exists: boolean;
66
- };
67
- }[];
68
- }
69
-
70
- /** ensures the site assets library exists and return its info. on errors - it will return null. */
71
- export function EnsureAssetLibrary(siteUrl: string): Promise<IGetSiteAssetLibraryResult> {
72
- siteUrl = GetSiteUrl(siteUrl);
73
-
74
- let url = GetRestBaseUrl(siteUrl) +
75
- "/web/lists/EnsureSiteAssetsLibrary?$select=ID,RootFolder/Name,RootFolder/ServerRelativeUrl,RootFolder/Exists&$expand=RootFolder";
76
- return GetJson<{
77
- d: { Id: string; RootFolder: { Name: string; ServerRelativeUrl: string; Exists: boolean; }; };
78
- }>(url, null, { method: "POST", spWebUrl: siteUrl, ...longLocalCache }).then(result => {
79
- if (result && result.d) {
80
- return {
81
- Id: result.d.Id,
82
- Name: result.d.RootFolder.Name,
83
- ServerRelativeUrl: result.d.RootFolder.ServerRelativeUrl
84
- };
85
- } else return null;
86
- }).catch<IGetSiteAssetLibraryResult>(() => null);
87
- }
88
-
89
- interface IGetSitePagesLibrarResult { Id: string, Name: string, ServerRelativeUrl: string }
90
-
91
- /** ensures the site pages library exists and return its info. on errors - it will return null. */
92
- export async function EnsureSitePagesLibrary(siteUrl: string): Promise<IGetSitePagesLibrarResult> {
93
- let url = `${GetRestBaseUrl(siteUrl)}/web/lists/EnsureSitePagesLibrary`
94
- + `?$select=ID,RootFolder/Name,RootFolder/ServerRelativeUrl,RootFolder/Exists&$expand=RootFolder`;
95
- let response = await GetJson<iList>(url, null, {
96
- method: "POST",
97
- jsonMetadata: jsonTypes.nometadata,
98
- includeDigestInPost: true,
99
- ...longLocalCache
100
- });
101
-
102
- if (!isNullOrUndefined(response) && !isNullOrUndefined(response.RootFolder)) {
103
- return {
104
- Id: response.Id,
105
- Name: response.RootFolder.Name,
106
- ServerRelativeUrl: response.RootFolder.ServerRelativeUrl
107
- };
108
- }
109
- return null;
110
- }
111
-
112
- export function GetSiteAssetLibrary(siteUrl: string, sync?: false): Promise<IGetSiteAssetLibraryResult>;
113
- export function GetSiteAssetLibrary(siteUrl: string, sync: true): IGetSiteAssetLibraryResult;
114
- export function GetSiteAssetLibrary(siteUrl: string, sync?: boolean): IGetSiteAssetLibraryResult | Promise<IGetSiteAssetLibraryResult> {
115
- let reqUrl = `${GetRestBaseUrl(siteUrl)}/web/lists?`
116
- //Issue 1492: isSiteAssetsLibrary eq true does not work for reader users.
117
- //+ `$filter=isSiteAssetsLibrary eq true&$select=ID,RootFolder/Name,RootFolder/ServerRelativeUrl,RootFolder/Exists`
118
- + `$filter=EntityTypeName%20eq%20%27SiteAssets%27&$select=ID,RootFolder/Name,RootFolder/ServerRelativeUrl,RootFolder/Exists`
119
- + `&$expand=RootFolder`;
120
-
121
- let caller = sync ? GetJsonSync : GetJson;
122
-
123
- let result = caller<IGetSiteAssetLibraryReturnValue>(reqUrl, null, { ...longLocalCache, jsonMetadata: jsonTypes.nometadata });
124
-
125
- let transform: (v: IGetSiteAssetLibraryReturnValue) => IGetSiteAssetLibraryResult = (v) => {
126
- if (isNotEmptyArray(v && v.value)) {
127
- let assetLibrary = v.value[0];
128
- return {
129
- Id: assetLibrary.Id,
130
- Name: assetLibrary.RootFolder.Name,
131
- ServerRelativeUrl: assetLibrary.RootFolder.ServerRelativeUrl
132
- };
133
- }
134
- return null;
135
- };
136
-
137
- if (isPromise(result))
138
- return result.then(r => transform(r), () => null);
139
- else
140
- return result.success ? transform(result.result) : null;
141
- }
142
-
143
- /** Return the list Title */
144
- export function GetListTitle(siteUrl: string, listIdOrTitle: string): Promise<string> {
145
- siteUrl = GetSiteUrl(siteUrl);
146
-
147
- return GetJson<{ d: { Title: string; }; }>(GetListRestUrl(siteUrl, listIdOrTitle) + `/Title`, null, { allowCache: true })
148
- .then(r => {
149
- return r.d.Title;
150
- })
151
- .catch<string>(() => null);
152
- }
153
-
154
- /** Return the list */
155
- export function GetList(siteUrlOrId: string, listIdOrTitle: string, options?: {
156
- includeViews?: boolean;
157
- viewOptions?: IListViewOptions;
158
- includeContentTypes?: boolean;
159
- includeRootFolder?: boolean;
160
- includeEventReceivers?: boolean;
161
- }, refreshCache = false): Promise<iList> {
162
- let siteUrl = GetSiteUrl(siteUrlOrId);
163
-
164
- if (isNullOrEmptyString(listIdOrTitle)) {
165
- return null;
166
- }
167
-
168
- return GetJson<{ d: iList; }>(GetListRestUrl(siteUrl, listIdOrTitle) + `?$select=${LIST_SELECT}&$expand=${LIST_EXPAND}`, null, { allowCache: true })
169
- .then(async r => {
170
- let list = r.d;
171
- if (options) {
172
- let promises = [];
173
-
174
- if (options.includeViews) {
175
- promises.push(GetListViews(siteUrl, listIdOrTitle, options.viewOptions, refreshCache).then((r) => {
176
- list.Views = r;
177
- }))
178
- }
179
- if (options.includeContentTypes) {
180
- promises.push(GetListContentTypes(siteUrl, listIdOrTitle, null, refreshCache).then((r) => {
181
- list.ContentTypes = r;
182
- }));
183
- }
184
- if (options.includeRootFolder) {
185
- promises.push(GetListRootFolder(siteUrl, listIdOrTitle).then((r) => {
186
- list.RootFolder = r;
187
- }));
188
- }
189
- if (options.includeEventReceivers) {
190
- promises.push(GetListEventReceivers(siteUrl, listIdOrTitle, refreshCache).then((r) => {
191
- list.EventReceivers = r;
192
- }));
193
- }
194
-
195
- if (promises.length > 0) {
196
- await Promise.all(promises);
197
- }
198
- }
199
-
200
- if (list.EffectiveBasePermissions
201
- && (isString(list.EffectiveBasePermissions.High) || isString(list.EffectiveBasePermissions.Low))) {
202
- list.EffectiveBasePermissions = {
203
- High: Number(list.EffectiveBasePermissions.High),
204
- Low: Number(list.EffectiveBasePermissions.Low)
205
- };
206
- }
207
-
208
- return list;
209
- })
210
- .catch<iList>(() => null);
211
- }
212
- /** Return the list */
213
- export function GetListSync(siteUrl: string, listIdOrTitle: string): iList {
214
- siteUrl = GetSiteUrl(siteUrl);
215
-
216
- if (isNullOrEmptyString(listIdOrTitle)) return null;
217
-
218
- let result = GetJsonSync<{ d: iList; }>(GetListRestUrl(siteUrl, listIdOrTitle) + `?$select=${LIST_SELECT}&$expand=${LIST_EXPAND}`, null, shortLocalCache);
219
- if (result && result.success) {
220
- let list = result.result.d;
221
-
222
- if (list.EffectiveBasePermissions
223
- && (isString(list.EffectiveBasePermissions.High)
224
- || isString(list.EffectiveBasePermissions.Low))) {
225
- list.EffectiveBasePermissions = {
226
- High: Number(list.EffectiveBasePermissions.High),
227
- Low: Number(list.EffectiveBasePermissions.Low)
228
- };
229
- }
230
-
231
- return list;
232
- }
233
- else return null;
234
- }
235
-
236
- export function GetListNameSync(webUrl: string, listIdOrTitle: string): string {
237
- let list = GetListSync(webUrl, listIdOrTitle);
238
- return NormalizeListName({ EntityTypeName: list.EntityTypeName, BaseType: list.BaseType });
239
- }
240
-
241
- export async function GetListName(webUrl: string, listIdOrTitle: string) {
242
- let list = await GetList(webUrl, listIdOrTitle);
243
- return NormalizeListName({ EntityTypeName: list.EntityTypeName, BaseType: list.BaseType });
244
- }
245
-
246
- export function GetListRootFolder(siteUrlOrId: string, listIdOrTitle: string): Promise<{ ServerRelativeUrl: string; Name: string; }> {
247
- let siteUrl = GetSiteUrl(siteUrlOrId);
248
-
249
- return GetJson<{
250
- d: { ServerRelativeUrl: string; Name: string; };
251
- }>(GetListRestUrl(siteUrl, listIdOrTitle) + `/RootFolder?$Select=Name,ServerRelativeUrl`,
252
- null, longLocalCache)
253
- .then(r => {
254
- return r.d;
255
- })
256
- .catch<{ ServerRelativeUrl: string; Name: string; }>(() => null);
257
- }
258
-
259
- export function GetListRootFolderSync(siteUrlOrId: string, listIdOrTitle: string): { ServerRelativeUrl: string; Name: string; } {
260
- let siteUrl = GetSiteUrl(siteUrlOrId);
261
-
262
- let result = GetJsonSync<{
263
- d: { ServerRelativeUrl: string; Name: string; };
264
- }>(GetListRestUrl(siteUrl, listIdOrTitle) + `/RootFolder?$Select=Name,ServerRelativeUrl`,
265
- null, longLocalCache);
266
-
267
- return SafeIfElse(() => result.result.d, null);
268
- }
269
-
270
- export function GetListField(siteUrlOrId: string, listIdOrTitle: string, fieldIdOrName: string, refreshCache?: boolean): Promise<IFieldInfo> {
271
- let siteUrl = GetSiteUrl(siteUrlOrId);
272
-
273
- var url = GetListRestUrl(siteUrl, listIdOrTitle) + `/fields`;
274
-
275
- if (isValidGuid(fieldIdOrName)) {
276
- url += `('${normalizeGuid(fieldIdOrName)}')`;
277
- } else {
278
- url += `/getbyinternalnameortitle(@u)?@u='${encodeURIComponent(fieldIdOrName)}'`;
279
- }
280
-
281
- let result = GetJson<{ d: IFieldInfo; }>(url, null, { allowCache: refreshCache !== true })
282
- .then(r => {
283
- return r.d;
284
- })
285
- .catch<IFieldInfo>(() => null);
286
-
287
- return result;
288
- }
289
-
290
- function _getListFieldsRequestUrl(siteUrl: string, listIdOrTitle: string) {
291
- return GetListRestUrl(siteUrl, listIdOrTitle) + `/fields`;
292
- }
293
-
294
- /** Gets ID, Title, ContentType Author, Editor, Created and Modified fields */
295
- export function GetStandardListFields(siteUrlOrId: string, listIdOrTitle: string, refreshCache?: boolean) {
296
- let fieldNames = ["ID", "Title", "ContentType", "Author", "Editor", "Created", "Modified"];
297
- return GetListFields(siteUrlOrId, listIdOrTitle, { refreshCache: refreshCache, fieldNames: fieldNames });
298
- }
299
-
300
- export interface IGetListFieldsOptions {
301
- refreshCache?: boolean;
302
- /** fieldNames that should be returned with the request */
303
- fieldNames?: string[];
304
- }
305
-
306
- function _processGetListFields(fields: IFieldInfo[], fieldNames: string[]) {
307
- if (isNullOrEmptyArray(fields)) {
308
- return fields as IFieldInfoEX[];
309
- }
310
- let extendedFields = extendFieldInfos(fields);
311
-
312
- if (!isNullOrEmptyArray(fieldNames)) {
313
- return extendedFields.filter((extendedField) => {
314
- return fieldNames.includes(extendedField.InternalName);
315
- });
316
- }
317
- return extendedFields;
318
- }
319
-
320
- export function GetListFields(siteUrlOrId: string, listIdOrTitle: string, options: IGetListFieldsOptions = {}): Promise<IFieldInfoEX[]> {
321
- let siteUrl = GetSiteUrl(siteUrlOrId);
322
- let url = _getListFieldsRequestUrl(siteUrl, listIdOrTitle);
323
-
324
- let restOptions: IRestOptions = {
325
- allowCache: options.refreshCache !== true,
326
- jsonMetadata: jsonTypes.nometadata
327
- };
328
-
329
- return GetJson<{ value: IFieldInfo[]; }>(url, null, restOptions)
330
- .then((result) => {
331
- return _processGetListFields(result.value, options.fieldNames);
332
- }).catch<IFieldInfoEX[]>(() => {
333
- return null;
334
- });
335
- }
336
-
337
- export function GetListFieldsSync(siteUrlOrId: string, listIdOrTitle: string, options: IGetListFieldsOptions = {}): IFieldInfoEX[] {
338
- let siteUrl = GetSiteUrl(siteUrlOrId);
339
- let url = _getListFieldsRequestUrl(siteUrl, listIdOrTitle);
340
-
341
- let restOptions: IRestOptions = {
342
- allowCache: options.refreshCache !== true,
343
- jsonMetadata: jsonTypes.nometadata
344
- };
345
-
346
- let result = GetJsonSync<{ value: IFieldInfo[]; }>(url, null, restOptions);
347
- if (result.success && !isNullOrUndefined(result.result)) {
348
- return _processGetListFields(result.result.value, options.fieldNames);
349
- }
350
- return null;
351
- }
352
-
353
- export async function GetListFieldsAsHash(siteUrlOrId: string, listIdOrTitle: string, refreshCache?: boolean): Promise<IFieldInfoExHash> {
354
- let siteUrl = GetSiteUrl(siteUrlOrId);
355
-
356
- let fields = await GetListFields(siteUrl, listIdOrTitle, { refreshCache: refreshCache });
357
- let hash: IFieldInfoExHash = {};
358
- if (isNotEmptyArray(fields)) {
359
- hash = toHash(fields, f => f.InternalName);
360
- }
361
- return hash;
362
- }
363
-
364
- export function GetListFieldsAsHashSync(siteUrlOrId: string, listIdOrTitle: string, refreshCache?: boolean): IFieldInfoExHash {
365
- let siteUrl = GetSiteUrl(siteUrlOrId);
366
-
367
- let fields = GetListFieldsSync(siteUrl, listIdOrTitle, { refreshCache: refreshCache });
368
- let hash: IFieldInfoExHash = {};
369
- if (isNotEmptyArray(fields)) {
370
- fields.forEach(f => { hash[f.InternalName] = f; });
371
- }
372
- return hash;
373
- }
374
-
375
- export function GetListWorkflows(siteUrl: string, listIdOrTitle: string, refreshCache?: boolean): Promise<IListWorkflowAssociation[]> {
376
- siteUrl = GetSiteUrl(siteUrl);
377
-
378
- return GetJson<{
379
- d: { results: IListWorkflowAssociation[]; };
380
- }>(GetListRestUrl(siteUrl, listIdOrTitle) + `/workflowAssociations`,
381
- null, { allowCache: refreshCache !== true })
382
- .then(r => {
383
- if (r && r.d && isNotEmptyArray(r.d.results)) {
384
- r.d.results.forEach(wf => {
385
- wf.BaseId = normalizeGuid(wf.BaseId);
386
- wf.Id = normalizeGuid(wf.Id);
387
- wf.ListId = normalizeGuid(wf.ListId);
388
- wf.WebId = normalizeGuid(wf.WebId);
389
- });
390
- return r.d.results;
391
- }
392
- else return [];
393
- })
394
- .catch<IListWorkflowAssociation[]>(() => []);
395
- }
396
-
397
- export function UserHasManagePermissions(siteUrl: string, listIdOrTitle: string): Promise<boolean> {
398
- siteUrl = GetSiteUrl(siteUrl);
399
-
400
- return GetJson<{ d: { EffectiveBasePermissions: { High: number; Low: number; }; }; }>(GetListRestUrl(siteUrl, listIdOrTitle) + `/EffectiveBasePermissions`, null,
401
- { ...shortLocalCache })
402
- .then(r => {
403
- return new SPBasePermissions(r.d.EffectiveBasePermissions).has(SPBasePermissionKind.ManageLists);
404
- })
405
- .catch<boolean>(() => null);
406
- }
407
-
408
- export function UserHasEditPermissions(siteUrl: string, listIdOrTitle: string): Promise<boolean> {
409
- return UserHasPermissions(siteUrl, listIdOrTitle, SPBasePermissionKind.EditListItems);
410
- }
411
-
412
- export function UserHasPermissions(siteUrlOrId: string, listIdOrTitle: string, permissionKind: SPBasePermissionKind): Promise<boolean> {
413
- let siteUrl = GetSiteUrl(siteUrlOrId);
414
-
415
- return GetJson<{ d: { EffectiveBasePermissions: { High: number; Low: number; }; }; }>(GetListRestUrl(siteUrl, listIdOrTitle) + `/EffectiveBasePermissions`, null,
416
- { ...shortLocalCache })
417
- .then(r => {
418
- return new SPBasePermissions(r.d.EffectiveBasePermissions).has(permissionKind);
419
- })
420
- .catch<boolean>(() => null);
421
- }
422
-
423
- export function UserHasPermissionsSync(siteUrlOrId: string, listIdOrTitle: string, permissionKind: SPBasePermissionKind): boolean {
424
- let siteUrl = GetSiteUrl(siteUrlOrId);
425
-
426
- const res = GetJsonSync<{ d: { EffectiveBasePermissions: { High: number; Low: number; }; }; }>(GetListRestUrl(siteUrl, listIdOrTitle) + `/EffectiveBasePermissions`, null,
427
- { ...shortLocalCache });
428
-
429
- return new SPBasePermissions(res.result.d.EffectiveBasePermissions).has(permissionKind);
430
- }
431
-
432
- /** create a new column and try to add it to default view. Send either Title and Type, or SchemaXml. Create with SchemaXml also adds to all content types */
433
- export async function CreateField(siteUrl: string, listIdOrTitle: string, options: {
434
- Title?: string;
435
- Type?: FieldTypes;
436
- Required?: boolean;
437
- Indexed?: boolean;
438
- SchemaXml?: string;
439
- /** requies Name and StaticName for the internal name */
440
- SchemaXmlSpecificInternalName?: boolean;
441
- SkipAddToDefaultView?: boolean;
442
- ClientSideComponentId?: string;
443
- ClientSideComponentProperties?: string;
444
- JSLink?: string;
445
-
446
- }): Promise<IFieldInfoEX> {
447
- siteUrl = GetSiteUrl(siteUrl);
448
-
449
- let finish = async (result: IFieldInfo) => {
450
- if (!result) {
451
- return null;
452
- }
453
-
454
- let internalName = result.InternalName;
455
- //we need to clear and reload the list fields cache, so call it and return our field from that collection.
456
- let fields = await GetListFields(siteUrl, listIdOrTitle, { refreshCache: true });
457
-
458
- try {
459
- if (options.SkipAddToDefaultView !== true) {
460
- //try to add it to default view, don't wait for it
461
- GetListViews(siteUrl, listIdOrTitle).then(views => {
462
- let defaultView = firstOrNull(views, v => v.DefaultView);
463
- if (defaultView)
464
- GetJson(GetListRestUrl(siteUrl, listIdOrTitle) + `/views('${defaultView.Id}')/ViewFields/addViewField('${internalName}')`, null, { method: "POST", spWebUrl: siteUrl });
465
- });
466
- }
467
- }
468
- catch (e) { }
469
-
470
- return firstOrNull(fields, f => f.InternalName === internalName);
471
- };
472
-
473
- if (!isNullOrEmptyString(options.SchemaXml)) {
474
- try {
475
- let updateObject: IDictionary<any> = {
476
- 'parameters': {
477
- '__metadata': { 'type': 'SP.XmlSchemaFieldCreationInformation' },
478
- 'SchemaXml': options.SchemaXml,
479
- 'Options': options.SchemaXmlSpecificInternalName !== true ?
480
- 4 ://SP.AddFieldOptions.addToAllContentTypes
481
- 4 | 8//SP.AddFieldOptions.addToAllContentTypes | addFieldInternalNameHint
482
- }
483
- };
484
- let url = `${GetListRestUrl(siteUrl, listIdOrTitle)}/fields/createFieldAsXml`;
485
- let newFieldResult = await GetJson<{ d: IFieldInfo; }>(url, JSON.stringify(updateObject));
486
-
487
- if (!isNullOrUndefined(newFieldResult)
488
- && !isNullOrUndefined(newFieldResult.d)) {
489
- if ((!isNullOrEmptyString(options.Title) && options.Title !== newFieldResult.d.Title)
490
- || (isBoolean(options.Indexed) && options.Indexed !== newFieldResult.d.Indexed)) {
491
- let updatedField = await UpdateField(siteUrl, listIdOrTitle, newFieldResult.d.InternalName, {
492
- Title: options.Title,
493
- Indexed: options.Indexed === true
494
- });
495
- return finish(updatedField);
496
- }
497
- }
498
-
499
- return finish(newFieldResult && newFieldResult.d);
500
- } catch {
501
- }
502
- return null;
503
- } else if (!isNullOrEmptyString(options.Title) && !isNullOrUndefined(options.Type)) {
504
- let updateObject: IDictionary<any> = {
505
- '__metadata': { 'type': 'SP.Field' },
506
- 'Title': options.Title,
507
- 'FieldTypeKind': options.Type,
508
- 'Required': options.Required === true,
509
- 'Indexed': options.Indexed === true
510
- };
511
- if (!isNullOrEmptyString(options.ClientSideComponentId)) {
512
- updateObject.ClientSideComponentId = options.ClientSideComponentId;
513
- }
514
- if (!isNullOrEmptyString(options.ClientSideComponentProperties)) {
515
- updateObject.ClientSideComponentProperties = options.ClientSideComponentProperties;
516
- }
517
- if (!isNullOrEmptyString(options.JSLink)) {
518
- updateObject.JSLink = options.JSLink;
519
- }
520
-
521
- try {
522
- let url = `${GetListRestUrl(siteUrl, listIdOrTitle)}/fields`;
523
- let newFieldResult = await GetJson<{ d: IFieldInfo; }>(url, JSON.stringify(updateObject));
524
- return finish(newFieldResult && newFieldResult.d);
525
- } catch {
526
- }
527
- return null;
528
- }
529
- else {
530
- console.error("You must send either SchemaXml or Title and Type");
531
- return null;
532
- }
533
- }
534
- /** Update field SchemaXml OR Title, only 1 update at a time supported. */
535
- export async function UpdateField(siteUrlOrId: string, listIdOrTitle: string, fieldInternalName: string, options: {
536
- Title?: string;
537
- Indexed?: boolean;
538
- /** Update 'Choices' propertry on 'Choice' and 'MultiChoice' field types. */
539
- Choices?: string[];
540
- SchemaXml?: string;
541
- FieldType?: FieldTypeAsString;
542
- Required?: boolean;
543
- Hidden?: boolean;
544
- JSLink?: string;
545
- ClientSideComponentId?: string;
546
- ClientSideComponentProperties?: string;
547
- }): Promise<IFieldInfoEX> {
548
- let siteUrl = GetSiteUrl(siteUrlOrId);
549
-
550
- let finish = async () => {
551
- //we need to clear and reload the list fields cache, so call it and return our field from that collection.
552
- let fields = await GetListFields(siteUrl, listIdOrTitle, { refreshCache: true });
553
- return firstOrNull(fields, f => f.InternalName === fieldInternalName);
554
- };
555
-
556
- let fields = await GetListFieldsAsHash(siteUrl, listIdOrTitle, true);
557
- let thisField = fields[fieldInternalName];
558
-
559
- //updates can either be SchemaXml, or others. Cannot be both.
560
- let updates: IDictionary<any> = {
561
- '__metadata': { 'type': 'SP.Field' }
562
- };
563
-
564
- if (!isNullOrEmptyString(options.SchemaXml)) {
565
- updates.SchemaXml = options.SchemaXml;
566
- }
567
- else {
568
- //cannot send schema updates with other updates.
569
- if (!isNullOrEmptyString(options.Title)) {
570
- updates.Title = options.Title;
571
- }
572
- if (!isNullOrEmptyString(options.FieldType)) {
573
- updates.TypeAsString = options.FieldType;
574
- }
575
- if (isBoolean(options.Required)) {
576
- updates.Required = options.Required === true;
577
- }
578
- if (isBoolean(options.Indexed)) {
579
- updates.Indexed = options.Indexed === true;
580
- }
581
- if (!isNullOrEmptyArray(options.Choices)) {
582
- let choiceType = options.FieldType || thisField.TypeAsString;
583
- if (choiceType === "Choice" || choiceType === "MultiChoice") {
584
- updates["__metadata"]["type"] = choiceType === "Choice" ? "SP.FieldChoice" : "SP.FieldMultiChoice"
585
- updates.Choices = { "results": options.Choices };
586
- } else {
587
- logger.warn("Can only update 'Choices' property on 'Choice' and 'MultiChoice' field types.");
588
- }
589
- }
590
- if (isBoolean(options.Hidden)) {
591
- //this requries the CanToggleHidden to be in the schema... if not - we will need to add it before we can update this.
592
- let fields = await GetListFieldsAsHash(siteUrl, listIdOrTitle, false);
593
- let thisField = fields[fieldInternalName];
594
- if (thisField.Hidden !== options.Hidden) {
595
- if (thisField) {
596
- if (thisField.SchemaJson.Attributes.CanToggleHidden !== "TRUE") {
597
- await UpdateField(siteUrl, listIdOrTitle, fieldInternalName, {
598
- SchemaXml:
599
- thisField.SchemaXml.replace("<Field ", `<Field CanToggleHidden="TRUE" `)
600
- });
601
- }
602
- }
603
- updates.Hidden = options.Hidden === true;
604
- }
605
- }
606
-
607
- if (!isNullOrEmptyString(options.ClientSideComponentId))
608
- updates.ClientSideComponentId = options.ClientSideComponentId;
609
- if (!isNullOrEmptyString(options.ClientSideComponentProperties))
610
- updates.ClientSideComponentProperties = options.ClientSideComponentProperties;
611
- if (!isNullOrEmptyString(options.JSLink))
612
- updates.JSLink = options.JSLink;
613
- }
614
-
615
- if (Object.keys(updates).length > 1) {
616
- return GetJson(GetListRestUrl(siteUrl, listIdOrTitle) + `/fields/getbyinternalnameortitle('${fieldInternalName}')`,
617
- JSON.stringify(updates), { xHttpMethod: "MERGE" })
618
- .then(r => {
619
- return finish();
620
- })
621
- .catch<IFieldInfoEX>(() => null);
622
- }
623
- else {
624
- console.error("You must send an option to update");
625
- return null;
626
- }
627
- }
628
-
629
- export async function ChangeTextFieldMode(
630
- siteUrlOrId: string,
631
- listIdOrTitle: string,
632
- textMode: "singleline" | "multiline" | "html",
633
- currentField: IFieldInfoEX
634
- ) {
635
- const newSchema = jsonClone(currentField.SchemaJson);
636
- const currentSchemaAttributes = newSchema.Attributes;
637
-
638
- switch (textMode) {
639
- case "singleline":
640
- let shouldIntermediateUpdate = false;
641
-
642
- if (currentSchemaAttributes.RichText === 'TRUE') {
643
- currentSchemaAttributes.RichText = 'FALSE';
644
- shouldIntermediateUpdate = true;
645
- };
646
- if (currentSchemaAttributes.RichTextMode === 'FullHTML') {
647
- currentSchemaAttributes.RichTextMode = 'Compatible';
648
- shouldIntermediateUpdate = true;
649
- };
650
-
651
- if (shouldIntermediateUpdate) {
652
- const intermediateSchema = SchemaJsonToXml(newSchema);
653
- const intermediateUpdatedField = await UpdateField(siteUrlOrId, listIdOrTitle, currentField.InternalName, {
654
- SchemaXml: intermediateSchema
655
- });
656
- // Early exit if intermediate change failed.
657
- if (isNullOrUndefined(intermediateUpdatedField))
658
- return false;
659
- };
660
-
661
- // Actual type update.
662
- currentSchemaAttributes.Type = 'Text';
663
- delete currentSchemaAttributes.RichTextMode;
664
- delete currentSchemaAttributes.RichText;
665
- break;
666
- case "multiline":
667
- currentSchemaAttributes.Type = 'Note';
668
- currentSchemaAttributes.RichText = 'FALSE';
669
- currentSchemaAttributes.RichTextMode = 'Compatible';
670
- break;
671
- case "html":
672
- currentSchemaAttributes.Type = 'Note';
673
- currentSchemaAttributes.RichText = 'TRUE';
674
- currentSchemaAttributes.RichTextMode = 'FullHTML';
675
- break;
676
- }
677
-
678
- const updatedSchema = SchemaJsonToXml(newSchema);
679
- const fieldUpdated = await UpdateField(siteUrlOrId, listIdOrTitle, currentField.InternalName, {
680
- SchemaXml: updatedSchema
681
- });
682
-
683
- // If object is null or undefined then request has failed.
684
- return !isNullOrUndefined(fieldUpdated);
685
- }
686
-
687
- export async function ChangeDatetimeFieldMode(
688
- siteUrlOrId: string,
689
- listIdOrTitle: string,
690
- includeTime: boolean,
691
- currentField: IFieldInfoEX
692
- ) {
693
- const dateTimeFormat = 'DateTime';
694
- const dateOnlyFormat = 'DateOnly';
695
-
696
- const newSchema = jsonClone(currentField.SchemaJson);
697
- const fieldAttributes = newSchema.Attributes;
698
- let needUpdate = false;
699
- if (includeTime && fieldAttributes.Format === dateOnlyFormat) {
700
- needUpdate = true;
701
- fieldAttributes.Format = dateTimeFormat;
702
- }
703
- else if (!includeTime && fieldAttributes.Format === dateTimeFormat) {
704
- needUpdate = true;
705
- fieldAttributes.Format = dateOnlyFormat;
706
- }
707
-
708
- if (needUpdate) {
709
- const updatedSchema = SchemaJsonToXml(newSchema);
710
- const updateResponse = await UpdateField(siteUrlOrId, listIdOrTitle, currentField.InternalName, {
711
- SchemaXml: updatedSchema
712
- });
713
- return !isNullOrUndefined(updateResponse);
714
- }
715
-
716
- // If an already existing format was chosen.
717
- return true;
718
- }
719
-
720
- export async function DeleteField(siteUrl: string, listIdOrTitle: string, fieldInternalName: string, options?: { DeleteHiddenField?: boolean; }): Promise<boolean> {
721
- siteUrl = GetSiteUrl(siteUrl);
722
-
723
- // let finish = async () => {
724
- // //we need to clear and reload the list fields cache, so call it and return our field from that collection.
725
- // let fields = await GetListFields(siteUrl, listIdOrTitle, { refreshCache: true });
726
- // return firstOrNull(fields, f => f.InternalName === fieldInternalName);
727
- // };
728
-
729
- if (options && options.DeleteHiddenField)
730
- await UpdateField(siteUrl, listIdOrTitle, fieldInternalName, { Hidden: false });
731
-
732
-
733
- return GetJson(GetListRestUrl(siteUrl, listIdOrTitle) + `/fields/getbyinternalnameortitle('${fieldInternalName}')`, null, {
734
- method: "POST",
735
- xHttpMethod: "DELETE"
736
- })
737
- .then(r => true)
738
- .catch<boolean>((e) => false);
739
- }
740
-
741
- export interface IListViewOptions { includeViewFields?: boolean; }
742
- export function GetListViews(siteUrl: string, listIdOrTitle: string, options?: IListViewOptions, refreshCache = false): Promise<iListView[]> {
743
- siteUrl = GetSiteUrl(siteUrl);
744
-
745
- return GetJson<{
746
- value: iListView[];
747
- }>(GetListRestUrl(siteUrl, listIdOrTitle) + `/views?$select=Title,Id,ServerRelativeUrl,RowLimit,Paged,ViewQuery,ListViewXml,PersonalView,MobileView,MobileDefaultView,Hidden,DefaultView,ReadOnlyView${options && options.includeViewFields ? "&$expand=ViewFields" : ""}`,
748
- null, { allowCache: refreshCache !== true, jsonMetadata: jsonTypes.nometadata })
749
- .then(r => {
750
- let views = r.value;
751
- if (isNotEmptyArray(views)) {
752
- views.forEach(v => {
753
- v.Id = normalizeGuid(v.Id);
754
- if (options && options.includeViewFields) {
755
- v.ViewFields = v.ViewFields && v.ViewFields["Items"] && v.ViewFields["Items"] || [];
756
- }
757
- });
758
- }
759
- return views;
760
- })
761
- .catch<iListView[]>(() => null);
762
- }
763
-
764
- export function GetListViewsSync(siteUrl: string, listIdOrTitle: string, refreshCache = false): iListView[] {
765
- siteUrl = GetSiteUrl(siteUrl);
766
-
767
- let result = GetJsonSync<{
768
- d: {
769
- results: iListView[];
770
- };
771
- }>(GetListRestUrl(siteUrl, listIdOrTitle) + `/views`,
772
- null, { allowCache: refreshCache !== true });
773
- if (result.success) {
774
- let views = result && result.result && result.result.d && result.result.d.results;
775
- if (isNotEmptyArray(views)) {
776
- views.forEach(v => { v.Id = normalizeGuid(v.Id); });
777
- }
778
- return views;
779
- }
780
- return null;
781
- }
782
-
783
- export async function AddViewFieldToListView(siteUrl: string, listIdOrTitle: string, viewId: string, viewField: string) {
784
- return _addOrRemoveViewField(siteUrl, listIdOrTitle, viewId, viewField, "addviewfield");
785
- }
786
-
787
- export async function RemoveViewFieldFromListView(siteUrl: string, listIdOrTitle: string, viewId: string, viewField: string) {
788
- return _addOrRemoveViewField(siteUrl, listIdOrTitle, viewId, viewField, "removeviewfield");
789
- }
790
-
791
- async function _addOrRemoveViewField(siteUrl: string, listIdOrTitle: string, viewId: string, viewField: string, action: "addviewfield" | "removeviewfield") {
792
- siteUrl = GetSiteUrl(siteUrl);
793
-
794
- if (isNullOrEmptyString(viewField) || !isValidGuid(viewId)) {
795
- return false;
796
- }
797
-
798
- let views = await GetListViews(siteUrl, listIdOrTitle, { includeViewFields: true });
799
-
800
- if (isNullOrEmptyArray(views)) {
801
- return false;
802
- }
803
-
804
- let view = views.filter((view) => {
805
- return normalizeGuid(view.Id) === normalizeGuid(viewId);
806
- })[0];
807
-
808
- if (isNullOrUndefined(view)) {
809
- return false;
810
- }
811
-
812
- let hasField = view.ViewFields.includes(viewField);
813
-
814
- if (action === "addviewfield" && hasField === true) {
815
- return true;
816
- }
817
-
818
- if (action === "removeviewfield" && hasField === false) {
819
- return true;
820
- }
821
-
822
- try {
823
- let url = GetListRestUrl(siteUrl, listIdOrTitle) + `/views('${normalizeGuid(view.Id)}')/viewfields/${action}('${viewField}')`;
824
-
825
- let result = await GetJson<{ "odata.null": boolean; }>(url, null, { method: "POST" });
826
-
827
- if (result && result["odata.null"] === true) {
828
- return true;
829
- }
830
- } catch { }
831
-
832
- return false;
833
- }
834
-
835
- export function GetListContentTypes(siteUrl: string, listIdOrTitle: string,
836
- options?: Omit<IGetContentTypesOptions, "listIdOrTitle" | "fromRooWeb">, refreshCache = false): Promise<iContentType[]> {
837
- return GetContentTypes(siteUrl, { ...(options || {}), listIdOrTitle: listIdOrTitle }, refreshCache);
838
- }
839
-
840
- export function GetListContentTypesSync(siteUrl: string, listIdOrTitle: string,
841
- options?: Omit<IGetContentTypesOptions, "listIdOrTitle" | "fromRooWeb">, refreshCache = false): iContentType[] {
842
- return GetContentTypesSync(siteUrl, { ...(options || {}), listIdOrTitle: listIdOrTitle }, refreshCache);
843
- }
844
-
845
- /** generic version. for the KWIZ forms version that supports action id call GetListFormUrlAppsWeb instead */
846
- export function GetListFormUrl(siteUrl: string, listId: string, pageType: PageType, params?: { contentTypeId?: string; itemId?: number | string; rootFolder?: string }) {
847
- siteUrl = GetSiteUrl(siteUrl);
848
-
849
- if (!isValidGuid(listId)) console.error('GetListFormUrl requires a list id');
850
- let url = `${normalizeUrl(siteUrl)}/_layouts/15/listform.aspx?PageType=${pageType}&ListId=${encodeURIComponent(listId)}`;
851
- if (params) {
852
- if (!isNullOrEmptyString(params.contentTypeId))
853
- url += `&ContentTypeId=${encodeURIComponent(params.contentTypeId)}`;
854
- if (!isNullOrEmptyString(params.itemId))
855
- url += `&ID=${encodeURIComponent(params.itemId as string)}`;
856
- if (!isNullOrEmptyString(params.rootFolder))
857
- url += `&RootFolder=${encodeURIComponent(params.rootFolder)}`;
858
- }
859
- return url;
860
- }
861
-
862
- export function GetFieldSchemaSync(siteUrl: string, listIdOrTitle: string, fieldInternalName: string, refreshCache?: boolean): IFieldJsonSchema {
863
- siteUrl = GetSiteUrl(siteUrl);
864
-
865
- //ISSUE: 1516 - The get schema request will fail if the field doesn't exist in the list, so we load the fields and ensure the field
866
- //exists before requesting the schema.
867
- let fields = GetListFieldsSync(siteUrl, listIdOrTitle, {
868
- refreshCache: refreshCache,
869
- fieldNames: [fieldInternalName]
870
- });
871
-
872
- if (isNullOrEmptyArray(fields)) {
873
- return null;
874
- }
875
-
876
- let field = fields[0];
877
- return SchemaXmlToJson(field.SchemaXml);
878
- // let url = GetListRestUrl(siteUrl, listIdOrTitle) + `/fields/getByInternalNameOrTitle('${fieldInternalName}')?$select=SchemaXml`;
879
- // let result = GetJsonSync<{ d: { SchemaXml: string; }; }>(
880
- // url,
881
- // null,
882
- // {
883
- // ...shortLocalCache,
884
- // forceCacheUpdate: refreshCache === true
885
- // });
886
-
887
- // if (result && result.success) {
888
- // return SchemaXmlToJson(result.result.d.SchemaXml);
889
- // }
890
- // return null;
891
- //#endregion
892
- }
893
-
894
- export async function GetFieldSchema(siteUrl: string, listIdOrTitle: string, fieldInternalName: string, refreshCache?: boolean) {
895
- siteUrl = GetSiteUrl(siteUrl);
896
-
897
- //ISSUE: 1516 - The get schema request will fail if the field doesn't exist in the list, so we load the fields and ensure the field
898
- //exists before requesting the schema
899
- let fields = await GetListFields(siteUrl, listIdOrTitle, {
900
- refreshCache: refreshCache,
901
- fieldNames: [fieldInternalName]
902
- });
903
-
904
- if (isNullOrEmptyArray(fields)) {
905
- return null;
906
- }
907
-
908
- let field = fields[0];
909
- return SchemaXmlToJson(field.SchemaXml);
910
- }
911
-
912
- export async function GetListItems(siteUrl: string, listIdOrTitle: string, options: {
913
- /** Optional, default: 1000. 0: get all items. */
914
- rowLimit?: number;
915
- /** Id, Title, Modified, FileLeafRef, FileDirRef, FileRef, FileSystemObjectType */
916
- columns: (string | IFieldInfoEX)[];
917
- foldersBehaviour?: GeListItemsFoldersBehaviour;
918
- /** Optional, request to expand some columns. */
919
- expand?: string[];
920
- /** allow to change the jsonMetadata for this request, default: verbose */
921
- jsonMetadata?: jsonTypes;
922
- refreshCache?: boolean;
923
- /** allow to send a filter statement */
924
- $filter?: string;
925
- }): Promise<IRestItem[]> {
926
- let info = _GetListItemsInfo(siteUrl, listIdOrTitle, options);
927
-
928
- let items: IRestItem[] = [];
929
-
930
- do {
931
- let resultItems: IRestItem[] = [];
932
- let next: string = null;
933
- if (info.noMetadata) {
934
- let requestResult = (await GetJson<{
935
- value: IRestItem[];
936
- "odata.nextLink": string;
937
- }>(info.requestUrl, null, {
938
- allowCache: options.refreshCache !== true,
939
- jsonMetadata: options.jsonMetadata
940
- }));
941
- resultItems = requestResult.value;
942
- next = requestResult["odata.nextLink"];
943
- }
944
- else {
945
- let requestResult = (await GetJson<{
946
- d: {
947
- results: IRestItem[];
948
- __next?: string;
949
- };
950
- }>(info.requestUrl, null, {
951
- allowCache: options.refreshCache !== true
952
- }));
953
- resultItems = requestResult.d.results;
954
- next = requestResult.d.__next;
955
- }
956
-
957
- if (isNotEmptyArray(resultItems))
958
- items.push(...resultItems);
959
-
960
- if (info.totalNumberOfItemsToGet > items.length)
961
- info.requestUrl = next;
962
- else
963
- info.requestUrl = null;
964
-
965
- } while (!isNullOrEmptyString(info.requestUrl));
966
-
967
- return __fixGetListItemsResults(siteUrl, listIdOrTitle, items, options.foldersBehaviour, info.expandedLookupFields);
968
- }
969
-
970
- export function GetListItemsSync(siteUrl: string, listIdOrTitle: string, options: {
971
- /** Optional, default: 1000. 0: get all items. */
972
- rowLimit?: number;
973
- /** Id, Title, Modified, FileLeafRef, FileDirRef, FileRef, FileSystemObjectType */
974
- columns: (string | IFieldInfoEX)[];
975
- foldersBehaviour?: GeListItemsFoldersBehaviour;
976
- /** Optional, request to expand some columns. */
977
- expand?: string[];
978
- /** allow to send a filter statement */
979
- $filter?: string;
980
- }): IRestItem[] {
981
- let info = _GetListItemsInfo(siteUrl, listIdOrTitle, options);
982
-
983
- let items: IRestItem[] = [];
984
-
985
- do {
986
- let resultItems: IRestItem[] = [];
987
- let next: string = null;
988
- if (info.noMetadata) {
989
- let requestResult = GetJsonSync<{
990
- value: IRestItem[];
991
- "odata.nextLink": string;
992
- }>(info.requestUrl, null, { allowCache: true });
993
- if (requestResult.success) {
994
- resultItems = requestResult.result.value;
995
- next = requestResult.result["odata.nextLink"];
996
- }
997
- }
998
- else {
999
- let requestResult = GetJsonSync<{
1000
- d: { results: IRestItem[]; __next?: string; };
1001
- }>(info.requestUrl, null, { allowCache: true });
1002
- if (requestResult.success) {
1003
- resultItems = requestResult.result.d.results;
1004
- next = requestResult.result.d.__next;
1005
- }
1006
- }
1007
-
1008
- if (isNotEmptyArray(resultItems))
1009
- items.push(...resultItems);
1010
-
1011
- if (info.totalNumberOfItemsToGet > items.length)
1012
- info.requestUrl = next;
1013
- else
1014
- info.requestUrl = null;
1015
-
1016
- } while (!isNullOrEmptyString(info.requestUrl));
1017
-
1018
- return __fixGetListItemsResults(siteUrl, listIdOrTitle, items, options.foldersBehaviour, info.expandedLookupFields);
1019
- }
1020
-
1021
- function _GetListItemsInfo(siteUrl: string, listIdOrTitle: string, options: {
1022
- /** Optional, default: 1000. 0: get all items. */
1023
- rowLimit?: number;
1024
- /** Id, Title, Modified, FileLeafRef, FileDirRef, FileRef, FileSystemObjectType */
1025
- columns: (string | IFieldInfoEX)[];
1026
- /** Optional, request to expand some columns. */
1027
- expand?: string[];
1028
- /** allow to change the jsonMetadata for this request, default: verbose */
1029
- jsonMetadata?: jsonTypes;
1030
- /** allow to send a filter statement */
1031
- $filter?: string;
1032
- }) {
1033
- siteUrl = GetSiteUrl(siteUrl);
1034
-
1035
- let url = GetListRestUrl(siteUrl, listIdOrTitle) + `/items`;
1036
- let queryParams: string[] = [];
1037
-
1038
- //Issue 8189 expand lookup fields
1039
- let columns: string[] = [];
1040
- let expand: string[] = [];
1041
- let expandedLookupFields: IFieldInfoEX[] = [];
1042
- options.columns.forEach(c => {
1043
- if (isString(c)) columns.push(c);
1044
- else {
1045
- let internalName = c.InternalName;
1046
- //Issue 828, 336
1047
- if (internalName.startsWith("_")) internalName = `OData_${internalName}`;
1048
-
1049
- let isLookupField = c.TypeAsString === "Lookup" || c.TypeAsString === "LookupMulti";
1050
- let isUserField = c.TypeAsString === "User" || c.TypeAsString === "UserMulti";
1051
-
1052
- if (isLookupField || isUserField) {
1053
- //ISSUE: 1519 - Added lookupField property to able to retrieve value of the additional lookup field key
1054
- let lookupField = (c as IFieldLookupInfo).LookupField;
1055
- if (!isNullOrEmptyString(lookupField) && isLookupField) {
1056
- columns.push(`${internalName}/${lookupField}`);
1057
- }
1058
- //we want to expand it
1059
- columns.push(`${internalName}/Title`);
1060
- columns.push(`${internalName}/Id`);
1061
- expand.push(internalName);
1062
- expandedLookupFields.push(c);
1063
- }
1064
- else columns.push(internalName);
1065
- }
1066
- });
1067
- if (isNotEmptyArray(options.expand)) {
1068
- expand.push(...options.expand);
1069
- }
1070
-
1071
- //add the ones we need
1072
- PushNoDuplicate(columns, "Id");
1073
- PushNoDuplicate(columns, "FileRef");
1074
- PushNoDuplicate(columns, "FileSystemObjectType");
1075
-
1076
- queryParams.push(`$select=${encodeURIComponent(makeUniqueArray(columns).join(','))}`);
1077
-
1078
- if (isNotEmptyArray(expand))
1079
- queryParams.push(`$expand=${encodeURIComponent(makeUniqueArray(expand).join(','))}`);
1080
-
1081
- let batchSize = 2000;
1082
- let limit = options.rowLimit >= 0 && options.rowLimit < batchSize ? options.rowLimit : batchSize;
1083
- let totalNumberOfItemsToGet = !isNumber(options.rowLimit) || options.rowLimit < 1 ? 99999 : options.rowLimit > batchSize ? options.rowLimit : limit;
1084
-
1085
- if (!isNullOrEmptyString(options.$filter))
1086
- queryParams.push(`$filter=${options.$filter}`);
1087
- queryParams.push(`$top=${limit}`);
1088
-
1089
- let requestUrl = url + (queryParams.length > 0 ? '?' + queryParams.join('&') : '');
1090
- let noMetadata = options.jsonMetadata === jsonTypes.nometadata;
1091
-
1092
- return { requestUrl, noMetadata, totalNumberOfItemsToGet, expandedLookupFields };
1093
- }
1094
-
1095
- /** Find an item by id, even if it is nested in a sub-folder */
1096
- export function FindListItemById(items: IRestItem[], itemId: number): IRestItem {
1097
- for (let i = 0; i < items.length; i++) {
1098
- let current = items[i];
1099
- if (current.Id === itemId) return current;
1100
- else if (isNotEmptyArray(current.__Items))//folder? look inside
1101
- {
1102
- let nestedResult = FindListItemById(current.__Items, itemId);
1103
- if (!isNullOrUndefined(nestedResult)) return nestedResult;
1104
- }
1105
- }
1106
- //not found
1107
- return null;
1108
- }
1109
-
1110
- function _getListEventReceiversRequestUrl(siteUrl: string, listIdOrTitle: string) {
1111
- return GetListRestUrl(siteUrl, listIdOrTitle) + `/EventReceivers`
1112
- }
1113
-
1114
- export async function GetListEventReceivers(siteUrl: string, listIdOrTitle: string, refreshCache?: boolean): Promise<ISPEventReceiver[]> {
1115
- try {
1116
- let url = _getListEventReceiversRequestUrl(siteUrl, listIdOrTitle);
1117
- let response = await GetJson<{
1118
- value: ISPEventReceiver[];
1119
- }>(url,
1120
- null, {
1121
- allowCache: refreshCache !== true,
1122
- jsonMetadata: jsonTypes.nometadata
1123
- });
1124
-
1125
- return !isNullOrUndefined(response) ? response.value : null;
1126
- } catch {
1127
- }
1128
-
1129
- return null;
1130
- }
1131
-
1132
- export async function AddListEventReceiver(siteUrl: string, listIdOrTitle: string, eventReceiverDefinition: Pick<ISPEventReceiver, "EventType" | "ReceiverName" | "ReceiverUrl" | "SequenceNumber">): Promise<ISPEventReceiver> {
1133
- let newEventReceiver: Omit<ISPEventReceiver, "ReceiverId" | "Synchronization"> = {
1134
- ReceiverAssembly: "",
1135
- ReceiverClass: "",
1136
- ...eventReceiverDefinition
1137
- };
1138
-
1139
- try {
1140
- let url = _getListEventReceiversRequestUrl(siteUrl, listIdOrTitle);
1141
- let response = await GetJson<ISPEventReceiver>(url, JSON.stringify(newEventReceiver), {
1142
- method: "POST",
1143
- includeDigestInPost: true,
1144
- jsonMetadata: jsonTypes.nometadata,
1145
- headers: {
1146
- "content-type": contentTypes.json
1147
- }
1148
- });
1149
-
1150
- return !isNullOrUndefined(response) && isValidGuid(response.ReceiverId) ? response : null;
1151
- } catch {
1152
- }
1153
-
1154
- return null;
1155
- }
1156
-
1157
- export async function DeleteListEventReceiver(siteUrl: string, listIdOrTitle: string, eventReceiverId: string): Promise<boolean> {
1158
- try {
1159
- let url = `${_getListEventReceiversRequestUrl(siteUrl, listIdOrTitle)}('${normalizeGuid(eventReceiverId)}')/deleteObject`;
1160
- let response = await GetJson<{ "odata.null": boolean }>(url, null, {
1161
- method: "POST",
1162
- includeDigestInPost: true,
1163
- jsonMetadata: jsonTypes.nometadata
1164
- });
1165
-
1166
- return !isNullOrUndefined(response) && response["odata.null"] === true;
1167
- } catch {
1168
- }
1169
-
1170
- return false;
1171
- }
1172
-
1173
- /** timestamp of changes:
1174
- * - item updates
1175
- * - changes to columns
1176
- * - content types
1177
- * - list versioning settings
1178
- * - list title/description
1179
- * - content approval settings
1180
- * does not track:
1181
- * - Changes to views
1182
- * - changing list/items permissions
1183
- */
1184
- export function GetListLastItemModifiedDate(siteUrl: string, listIdOrTitle: string, options: {
1185
- sync: true;
1186
- refreshCache?: boolean;
1187
- /** ignore system changes */
1188
- userChangesOnly?: boolean;
1189
- }): string;
1190
- /** timestamp of changes:
1191
- * - item updates
1192
- * - changes to columns
1193
- * - content types
1194
- * - list versioning settings
1195
- * - list title/description
1196
- * - content approval settings
1197
- * does not track:
1198
- * - Changes to views
1199
- * - changing list/items permissions
1200
- */
1201
- export function GetListLastItemModifiedDate(siteUrl: string, listIdOrTitle: string, options?: {
1202
- sync?: false;
1203
- refreshCache?: boolean;
1204
- /** ignore system changes */
1205
- userChangesOnly?: boolean;
1206
- }): Promise<string>;
1207
-
1208
- export function GetListLastItemModifiedDate(siteUrl: string, listIdOrTitle: string, options?: {
1209
- sync?: boolean;
1210
- refreshCache?: boolean;
1211
- /** ignore system changes */
1212
- userChangesOnly?: boolean;
1213
- }): string | Promise<string> {
1214
- siteUrl = GetSiteUrl(siteUrl);
1215
-
1216
- let endPoint = options && options.userChangesOnly ? 'LastItemUserModifiedDate' : 'LastItemModifiedDate';
1217
-
1218
- let sync = options && options.sync ? true : false;
1219
- let caller = sync ? GetJsonSync : GetJson;
1220
-
1221
- let result = caller<{ value: string; }>(GetListRestUrl(siteUrl, listIdOrTitle) + `/${endPoint}`, null, {
1222
- allowCache: true,//in memory only
1223
- jsonMetadata: jsonTypes.nometadata,
1224
- forceCacheUpdate: options && options.refreshCache === true || false
1225
- });
1226
-
1227
- if (isPromise(result))
1228
- return result.then(r => r.value, () => null);
1229
- else
1230
- return result.success ? result.result.value : null;
1231
- }
1232
-
1233
- export async function ReloadListLastModified(siteUrl: string, listIdOrTitle: string) {
1234
- await GetListLastItemModifiedDate(siteUrl, listIdOrTitle, { refreshCache: true });
1235
- //make sure we do it for both title and id, we don't know how the other callers may use this in their API
1236
-
1237
- if (!isValidGuid(listIdOrTitle)) {
1238
- try {
1239
- var listId = GetListId(siteUrl, listIdOrTitle);
1240
- await GetListLastItemModifiedDate(siteUrl, listId, { refreshCache: true });
1241
- } catch (e) { }
1242
- }
1243
- else {
1244
- try {
1245
- var listTitle = await GetListTitle(siteUrl, listIdOrTitle);
1246
- await GetListLastItemModifiedDate(siteUrl, listTitle, { refreshCache: true });
1247
- } catch (e) { }
1248
- }
1249
- }
1250
-
1251
- export async function ListHasUniquePermissions(siteUrl: string, listIdOrTitle: string): Promise<boolean> {
1252
- let url = `${GetListRestUrl(siteUrl, listIdOrTitle)}/?$select=hasuniqueroleassignments`;
1253
- let has = await GetJson<{ HasUniqueRoleAssignments: boolean }>(url, undefined, { allowCache: false, jsonMetadata: jsonTypes.nometadata });
1254
- return has.HasUniqueRoleAssignments === true;
1255
- }
1256
- export async function RestoreListPermissionInheritance(siteUrl: string, listIdOrTitle: string): Promise<void> {
1257
- let url = `${GetListRestUrl(siteUrl, listIdOrTitle)}/ResetRoleInheritance`;
1258
- await GetJson(url, undefined, { method: "POST", allowCache: false, jsonMetadata: jsonTypes.nometadata, spWebUrl: siteUrl });
1259
- }
1260
- export async function BreakListPermissionInheritance(siteUrl: string, listIdOrTitle: string, clear = true): Promise<void> {
1261
- let url = `${GetListRestUrl(siteUrl, listIdOrTitle)}/breakroleinheritance(copyRoleAssignments=${clear ? 'false' : 'true'}, clearSubscopes=true)`;
1262
- await GetJson(url, undefined, { method: "POST", allowCache: false, jsonMetadata: jsonTypes.nometadata, spWebUrl: siteUrl });
1263
- }
1264
- export async function AssignListPermission(siteUrl: string, listIdOrTitle: string, principalId: number, roleId: number) {
1265
- let url = `${GetListRestUrl(siteUrl, listIdOrTitle)}/roleassignments/addroleassignment(principalid=${principalId},roleDefId=${roleId})`;
1266
- await GetJson(url, undefined, { method: "POST", allowCache: false, jsonMetadata: jsonTypes.nometadata, spWebUrl: siteUrl });
1267
- }
1268
- export async function RemoveListPermission(siteUrl: string, listIdOrTitle: string, principalId: number, roleId: number) {
1269
- let url = `${GetListRestUrl(siteUrl, listIdOrTitle)}/roleassignments/removeroleassignment(principalid=${principalId},roleDefId=${roleId})`;
1270
- await GetJson(url, undefined, { method: "POST", allowCache: false, jsonMetadata: jsonTypes.nometadata, spWebUrl: siteUrl });
1271
- }
1272
-
1273
- interface iCreateListResult {
1274
- AllowContentTypes: boolean,
1275
- BaseTemplate: ListTemplateTypes,
1276
- BaseType: BaseTypes,
1277
- ContentTypesEnabled: boolean,
1278
- Created: string,
1279
- DefaultItemOpenUseListSetting: boolean,
1280
- Description: string,
1281
- DisableCommenting: boolean,
1282
- DisableGridEditing: boolean,
1283
- DocumentTemplateUrl: string,//"/sites/s/cms/CMSLayouts/Forms/template.dotx",
1284
- DraftVersionVisibility: 0,
1285
- EnableAttachments: boolean,
1286
- EnableFolderCreation: boolean,
1287
- EnableMinorVersions: boolean,
1288
- EnableModeration: false,
1289
- EnableRequestSignOff: boolean,
1290
- EnableVersioning: boolean,
1291
- EntityTypeName: string,//"CMSLayouts",
1292
- ExemptFromBlockDownloadOfNonViewableFiles: boolean,
1293
- FileSavePostProcessingEnabled: boolean,
1294
- ForceCheckout: boolean,
1295
- HasExternalDataSource: boolean,
1296
- Hidden: boolean,
1297
- Id: string,//"c21d4eb4-70cc-4c95-925a-aa34bb9e01e0",
1298
- ImagePath: {
1299
- DecodedUrl: string,//"/_layouts/15/images/itdl.png?rev=47"
1300
- },
1301
- ImageUrl: string,//"/_layouts/15/images/itdl.png?rev=47",
1302
- IsApplicationList: boolean,
1303
- IsCatalog: boolean,
1304
- IsPrivate: boolean,
1305
- ItemCount: 0,
1306
- LastItemDeletedDate: string,//"2024-02-05T18:26:05Z",
1307
- LastItemModifiedDate: string,//"2024-02-05T18:26:06Z",
1308
- LastItemUserModifiedDate: string,//"2024-02-05T18:26:05Z",
1309
- ListExperienceOptions: ListExperienceOptions,
1310
- ListItemEntityTypeFullName: string,//"SP.Data.CMSLayoutsItem",
1311
- MajorVersionLimit: number,//500,
1312
- MajorWithMinorVersionsLimit: number,//0,
1313
- MultipleDataList: boolean,
1314
- NoCrawl: boolean,
1315
- ParentWebPath: {
1316
- DecodedUrl: string,//"/sites/s/cms"
1317
- },
1318
- ParentWebUrl: string,//"/sites/s/cms",
1319
- ParserDisabled: boolean,
1320
- ServerTemplateCanCreateFolders: boolean,
1321
- TemplateFeatureId: string,//"00bfea71-e717-4e80-aa17-d0c71b360101",
1322
- Title: string,//"CMSLayouts"
1323
- }
1324
- export async function CreateList(siteUrl: string, info: {
1325
- title: string; description: string;
1326
- type: BaseTypes; template: ListTemplateTypes;
1327
- }): Promise<iCreateListResult> {
1328
- let url = `${GetRestBaseUrl(siteUrl)}/web/lists`;
1329
- const body = {
1330
- __metadata: { type: 'SP.List' },
1331
- AllowContentTypes: false,
1332
- ContentTypesEnabled: false,
1333
- BaseTemplate: info.template,
1334
- BaseType: info.type,
1335
- Description: info.description,
1336
- Title: info.title
1337
- };
1338
-
1339
- let newList = (await GetJson<{ d: iCreateListResult }>(url, jsonStringify(body))).d;
1340
- normalizeGuid(newList.Id);
1341
- return newList;
1342
- }
1343
-
1344
- export async function SearchList(siteUrl: string, listIdOrTitle: string, query: string) {
1345
- let listId = GetListId(siteUrl, listIdOrTitle);
1346
- let url = `${GetRestBaseUrl(siteUrl)}/search/query?querytext='(${query}*)'&querytemplate='{searchTerms} (NormListID:${listId})'`;
1347
-
1348
- try {
1349
- const result = await GetJson<{
1350
- ElapsedTime: number,
1351
- PrimaryQueryResult: {
1352
- CustomResults: [];
1353
- QueryId: string;//"7fdf01b1-f6f0-4d42-b046-d9db22597084",
1354
- QueryRuleId: string;// "00000000-0000-0000-0000-000000000000",
1355
- RefinementResults: null,
1356
- RelevantResults: {
1357
- RowCount: number,
1358
- Table: {
1359
- Rows: {
1360
- Cells: {
1361
- Key:
1362
- /** "1989637621861439888" "Edm.Int64" */
1363
- "WorkId"
1364
- /** "1000.1073372","Edm.Double" */
1365
- | "Rank"
1366
- /** "sample md as text","Edm.String" */
1367
- | "Title"
1368
- /** "Shai Petel", "Edm.String" */
1369
- | "Author"
1370
- /** "91", "Edm.Int64" */
1371
- | "Size"
1372
- /** "https://kwizcom.sharepoint.com/sites/s/cms/CMSPages/sample md as text.txt", "Edm.String" */
1373
- | "Path"
1374
- /** null, "Null" */
1375
- | "Description"
1376
- /** "# hello world! - bullet - bullet | table | col | | ----- | ---- | |table |col | ", "Edm.String" */
1377
- | "HitHighlightedSummary"
1378
- /** "https://kwizcom.sharepoint.com/_api/v2.1/drives/b!8NAeO-mocUWbgyMTqcM0Mfh8XKPhn7xOhhMrO5KfJjBs_gXb9j8ZRaLxuppgj0Uk/items/01OBXW4FLU6G4LIT7AX5BK3MXHDICVTIOT/thumbnails/0/c400x99999/content?prefer=noRedirect", "Edm.String" */
1379
- | "PictureThumbnailURL"
1380
- /** null,"Null" */
1381
- | "ServerRedirectedURL"
1382
- /** null,"Null" */
1383
- | "ServerRedirectedEmbedURL"
1384
- /** null,"Null" */
1385
- | "ServerRedirectedPreviewURL"
1386
- /** "txt","Edm.String" */
1387
- | "FileExtension"
1388
- /** "0x010100CB212272F1372446A2423F0A2BEA12B8", "Edm.String" */
1389
- | "ContentTypeId"
1390
- /** "https://kwizcom.sharepoint.com/sites/s/cms/CMSPages/Forms/AllItems.aspx","Edm.String" */
1391
- | "ParentLink"
1392
- /** "1","Edm.Int64" */
1393
- | "ViewsLifeTime"
1394
- /** "1","Edm.Int64" */
1395
- | "ViewsRecent"
1396
- /** "2024-02-22T18:35:48.0000000Z","Edm.DateTime" */
1397
- | "LastModifiedTime"
1398
- /** "txt","Edm.String" */
1399
- | "FileType"
1400
- /** "1989637621861439888","Edm.Int64" */
1401
- | "DocId"
1402
- /** "https://kwizcom.sharepoint.com/sites/s/cms","Edm.String" */
1403
- | "SPWebUrl"
1404
- /** "{b4b8f174-e04f-42bf-adb2-e71a0559a1d3}","Edm.String" */
1405
- | "UniqueId"
1406
- /** "3b1ed0f0-a8e9-4571-9b83-2313a9c33431","Edm.String" */
1407
- | "SiteId"
1408
- /** "a35c7cf8-9fe1-4ebc-8613-2b3b929f2630","Edm.String" */
1409
- | "WebId"
1410
- /** "db05fe6c-3ff6-4519-a2f1-ba9a608f4524","Edm.String" */
1411
- | "ListId"
1412
- /** "https://kwizcom.sharepoint.com/sites/s/cms/CMSPages/sample md as text.txt","Edm.String" */
1413
- | "OriginalPath"
1414
- ;
1415
- Value: string,
1416
- ValueType: "Edm.Int64" | "Edm.Double" | "Edm.String" | "Edm.DateTime" | "Null"
1417
- }[]
1418
- }[]
1419
- },
1420
- TotalRows: number,
1421
- TotalRowsIncludingDuplicates: number
1422
- }
1423
- },
1424
- }>(url, null, { jsonMetadata: jsonTypes.nometadata });
1425
- logger.json(result.PrimaryQueryResult.RelevantResults, `search ${query}`);
1426
- let rows = result.PrimaryQueryResult.RelevantResults.Table.Rows;
1427
-
1428
- const mapped: (IDictionary<string | Date | number> & {
1429
- WorkId?: number;
1430
- Rank?: number;
1431
- Title?: string;
1432
- Author?: string;
1433
- Size?: number;
1434
- Path?: string;
1435
- Description?: string;
1436
- HitHighlightedSummary?: string;
1437
- PictureThumbnailURL?: string;
1438
- ServerRedirectedURL?: string;
1439
- ServerRedirectedEmbedURL?: string;
1440
- ServerRedirectedPreviewURL?: string;
1441
- FileExtension?: string;
1442
- ContentTypeId?: string;
1443
- ParentLink?: string;
1444
- ViewsLifeTime?: number;
1445
- ViewsRecent?: number;
1446
- LastModifiedTime?: Date;
1447
- FileType?: string;
1448
- DocId?: number;
1449
- SPWebUrl?: string;
1450
- UniqueId?: string;
1451
- SiteId?: string;
1452
- WebId?: string;
1453
- ListId?: string;
1454
- OriginalPath?: string;
1455
- $itemId?: number;
1456
- })[] = [];
1457
- rows.forEach(r => {
1458
- try {
1459
- const rowValues: IDictionary<string | Date | number> = {};
1460
- r.Cells.forEach(cell => {
1461
- rowValues[cell.Key] = cell.ValueType === "Edm.Int64" || cell.ValueType === "Edm.Double"
1462
- ? parseInt(cell.Value, 10)
1463
- : cell.ValueType === "Edm.DateTime"
1464
- ? new Date(cell.Value)
1465
- : cell.ValueType === "Null"
1466
- ? ""
1467
- : cell.Value
1468
- });
1469
- let resultPath = isNullOrEmptyString(rowValues.Path) ? "" : (rowValues.Path as string).toLowerCase();
1470
- let indexOfId = resultPath.toLowerCase().indexOf("id=");
1471
- let itemId = indexOfId >= 0 ? parseInt(resultPath.slice(indexOfId + 3)) : -1;
1472
- if (itemId >= 0)
1473
- rowValues.$itemId = itemId;
1474
- mapped.push(rowValues);
1475
- } catch (e) { return null; }
1476
- });
1477
-
1478
- return mapped;
1479
- } catch (e) {
1480
- logger.error(e);
1481
- }
1482
-
1483
- return [];
1484
- }
1485
-
1486
- export async function UpdateListExperience(siteUrl: string, listId: string, experience: ListExperienceOptions) {
1487
- try {
1488
- let url = GetListRestUrl(siteUrl, listId);
1489
- let data = {
1490
- "ListExperienceOptions": experience
1491
- };
1492
- let result = await GetJson(url, JSON.stringify(data), {
1493
- xHttpMethod: "MERGE",
1494
- jsonMetadata: jsonTypes.nometadata
1495
- });
1496
- return isNullOrEmptyString(result);
1497
- } catch (e) {
1498
- logger.error(e);
1499
- }
1500
- return false;
1501
- }
1502
-
1503
- export async function GetListVersionSettings(siteUrlOrId: string, listIdOrTitle: string, options?: { refreshCache?: boolean }): Promise<iListVersionSettings> {
1504
- let siteUrl = GetSiteUrl(siteUrlOrId);
1505
-
1506
- if (isNullOrEmptyString(listIdOrTitle)) return null;
1507
-
1508
- try {
1509
- const result = await GetJson<iListVersionSettings>(GetListRestUrl(siteUrl, listIdOrTitle) + `?$select=EnableMinorVersions,EnableVersioning,DraftVersionVisibility,MajorWithMinorVersionsLimit,MajorVersionLimit,EnableModeration`, null, {
1510
- allowCache: options && options.refreshCache ? false : true,
1511
- jsonMetadata: jsonTypes.nometadata
1512
- });
1513
- return result;
1514
- } catch {
1515
- const result_1: iListVersionSettings = null;
1516
- return result_1;
1517
- }
1518
- }
1519
- export async function SetListVersionSettings(siteUrlOrId: string, listIdOrTitle: string, options: {
1520
- newSettings: Pick<iListVersionSettings, "EnableMinorVersions" | "EnableModeration" | "DraftVersionVisibility">
1521
- }): Promise<iListVersionSettings> {
1522
- let siteUrl = GetSiteUrl(siteUrlOrId);
1523
-
1524
- if (isNullOrEmptyString(listIdOrTitle)) return null;
1525
-
1526
- try {
1527
- function updateProp(prop: Partial<iListVersionSettings>) {
1528
- return GetJson<iListVersionSettings>(GetListRestUrl(siteUrl, listIdOrTitle),
1529
- jsonStringify(prop), {
1530
- method: "POST", spWebUrl: siteUrl,
1531
- xHttpMethod: "MERGE",
1532
- jsonMetadata: jsonTypes.nometadata
1533
- });
1534
- }
1535
-
1536
- const currentValues = await GetListVersionSettings(siteUrlOrId, listIdOrTitle, { refreshCache: true });
1537
- //replace undefined props with current values
1538
- const newSettings: iListVersionSettings = { ...currentValues, ...options.newSettings }
1539
-
1540
-
1541
- //need to do some of the changes one by one...
1542
- if (newSettings.EnableMinorVersions) {
1543
- if (!currentValues.EnableMinorVersions)
1544
- await updateProp({ EnableMinorVersions: true });
1545
-
1546
- if (!isNullOrUndefined(newSettings.DraftVersionVisibility)) {
1547
- if (currentValues.DraftVersionVisibility !== newSettings.DraftVersionVisibility)
1548
- await updateProp({ DraftVersionVisibility: newSettings.DraftVersionVisibility });
1549
- }
1550
- }
1551
- else {
1552
- if (currentValues.EnableMinorVersions) {
1553
- await updateProp({ EnableMinorVersions: false });
1554
- }
1555
- }
1556
-
1557
- if (newSettings.EnableMinorVersions && newSettings.EnableModeration) {
1558
- if (!currentValues.EnableModeration)
1559
- await updateProp({ EnableModeration: true });
1560
- }
1561
- else {
1562
- if (currentValues.EnableModeration) {
1563
- await updateProp({ EnableModeration: false });
1564
- }
1565
- }
1566
-
1567
- return await GetListVersionSettings(siteUrlOrId, listIdOrTitle, { refreshCache: true });
1568
- } catch {
1569
- const result: iListVersionSettings = null;
1570
- return result;
1571
- }
1572
- }
1
+ import { jsonClone } from "../../exports-index";
2
+ import { PushNoDuplicate, firstOrNull, makeUniqueArray, toHash } from "../../helpers/collections.base";
3
+ import { jsonStringify } from "../../helpers/json";
4
+ import { NormalizeListName, SPBasePermissions, SchemaJsonToXml, SchemaXmlToJson, extendFieldInfos } from "../../helpers/sharepoint";
5
+ import { normalizeGuid } from "../../helpers/strings";
6
+ import { SafeIfElse, isBoolean, isNotEmptyArray, isNullOrEmptyArray, isNullOrEmptyString, isNullOrUndefined, isNumber, isPromise, isString, isValidGuid } from "../../helpers/typecheckers";
7
+ import { makeServerRelativeUrl, normalizeUrl } from "../../helpers/url";
8
+ import { IDictionary } from "../../types/common.types";
9
+ import { IRestOptions, contentTypes, jsonTypes } from "../../types/rest.types";
10
+ import { BaseTypes, FieldTypeAsString, FieldTypes, IFieldInfo, IFieldInfoEX, IFieldInfoExHash, IFieldJsonSchema, IFieldLookupInfo, ISPEventReceiver, ListTemplateTypes, PageType, SPBasePermissionKind } from "../../types/sharepoint.types";
11
+ import { GeListItemsFoldersBehaviour, IListWorkflowAssociation, IRestItem, ListExperienceOptions, iContentType, iList, iListVersionSettings, iListView } from "../../types/sharepoint.utils.types";
12
+ import { ConsoleLogger } from "../consolelogger";
13
+ import { GetJson, GetJsonSync, longLocalCache, shortLocalCache } from "../rest";
14
+ import { GetRestBaseUrl, GetSiteUrl, LIST_EXPAND, LIST_SELECT } from "./common";
15
+ import { __fixGetListItemsResults } from "./listutils/common";
16
+ import { GetContentTypes, GetContentTypesSync, GetListsSync, IGetContentTypesOptions } from "./web";
17
+
18
+ const logger = ConsoleLogger.get("SharePoint.Rest.List");
19
+
20
+ /** returns /_api/web/lists/getById() or /_api/web/lists/getByTitle() */
21
+ export function GetListRestUrl(siteUrl: string, listIdOrTitle: string): string {
22
+ siteUrl = GetSiteUrl(siteUrl);
23
+
24
+ let listId = GetListId(siteUrl, listIdOrTitle);
25
+
26
+ let listPart = isValidGuid(listId) ? `getById('${normalizeGuid(listId)}')` : `getByTitle('${encodeURIComponent(listIdOrTitle)}')`;
27
+ return GetRestBaseUrl(siteUrl) + `/web/lists/${listPart}`;
28
+ }
29
+
30
+ export function GetListId(siteUrl: string, listIdOrTitle: string): string {
31
+ if (isNullOrEmptyString(listIdOrTitle)) return null;
32
+ if (isValidGuid(listIdOrTitle)) return listIdOrTitle;
33
+ //Issue 7508
34
+ //When translation is enabled, and user changes list title but he is not on the same language as the site
35
+ //he translates the list, but not changing its title
36
+ //so REST api /lists/getByTitle will not work
37
+ //instead, we need to get the list id from the web's lists collection.
38
+ let lists = GetListsSync(siteUrl);
39
+ var lower = listIdOrTitle.toLowerCase();
40
+ var list = firstOrNull(lists, l => l.Title.toLowerCase() === lower);
41
+ return list && list.Id || null;
42
+ }
43
+
44
+ /** get the list ID from a list page, such as a list view or an item form */
45
+ export function GetListIdFromPageSync(siteUrl: string, listPageUrl: string): string {
46
+ let url = `${GetRestBaseUrl(siteUrl)}/web/getlist('${makeServerRelativeUrl(listPageUrl.split('?')[0].split('#')[0])}')?$select=id`;
47
+ let response = GetJsonSync<{ Id: string; }>(url, null, {
48
+ ...longLocalCache,
49
+ jsonMetadata: jsonTypes.nometadata
50
+ });
51
+ if (!isNullOrUndefined(response) && response.success) {
52
+ let listId = response.result.Id;
53
+ return normalizeGuid(listId);
54
+ }
55
+ return null;
56
+ }
57
+
58
+ interface IGetSiteAssetLibraryResult { Id: string, Name: string, ServerRelativeUrl: string }
59
+ interface IGetSiteAssetLibraryReturnValue {
60
+ value: {
61
+ Id: string;
62
+ RootFolder: {
63
+ Name: string;
64
+ ServerRelativeUrl: string;
65
+ Exists: boolean;
66
+ };
67
+ }[];
68
+ }
69
+
70
+ /** ensures the site assets library exists and return its info. on errors - it will return null. */
71
+ export function EnsureAssetLibrary(siteUrl: string): Promise<IGetSiteAssetLibraryResult> {
72
+ siteUrl = GetSiteUrl(siteUrl);
73
+
74
+ let url = GetRestBaseUrl(siteUrl) +
75
+ "/web/lists/EnsureSiteAssetsLibrary?$select=ID,RootFolder/Name,RootFolder/ServerRelativeUrl,RootFolder/Exists&$expand=RootFolder";
76
+ return GetJson<{
77
+ d: { Id: string; RootFolder: { Name: string; ServerRelativeUrl: string; Exists: boolean; }; };
78
+ }>(url, null, { method: "POST", spWebUrl: siteUrl, ...longLocalCache }).then(result => {
79
+ if (result && result.d) {
80
+ return {
81
+ Id: result.d.Id,
82
+ Name: result.d.RootFolder.Name,
83
+ ServerRelativeUrl: result.d.RootFolder.ServerRelativeUrl
84
+ };
85
+ } else return null;
86
+ }).catch<IGetSiteAssetLibraryResult>(() => null);
87
+ }
88
+
89
+ interface IGetSitePagesLibrarResult { Id: string, Name: string, ServerRelativeUrl: string }
90
+
91
+ /** ensures the site pages library exists and return its info. on errors - it will return null. */
92
+ export async function EnsureSitePagesLibrary(siteUrl: string): Promise<IGetSitePagesLibrarResult> {
93
+ let url = `${GetRestBaseUrl(siteUrl)}/web/lists/EnsureSitePagesLibrary`
94
+ + `?$select=ID,RootFolder/Name,RootFolder/ServerRelativeUrl,RootFolder/Exists&$expand=RootFolder`;
95
+ let response = await GetJson<iList>(url, null, {
96
+ method: "POST",
97
+ jsonMetadata: jsonTypes.nometadata,
98
+ includeDigestInPost: true,
99
+ ...longLocalCache
100
+ });
101
+
102
+ if (!isNullOrUndefined(response) && !isNullOrUndefined(response.RootFolder)) {
103
+ return {
104
+ Id: response.Id,
105
+ Name: response.RootFolder.Name,
106
+ ServerRelativeUrl: response.RootFolder.ServerRelativeUrl
107
+ };
108
+ }
109
+ return null;
110
+ }
111
+
112
+ export function GetSiteAssetLibrary(siteUrl: string, sync?: false): Promise<IGetSiteAssetLibraryResult>;
113
+ export function GetSiteAssetLibrary(siteUrl: string, sync: true): IGetSiteAssetLibraryResult;
114
+ export function GetSiteAssetLibrary(siteUrl: string, sync?: boolean): IGetSiteAssetLibraryResult | Promise<IGetSiteAssetLibraryResult> {
115
+ let reqUrl = `${GetRestBaseUrl(siteUrl)}/web/lists?`
116
+ //Issue 1492: isSiteAssetsLibrary eq true does not work for reader users.
117
+ //+ `$filter=isSiteAssetsLibrary eq true&$select=ID,RootFolder/Name,RootFolder/ServerRelativeUrl,RootFolder/Exists`
118
+ + `$filter=EntityTypeName%20eq%20%27SiteAssets%27&$select=ID,RootFolder/Name,RootFolder/ServerRelativeUrl,RootFolder/Exists`
119
+ + `&$expand=RootFolder`;
120
+
121
+ let caller = sync ? GetJsonSync : GetJson;
122
+
123
+ let result = caller<IGetSiteAssetLibraryReturnValue>(reqUrl, null, { ...longLocalCache, jsonMetadata: jsonTypes.nometadata });
124
+
125
+ let transform: (v: IGetSiteAssetLibraryReturnValue) => IGetSiteAssetLibraryResult = (v) => {
126
+ if (isNotEmptyArray(v && v.value)) {
127
+ let assetLibrary = v.value[0];
128
+ return {
129
+ Id: assetLibrary.Id,
130
+ Name: assetLibrary.RootFolder.Name,
131
+ ServerRelativeUrl: assetLibrary.RootFolder.ServerRelativeUrl
132
+ };
133
+ }
134
+ return null;
135
+ };
136
+
137
+ if (isPromise(result))
138
+ return result.then(r => transform(r), () => null);
139
+ else
140
+ return result.success ? transform(result.result) : null;
141
+ }
142
+
143
+ /** Return the list Title */
144
+ export function GetListTitle(siteUrl: string, listIdOrTitle: string): Promise<string> {
145
+ siteUrl = GetSiteUrl(siteUrl);
146
+
147
+ return GetJson<{ d: { Title: string; }; }>(GetListRestUrl(siteUrl, listIdOrTitle) + `/Title`, null, { allowCache: true })
148
+ .then(r => {
149
+ return r.d.Title;
150
+ })
151
+ .catch<string>(() => null);
152
+ }
153
+
154
+ /** Return the list */
155
+ export function GetList(siteUrlOrId: string, listIdOrTitle: string, options?: {
156
+ includeViews?: boolean;
157
+ viewOptions?: IListViewOptions;
158
+ includeContentTypes?: boolean;
159
+ includeRootFolder?: boolean;
160
+ includeEventReceivers?: boolean;
161
+ }, refreshCache = false): Promise<iList> {
162
+ let siteUrl = GetSiteUrl(siteUrlOrId);
163
+
164
+ if (isNullOrEmptyString(listIdOrTitle)) {
165
+ return null;
166
+ }
167
+
168
+ return GetJson<{ d: iList; }>(GetListRestUrl(siteUrl, listIdOrTitle) + `?$select=${LIST_SELECT}&$expand=${LIST_EXPAND}`, null, { allowCache: true })
169
+ .then(async r => {
170
+ let list = r.d;
171
+ if (options) {
172
+ let promises = [];
173
+
174
+ if (options.includeViews) {
175
+ promises.push(GetListViews(siteUrl, listIdOrTitle, options.viewOptions, refreshCache).then((r) => {
176
+ list.Views = r;
177
+ }))
178
+ }
179
+ if (options.includeContentTypes) {
180
+ promises.push(GetListContentTypes(siteUrl, listIdOrTitle, null, refreshCache).then((r) => {
181
+ list.ContentTypes = r;
182
+ }));
183
+ }
184
+ if (options.includeRootFolder) {
185
+ promises.push(GetListRootFolder(siteUrl, listIdOrTitle).then((r) => {
186
+ list.RootFolder = r;
187
+ }));
188
+ }
189
+ if (options.includeEventReceivers) {
190
+ promises.push(GetListEventReceivers(siteUrl, listIdOrTitle, refreshCache).then((r) => {
191
+ list.EventReceivers = r;
192
+ }));
193
+ }
194
+
195
+ if (promises.length > 0) {
196
+ await Promise.all(promises);
197
+ }
198
+ }
199
+
200
+ if (list.EffectiveBasePermissions
201
+ && (isString(list.EffectiveBasePermissions.High) || isString(list.EffectiveBasePermissions.Low))) {
202
+ list.EffectiveBasePermissions = {
203
+ High: Number(list.EffectiveBasePermissions.High),
204
+ Low: Number(list.EffectiveBasePermissions.Low)
205
+ };
206
+ }
207
+
208
+ return list;
209
+ })
210
+ .catch<iList>(() => null);
211
+ }
212
+ /** Return the list */
213
+ export function GetListSync(siteUrl: string, listIdOrTitle: string): iList {
214
+ siteUrl = GetSiteUrl(siteUrl);
215
+
216
+ if (isNullOrEmptyString(listIdOrTitle)) return null;
217
+
218
+ let result = GetJsonSync<{ d: iList; }>(GetListRestUrl(siteUrl, listIdOrTitle) + `?$select=${LIST_SELECT}&$expand=${LIST_EXPAND}`, null, shortLocalCache);
219
+ if (result && result.success) {
220
+ let list = result.result.d;
221
+
222
+ if (list.EffectiveBasePermissions
223
+ && (isString(list.EffectiveBasePermissions.High)
224
+ || isString(list.EffectiveBasePermissions.Low))) {
225
+ list.EffectiveBasePermissions = {
226
+ High: Number(list.EffectiveBasePermissions.High),
227
+ Low: Number(list.EffectiveBasePermissions.Low)
228
+ };
229
+ }
230
+
231
+ return list;
232
+ }
233
+ else return null;
234
+ }
235
+
236
+ export function GetListNameSync(webUrl: string, listIdOrTitle: string): string {
237
+ let list = GetListSync(webUrl, listIdOrTitle);
238
+ return NormalizeListName({ EntityTypeName: list.EntityTypeName, BaseType: list.BaseType });
239
+ }
240
+
241
+ export async function GetListName(webUrl: string, listIdOrTitle: string) {
242
+ let list = await GetList(webUrl, listIdOrTitle);
243
+ return NormalizeListName({ EntityTypeName: list.EntityTypeName, BaseType: list.BaseType });
244
+ }
245
+
246
+ export function GetListRootFolder(siteUrlOrId: string, listIdOrTitle: string): Promise<{ ServerRelativeUrl: string; Name: string; }> {
247
+ let siteUrl = GetSiteUrl(siteUrlOrId);
248
+
249
+ return GetJson<{
250
+ d: { ServerRelativeUrl: string; Name: string; };
251
+ }>(GetListRestUrl(siteUrl, listIdOrTitle) + `/RootFolder?$Select=Name,ServerRelativeUrl`,
252
+ null, longLocalCache)
253
+ .then(r => {
254
+ return r.d;
255
+ })
256
+ .catch<{ ServerRelativeUrl: string; Name: string; }>(() => null);
257
+ }
258
+
259
+ export function GetListRootFolderSync(siteUrlOrId: string, listIdOrTitle: string): { ServerRelativeUrl: string; Name: string; } {
260
+ let siteUrl = GetSiteUrl(siteUrlOrId);
261
+
262
+ let result = GetJsonSync<{
263
+ d: { ServerRelativeUrl: string; Name: string; };
264
+ }>(GetListRestUrl(siteUrl, listIdOrTitle) + `/RootFolder?$Select=Name,ServerRelativeUrl`,
265
+ null, longLocalCache);
266
+
267
+ return SafeIfElse(() => result.result.d, null);
268
+ }
269
+
270
+ export function GetListField(siteUrlOrId: string, listIdOrTitle: string, fieldIdOrName: string, refreshCache?: boolean): Promise<IFieldInfo> {
271
+ let siteUrl = GetSiteUrl(siteUrlOrId);
272
+
273
+ var url = GetListRestUrl(siteUrl, listIdOrTitle) + `/fields`;
274
+
275
+ if (isValidGuid(fieldIdOrName)) {
276
+ url += `('${normalizeGuid(fieldIdOrName)}')`;
277
+ } else {
278
+ url += `/getbyinternalnameortitle(@u)?@u='${encodeURIComponent(fieldIdOrName)}'`;
279
+ }
280
+
281
+ let result = GetJson<{ d: IFieldInfo; }>(url, null, { allowCache: refreshCache !== true })
282
+ .then(r => {
283
+ return r.d;
284
+ })
285
+ .catch<IFieldInfo>(() => null);
286
+
287
+ return result;
288
+ }
289
+
290
+ function _getListFieldsRequestUrl(siteUrl: string, listIdOrTitle: string) {
291
+ return GetListRestUrl(siteUrl, listIdOrTitle) + `/fields`;
292
+ }
293
+
294
+ /** Gets ID, Title, ContentType Author, Editor, Created and Modified fields */
295
+ export function GetStandardListFields(siteUrlOrId: string, listIdOrTitle: string, refreshCache?: boolean) {
296
+ let fieldNames = ["ID", "Title", "ContentType", "Author", "Editor", "Created", "Modified"];
297
+ return GetListFields(siteUrlOrId, listIdOrTitle, { refreshCache: refreshCache, fieldNames: fieldNames });
298
+ }
299
+
300
+ export interface IGetListFieldsOptions {
301
+ refreshCache?: boolean;
302
+ /** fieldNames that should be returned with the request */
303
+ fieldNames?: string[];
304
+ }
305
+
306
+ function _processGetListFields(fields: IFieldInfo[], fieldNames: string[]) {
307
+ if (isNullOrEmptyArray(fields)) {
308
+ return fields as IFieldInfoEX[];
309
+ }
310
+ let extendedFields = extendFieldInfos(fields);
311
+
312
+ if (!isNullOrEmptyArray(fieldNames)) {
313
+ return extendedFields.filter((extendedField) => {
314
+ return fieldNames.includes(extendedField.InternalName);
315
+ });
316
+ }
317
+ return extendedFields;
318
+ }
319
+
320
+ export function GetListFields(siteUrlOrId: string, listIdOrTitle: string, options: IGetListFieldsOptions = {}): Promise<IFieldInfoEX[]> {
321
+ let siteUrl = GetSiteUrl(siteUrlOrId);
322
+ let url = _getListFieldsRequestUrl(siteUrl, listIdOrTitle);
323
+
324
+ let restOptions: IRestOptions = {
325
+ allowCache: options.refreshCache !== true,
326
+ jsonMetadata: jsonTypes.nometadata
327
+ };
328
+
329
+ return GetJson<{ value: IFieldInfo[]; }>(url, null, restOptions)
330
+ .then((result) => {
331
+ return _processGetListFields(result.value, options.fieldNames);
332
+ }).catch<IFieldInfoEX[]>(() => {
333
+ return null;
334
+ });
335
+ }
336
+
337
+ export function GetListFieldsSync(siteUrlOrId: string, listIdOrTitle: string, options: IGetListFieldsOptions = {}): IFieldInfoEX[] {
338
+ let siteUrl = GetSiteUrl(siteUrlOrId);
339
+ let url = _getListFieldsRequestUrl(siteUrl, listIdOrTitle);
340
+
341
+ let restOptions: IRestOptions = {
342
+ allowCache: options.refreshCache !== true,
343
+ jsonMetadata: jsonTypes.nometadata
344
+ };
345
+
346
+ let result = GetJsonSync<{ value: IFieldInfo[]; }>(url, null, restOptions);
347
+ if (result.success && !isNullOrUndefined(result.result)) {
348
+ return _processGetListFields(result.result.value, options.fieldNames);
349
+ }
350
+ return null;
351
+ }
352
+
353
+ export async function GetListFieldsAsHash(siteUrlOrId: string, listIdOrTitle: string, refreshCache?: boolean): Promise<IFieldInfoExHash> {
354
+ let siteUrl = GetSiteUrl(siteUrlOrId);
355
+
356
+ let fields = await GetListFields(siteUrl, listIdOrTitle, { refreshCache: refreshCache });
357
+ let hash: IFieldInfoExHash = {};
358
+ if (isNotEmptyArray(fields)) {
359
+ hash = toHash(fields, f => f.InternalName);
360
+ }
361
+ return hash;
362
+ }
363
+
364
+ export function GetListFieldsAsHashSync(siteUrlOrId: string, listIdOrTitle: string, refreshCache?: boolean): IFieldInfoExHash {
365
+ let siteUrl = GetSiteUrl(siteUrlOrId);
366
+
367
+ let fields = GetListFieldsSync(siteUrl, listIdOrTitle, { refreshCache: refreshCache });
368
+ let hash: IFieldInfoExHash = {};
369
+ if (isNotEmptyArray(fields)) {
370
+ fields.forEach(f => { hash[f.InternalName] = f; });
371
+ }
372
+ return hash;
373
+ }
374
+
375
+ export function GetListWorkflows(siteUrl: string, listIdOrTitle: string, refreshCache?: boolean): Promise<IListWorkflowAssociation[]> {
376
+ siteUrl = GetSiteUrl(siteUrl);
377
+
378
+ return GetJson<{
379
+ d: { results: IListWorkflowAssociation[]; };
380
+ }>(GetListRestUrl(siteUrl, listIdOrTitle) + `/workflowAssociations`,
381
+ null, { allowCache: refreshCache !== true })
382
+ .then(r => {
383
+ if (r && r.d && isNotEmptyArray(r.d.results)) {
384
+ r.d.results.forEach(wf => {
385
+ wf.BaseId = normalizeGuid(wf.BaseId);
386
+ wf.Id = normalizeGuid(wf.Id);
387
+ wf.ListId = normalizeGuid(wf.ListId);
388
+ wf.WebId = normalizeGuid(wf.WebId);
389
+ });
390
+ return r.d.results;
391
+ }
392
+ else return [];
393
+ })
394
+ .catch<IListWorkflowAssociation[]>(() => []);
395
+ }
396
+
397
+ export function UserHasManagePermissions(siteUrl: string, listIdOrTitle: string): Promise<boolean> {
398
+ siteUrl = GetSiteUrl(siteUrl);
399
+
400
+ return GetJson<{ d: { EffectiveBasePermissions: { High: number; Low: number; }; }; }>(GetListRestUrl(siteUrl, listIdOrTitle) + `/EffectiveBasePermissions`, null,
401
+ { ...shortLocalCache })
402
+ .then(r => {
403
+ return new SPBasePermissions(r.d.EffectiveBasePermissions).has(SPBasePermissionKind.ManageLists);
404
+ })
405
+ .catch<boolean>(() => null);
406
+ }
407
+
408
+ export function UserHasEditPermissions(siteUrl: string, listIdOrTitle: string): Promise<boolean> {
409
+ return UserHasPermissions(siteUrl, listIdOrTitle, SPBasePermissionKind.EditListItems);
410
+ }
411
+
412
+ export function UserHasPermissions(siteUrlOrId: string, listIdOrTitle: string, permissionKind: SPBasePermissionKind): Promise<boolean> {
413
+ let siteUrl = GetSiteUrl(siteUrlOrId);
414
+
415
+ return GetJson<{ d: { EffectiveBasePermissions: { High: number; Low: number; }; }; }>(GetListRestUrl(siteUrl, listIdOrTitle) + `/EffectiveBasePermissions`, null,
416
+ { ...shortLocalCache })
417
+ .then(r => {
418
+ return new SPBasePermissions(r.d.EffectiveBasePermissions).has(permissionKind);
419
+ })
420
+ .catch<boolean>(() => null);
421
+ }
422
+
423
+ export function UserHasPermissionsSync(siteUrlOrId: string, listIdOrTitle: string, permissionKind: SPBasePermissionKind): boolean {
424
+ let siteUrl = GetSiteUrl(siteUrlOrId);
425
+
426
+ const res = GetJsonSync<{ d: { EffectiveBasePermissions: { High: number; Low: number; }; }; }>(GetListRestUrl(siteUrl, listIdOrTitle) + `/EffectiveBasePermissions`, null,
427
+ { ...shortLocalCache });
428
+
429
+ return new SPBasePermissions(res.result.d.EffectiveBasePermissions).has(permissionKind);
430
+ }
431
+
432
+ /** create a new column and try to add it to default view. Send either Title and Type, or SchemaXml. Create with SchemaXml also adds to all content types */
433
+ export async function CreateField(siteUrl: string, listIdOrTitle: string, options: {
434
+ Title?: string;
435
+ Type?: FieldTypes;
436
+ Required?: boolean;
437
+ Indexed?: boolean;
438
+ SchemaXml?: string;
439
+ /** requies Name and StaticName for the internal name */
440
+ SchemaXmlSpecificInternalName?: boolean;
441
+ SkipAddToDefaultView?: boolean;
442
+ ClientSideComponentId?: string;
443
+ ClientSideComponentProperties?: string;
444
+ JSLink?: string;
445
+
446
+ }): Promise<IFieldInfoEX> {
447
+ siteUrl = GetSiteUrl(siteUrl);
448
+
449
+ let finish = async (result: IFieldInfo) => {
450
+ if (!result) {
451
+ return null;
452
+ }
453
+
454
+ let internalName = result.InternalName;
455
+ //we need to clear and reload the list fields cache, so call it and return our field from that collection.
456
+ let fields = await GetListFields(siteUrl, listIdOrTitle, { refreshCache: true });
457
+
458
+ try {
459
+ if (options.SkipAddToDefaultView !== true) {
460
+ //try to add it to default view, don't wait for it
461
+ GetListViews(siteUrl, listIdOrTitle).then(views => {
462
+ let defaultView = firstOrNull(views, v => v.DefaultView);
463
+ if (defaultView)
464
+ GetJson(GetListRestUrl(siteUrl, listIdOrTitle) + `/views('${defaultView.Id}')/ViewFields/addViewField('${internalName}')`, null, { method: "POST", spWebUrl: siteUrl });
465
+ });
466
+ }
467
+ }
468
+ catch (e) { }
469
+
470
+ return firstOrNull(fields, f => f.InternalName === internalName);
471
+ };
472
+
473
+ if (!isNullOrEmptyString(options.SchemaXml)) {
474
+ try {
475
+ let updateObject: IDictionary<any> = {
476
+ 'parameters': {
477
+ '__metadata': { 'type': 'SP.XmlSchemaFieldCreationInformation' },
478
+ 'SchemaXml': options.SchemaXml,
479
+ 'Options': options.SchemaXmlSpecificInternalName !== true ?
480
+ 4 ://SP.AddFieldOptions.addToAllContentTypes
481
+ 4 | 8//SP.AddFieldOptions.addToAllContentTypes | addFieldInternalNameHint
482
+ }
483
+ };
484
+ let url = `${GetListRestUrl(siteUrl, listIdOrTitle)}/fields/createFieldAsXml`;
485
+ let newFieldResult = await GetJson<{ d: IFieldInfo; }>(url, JSON.stringify(updateObject));
486
+
487
+ if (!isNullOrUndefined(newFieldResult)
488
+ && !isNullOrUndefined(newFieldResult.d)) {
489
+ if ((!isNullOrEmptyString(options.Title) && options.Title !== newFieldResult.d.Title)
490
+ || (isBoolean(options.Indexed) && options.Indexed !== newFieldResult.d.Indexed)) {
491
+ let updatedField = await UpdateField(siteUrl, listIdOrTitle, newFieldResult.d.InternalName, {
492
+ Title: options.Title,
493
+ Indexed: options.Indexed === true
494
+ });
495
+ return finish(updatedField);
496
+ }
497
+ }
498
+
499
+ return finish(newFieldResult && newFieldResult.d);
500
+ } catch {
501
+ }
502
+ return null;
503
+ } else if (!isNullOrEmptyString(options.Title) && !isNullOrUndefined(options.Type)) {
504
+ let updateObject: IDictionary<any> = {
505
+ '__metadata': { 'type': 'SP.Field' },
506
+ 'Title': options.Title,
507
+ 'FieldTypeKind': options.Type,
508
+ 'Required': options.Required === true,
509
+ 'Indexed': options.Indexed === true
510
+ };
511
+ if (!isNullOrEmptyString(options.ClientSideComponentId)) {
512
+ updateObject.ClientSideComponentId = options.ClientSideComponentId;
513
+ }
514
+ if (!isNullOrEmptyString(options.ClientSideComponentProperties)) {
515
+ updateObject.ClientSideComponentProperties = options.ClientSideComponentProperties;
516
+ }
517
+ if (!isNullOrEmptyString(options.JSLink)) {
518
+ updateObject.JSLink = options.JSLink;
519
+ }
520
+
521
+ try {
522
+ let url = `${GetListRestUrl(siteUrl, listIdOrTitle)}/fields`;
523
+ let newFieldResult = await GetJson<{ d: IFieldInfo; }>(url, JSON.stringify(updateObject));
524
+ return finish(newFieldResult && newFieldResult.d);
525
+ } catch {
526
+ }
527
+ return null;
528
+ }
529
+ else {
530
+ console.error("You must send either SchemaXml or Title and Type");
531
+ return null;
532
+ }
533
+ }
534
+ /** Update field SchemaXml OR Title, only 1 update at a time supported. */
535
+ export async function UpdateField(siteUrlOrId: string, listIdOrTitle: string, fieldInternalName: string, options: {
536
+ Title?: string;
537
+ Indexed?: boolean;
538
+ /** Update 'Choices' propertry on 'Choice' and 'MultiChoice' field types. */
539
+ Choices?: string[];
540
+ SchemaXml?: string;
541
+ FieldType?: FieldTypeAsString;
542
+ Required?: boolean;
543
+ Hidden?: boolean;
544
+ JSLink?: string;
545
+ ClientSideComponentId?: string;
546
+ ClientSideComponentProperties?: string;
547
+ }): Promise<IFieldInfoEX> {
548
+ let siteUrl = GetSiteUrl(siteUrlOrId);
549
+
550
+ let finish = async () => {
551
+ //we need to clear and reload the list fields cache, so call it and return our field from that collection.
552
+ let fields = await GetListFields(siteUrl, listIdOrTitle, { refreshCache: true });
553
+ return firstOrNull(fields, f => f.InternalName === fieldInternalName);
554
+ };
555
+
556
+ let fields = await GetListFieldsAsHash(siteUrl, listIdOrTitle, true);
557
+ let thisField = fields[fieldInternalName];
558
+
559
+ //updates can either be SchemaXml, or others. Cannot be both.
560
+ let updates: IDictionary<any> = {
561
+ '__metadata': { 'type': 'SP.Field' }
562
+ };
563
+
564
+ if (!isNullOrEmptyString(options.SchemaXml)) {
565
+ updates.SchemaXml = options.SchemaXml;
566
+ }
567
+ else {
568
+ //cannot send schema updates with other updates.
569
+ if (!isNullOrEmptyString(options.Title)) {
570
+ updates.Title = options.Title;
571
+ }
572
+ if (!isNullOrEmptyString(options.FieldType)) {
573
+ updates.TypeAsString = options.FieldType;
574
+ }
575
+ if (isBoolean(options.Required)) {
576
+ updates.Required = options.Required === true;
577
+ }
578
+ if (isBoolean(options.Indexed)) {
579
+ updates.Indexed = options.Indexed === true;
580
+ }
581
+ if (!isNullOrEmptyArray(options.Choices)) {
582
+ let choiceType = options.FieldType || thisField.TypeAsString;
583
+ if (choiceType === "Choice" || choiceType === "MultiChoice") {
584
+ updates["__metadata"]["type"] = choiceType === "Choice" ? "SP.FieldChoice" : "SP.FieldMultiChoice"
585
+ updates.Choices = { "results": options.Choices };
586
+ } else {
587
+ logger.warn("Can only update 'Choices' property on 'Choice' and 'MultiChoice' field types.");
588
+ }
589
+ }
590
+ if (isBoolean(options.Hidden)) {
591
+ //this requries the CanToggleHidden to be in the schema... if not - we will need to add it before we can update this.
592
+ let fields = await GetListFieldsAsHash(siteUrl, listIdOrTitle, false);
593
+ let thisField = fields[fieldInternalName];
594
+ if (thisField.Hidden !== options.Hidden) {
595
+ if (thisField) {
596
+ if (thisField.SchemaJson.Attributes.CanToggleHidden !== "TRUE") {
597
+ await UpdateField(siteUrl, listIdOrTitle, fieldInternalName, {
598
+ SchemaXml:
599
+ thisField.SchemaXml.replace("<Field ", `<Field CanToggleHidden="TRUE" `)
600
+ });
601
+ }
602
+ }
603
+ updates.Hidden = options.Hidden === true;
604
+ }
605
+ }
606
+
607
+ if (!isNullOrEmptyString(options.ClientSideComponentId))
608
+ updates.ClientSideComponentId = options.ClientSideComponentId;
609
+ if (!isNullOrEmptyString(options.ClientSideComponentProperties))
610
+ updates.ClientSideComponentProperties = options.ClientSideComponentProperties;
611
+ if (!isNullOrEmptyString(options.JSLink))
612
+ updates.JSLink = options.JSLink;
613
+ }
614
+
615
+ if (Object.keys(updates).length > 1) {
616
+ return GetJson(GetListRestUrl(siteUrl, listIdOrTitle) + `/fields/getbyinternalnameortitle('${fieldInternalName}')`,
617
+ JSON.stringify(updates), { xHttpMethod: "MERGE" })
618
+ .then(r => {
619
+ return finish();
620
+ })
621
+ .catch<IFieldInfoEX>(() => null);
622
+ }
623
+ else {
624
+ console.error("You must send an option to update");
625
+ return null;
626
+ }
627
+ }
628
+
629
+ export async function ChangeTextFieldMode(
630
+ siteUrlOrId: string,
631
+ listIdOrTitle: string,
632
+ textMode: "singleline" | "multiline" | "html",
633
+ currentField: IFieldInfoEX
634
+ ) {
635
+ const newSchema = jsonClone(currentField.SchemaJson);
636
+ const currentSchemaAttributes = newSchema.Attributes;
637
+
638
+ switch (textMode) {
639
+ case "singleline":
640
+ let shouldIntermediateUpdate = false;
641
+
642
+ if (currentSchemaAttributes.RichText === 'TRUE') {
643
+ currentSchemaAttributes.RichText = 'FALSE';
644
+ shouldIntermediateUpdate = true;
645
+ };
646
+ if (currentSchemaAttributes.RichTextMode === 'FullHTML') {
647
+ currentSchemaAttributes.RichTextMode = 'Compatible';
648
+ shouldIntermediateUpdate = true;
649
+ };
650
+
651
+ if (shouldIntermediateUpdate) {
652
+ const intermediateSchema = SchemaJsonToXml(newSchema);
653
+ const intermediateUpdatedField = await UpdateField(siteUrlOrId, listIdOrTitle, currentField.InternalName, {
654
+ SchemaXml: intermediateSchema
655
+ });
656
+ // Early exit if intermediate change failed.
657
+ if (isNullOrUndefined(intermediateUpdatedField))
658
+ return false;
659
+ };
660
+
661
+ // Actual type update.
662
+ currentSchemaAttributes.Type = 'Text';
663
+ delete currentSchemaAttributes.RichTextMode;
664
+ delete currentSchemaAttributes.RichText;
665
+ break;
666
+ case "multiline":
667
+ currentSchemaAttributes.Type = 'Note';
668
+ currentSchemaAttributes.RichText = 'FALSE';
669
+ currentSchemaAttributes.RichTextMode = 'Compatible';
670
+ break;
671
+ case "html":
672
+ currentSchemaAttributes.Type = 'Note';
673
+ currentSchemaAttributes.RichText = 'TRUE';
674
+ currentSchemaAttributes.RichTextMode = 'FullHTML';
675
+ break;
676
+ }
677
+
678
+ const updatedSchema = SchemaJsonToXml(newSchema);
679
+ const fieldUpdated = await UpdateField(siteUrlOrId, listIdOrTitle, currentField.InternalName, {
680
+ SchemaXml: updatedSchema
681
+ });
682
+
683
+ // If object is null or undefined then request has failed.
684
+ return !isNullOrUndefined(fieldUpdated);
685
+ }
686
+
687
+ export async function ChangeDatetimeFieldMode(
688
+ siteUrlOrId: string,
689
+ listIdOrTitle: string,
690
+ includeTime: boolean,
691
+ currentField: IFieldInfoEX
692
+ ) {
693
+ const dateTimeFormat = 'DateTime';
694
+ const dateOnlyFormat = 'DateOnly';
695
+
696
+ const newSchema = jsonClone(currentField.SchemaJson);
697
+ const fieldAttributes = newSchema.Attributes;
698
+ let needUpdate = false;
699
+ if (includeTime && fieldAttributes.Format === dateOnlyFormat) {
700
+ needUpdate = true;
701
+ fieldAttributes.Format = dateTimeFormat;
702
+ }
703
+ else if (!includeTime && fieldAttributes.Format === dateTimeFormat) {
704
+ needUpdate = true;
705
+ fieldAttributes.Format = dateOnlyFormat;
706
+ }
707
+
708
+ if (needUpdate) {
709
+ const updatedSchema = SchemaJsonToXml(newSchema);
710
+ const updateResponse = await UpdateField(siteUrlOrId, listIdOrTitle, currentField.InternalName, {
711
+ SchemaXml: updatedSchema
712
+ });
713
+ return !isNullOrUndefined(updateResponse);
714
+ }
715
+
716
+ // If an already existing format was chosen.
717
+ return true;
718
+ }
719
+
720
+ export async function DeleteField(siteUrl: string, listIdOrTitle: string, fieldInternalName: string, options?: { DeleteHiddenField?: boolean; }): Promise<boolean> {
721
+ siteUrl = GetSiteUrl(siteUrl);
722
+
723
+ // let finish = async () => {
724
+ // //we need to clear and reload the list fields cache, so call it and return our field from that collection.
725
+ // let fields = await GetListFields(siteUrl, listIdOrTitle, { refreshCache: true });
726
+ // return firstOrNull(fields, f => f.InternalName === fieldInternalName);
727
+ // };
728
+
729
+ if (options && options.DeleteHiddenField)
730
+ await UpdateField(siteUrl, listIdOrTitle, fieldInternalName, { Hidden: false });
731
+
732
+
733
+ return GetJson(GetListRestUrl(siteUrl, listIdOrTitle) + `/fields/getbyinternalnameortitle('${fieldInternalName}')`, null, {
734
+ method: "POST",
735
+ xHttpMethod: "DELETE"
736
+ })
737
+ .then(r => true)
738
+ .catch<boolean>((e) => false);
739
+ }
740
+
741
+ export interface IListViewOptions { includeViewFields?: boolean; }
742
+ export function GetListViews(siteUrl: string, listIdOrTitle: string, options?: IListViewOptions, refreshCache = false): Promise<iListView[]> {
743
+ siteUrl = GetSiteUrl(siteUrl);
744
+
745
+ return GetJson<{
746
+ value: iListView[];
747
+ }>(GetListRestUrl(siteUrl, listIdOrTitle) + `/views?$select=Title,Id,ServerRelativeUrl,RowLimit,Paged,ViewQuery,ListViewXml,PersonalView,MobileView,MobileDefaultView,Hidden,DefaultView,ReadOnlyView${options && options.includeViewFields ? "&$expand=ViewFields" : ""}`,
748
+ null, { allowCache: refreshCache !== true, jsonMetadata: jsonTypes.nometadata })
749
+ .then(r => {
750
+ let views = r.value;
751
+ if (isNotEmptyArray(views)) {
752
+ views.forEach(v => {
753
+ v.Id = normalizeGuid(v.Id);
754
+ if (options && options.includeViewFields) {
755
+ v.ViewFields = v.ViewFields && v.ViewFields["Items"] && v.ViewFields["Items"] || [];
756
+ }
757
+ });
758
+ }
759
+ return views;
760
+ })
761
+ .catch<iListView[]>(() => null);
762
+ }
763
+
764
+ export function GetListViewsSync(siteUrl: string, listIdOrTitle: string, refreshCache = false): iListView[] {
765
+ siteUrl = GetSiteUrl(siteUrl);
766
+
767
+ let result = GetJsonSync<{
768
+ d: {
769
+ results: iListView[];
770
+ };
771
+ }>(GetListRestUrl(siteUrl, listIdOrTitle) + `/views`,
772
+ null, { allowCache: refreshCache !== true });
773
+ if (result.success) {
774
+ let views = result && result.result && result.result.d && result.result.d.results;
775
+ if (isNotEmptyArray(views)) {
776
+ views.forEach(v => { v.Id = normalizeGuid(v.Id); });
777
+ }
778
+ return views;
779
+ }
780
+ return null;
781
+ }
782
+
783
+ export async function AddViewFieldToListView(siteUrl: string, listIdOrTitle: string, viewId: string, viewField: string) {
784
+ return _addOrRemoveViewField(siteUrl, listIdOrTitle, viewId, viewField, "addviewfield");
785
+ }
786
+
787
+ export async function RemoveViewFieldFromListView(siteUrl: string, listIdOrTitle: string, viewId: string, viewField: string) {
788
+ return _addOrRemoveViewField(siteUrl, listIdOrTitle, viewId, viewField, "removeviewfield");
789
+ }
790
+
791
+ async function _addOrRemoveViewField(siteUrl: string, listIdOrTitle: string, viewId: string, viewField: string, action: "addviewfield" | "removeviewfield") {
792
+ siteUrl = GetSiteUrl(siteUrl);
793
+
794
+ if (isNullOrEmptyString(viewField) || !isValidGuid(viewId)) {
795
+ return false;
796
+ }
797
+
798
+ let views = await GetListViews(siteUrl, listIdOrTitle, { includeViewFields: true });
799
+
800
+ if (isNullOrEmptyArray(views)) {
801
+ return false;
802
+ }
803
+
804
+ let view = views.filter((view) => {
805
+ return normalizeGuid(view.Id) === normalizeGuid(viewId);
806
+ })[0];
807
+
808
+ if (isNullOrUndefined(view)) {
809
+ return false;
810
+ }
811
+
812
+ let hasField = view.ViewFields.includes(viewField);
813
+
814
+ if (action === "addviewfield" && hasField === true) {
815
+ return true;
816
+ }
817
+
818
+ if (action === "removeviewfield" && hasField === false) {
819
+ return true;
820
+ }
821
+
822
+ try {
823
+ let url = GetListRestUrl(siteUrl, listIdOrTitle) + `/views('${normalizeGuid(view.Id)}')/viewfields/${action}('${viewField}')`;
824
+
825
+ let result = await GetJson<{ "odata.null": boolean; }>(url, null, { method: "POST" });
826
+
827
+ if (result && result["odata.null"] === true) {
828
+ return true;
829
+ }
830
+ } catch { }
831
+
832
+ return false;
833
+ }
834
+
835
+ export function GetListContentTypes(siteUrl: string, listIdOrTitle: string,
836
+ options?: Omit<IGetContentTypesOptions, "listIdOrTitle" | "fromRooWeb">, refreshCache = false): Promise<iContentType[]> {
837
+ return GetContentTypes(siteUrl, { ...(options || {}), listIdOrTitle: listIdOrTitle }, refreshCache);
838
+ }
839
+
840
+ export function GetListContentTypesSync(siteUrl: string, listIdOrTitle: string,
841
+ options?: Omit<IGetContentTypesOptions, "listIdOrTitle" | "fromRooWeb">, refreshCache = false): iContentType[] {
842
+ return GetContentTypesSync(siteUrl, { ...(options || {}), listIdOrTitle: listIdOrTitle }, refreshCache);
843
+ }
844
+
845
+ /** generic version. for the KWIZ forms version that supports action id call GetListFormUrlAppsWeb instead */
846
+ export function GetListFormUrl(siteUrl: string, listId: string, pageType: PageType, params?: { contentTypeId?: string; itemId?: number | string; rootFolder?: string }) {
847
+ siteUrl = GetSiteUrl(siteUrl);
848
+
849
+ if (!isValidGuid(listId)) console.error('GetListFormUrl requires a list id');
850
+ let url = `${normalizeUrl(siteUrl)}/_layouts/15/listform.aspx?PageType=${pageType}&ListId=${encodeURIComponent(listId)}`;
851
+ if (params) {
852
+ if (!isNullOrEmptyString(params.contentTypeId))
853
+ url += `&ContentTypeId=${encodeURIComponent(params.contentTypeId)}`;
854
+ if (!isNullOrEmptyString(params.itemId))
855
+ url += `&ID=${encodeURIComponent(params.itemId as string)}`;
856
+ if (!isNullOrEmptyString(params.rootFolder))
857
+ url += `&RootFolder=${encodeURIComponent(params.rootFolder)}`;
858
+ }
859
+ return url;
860
+ }
861
+
862
+ export function GetFieldSchemaSync(siteUrl: string, listIdOrTitle: string, fieldInternalName: string, refreshCache?: boolean): IFieldJsonSchema {
863
+ siteUrl = GetSiteUrl(siteUrl);
864
+
865
+ //ISSUE: 1516 - The get schema request will fail if the field doesn't exist in the list, so we load the fields and ensure the field
866
+ //exists before requesting the schema.
867
+ let fields = GetListFieldsSync(siteUrl, listIdOrTitle, {
868
+ refreshCache: refreshCache,
869
+ fieldNames: [fieldInternalName]
870
+ });
871
+
872
+ if (isNullOrEmptyArray(fields)) {
873
+ return null;
874
+ }
875
+
876
+ let field = fields[0];
877
+ return SchemaXmlToJson(field.SchemaXml);
878
+ // let url = GetListRestUrl(siteUrl, listIdOrTitle) + `/fields/getByInternalNameOrTitle('${fieldInternalName}')?$select=SchemaXml`;
879
+ // let result = GetJsonSync<{ d: { SchemaXml: string; }; }>(
880
+ // url,
881
+ // null,
882
+ // {
883
+ // ...shortLocalCache,
884
+ // forceCacheUpdate: refreshCache === true
885
+ // });
886
+
887
+ // if (result && result.success) {
888
+ // return SchemaXmlToJson(result.result.d.SchemaXml);
889
+ // }
890
+ // return null;
891
+ //#endregion
892
+ }
893
+
894
+ export async function GetFieldSchema(siteUrl: string, listIdOrTitle: string, fieldInternalName: string, refreshCache?: boolean) {
895
+ siteUrl = GetSiteUrl(siteUrl);
896
+
897
+ //ISSUE: 1516 - The get schema request will fail if the field doesn't exist in the list, so we load the fields and ensure the field
898
+ //exists before requesting the schema
899
+ let fields = await GetListFields(siteUrl, listIdOrTitle, {
900
+ refreshCache: refreshCache,
901
+ fieldNames: [fieldInternalName]
902
+ });
903
+
904
+ if (isNullOrEmptyArray(fields)) {
905
+ return null;
906
+ }
907
+
908
+ let field = fields[0];
909
+ return SchemaXmlToJson(field.SchemaXml);
910
+ }
911
+
912
+ export async function GetListItems(siteUrl: string, listIdOrTitle: string, options: {
913
+ /** Optional, default: 1000. 0: get all items. */
914
+ rowLimit?: number;
915
+ /** Id, Title, Modified, FileLeafRef, FileDirRef, FileRef, FileSystemObjectType */
916
+ columns: (string | IFieldInfoEX)[];
917
+ foldersBehaviour?: GeListItemsFoldersBehaviour;
918
+ /** Optional, request to expand some columns. */
919
+ expand?: string[];
920
+ /** allow to change the jsonMetadata for this request, default: verbose */
921
+ jsonMetadata?: jsonTypes;
922
+ refreshCache?: boolean;
923
+ /** allow to send a filter statement */
924
+ $filter?: string;
925
+ }): Promise<IRestItem[]> {
926
+ let info = _GetListItemsInfo(siteUrl, listIdOrTitle, options);
927
+
928
+ let items: IRestItem[] = [];
929
+
930
+ do {
931
+ let resultItems: IRestItem[] = [];
932
+ let next: string = null;
933
+ if (info.noMetadata) {
934
+ let requestResult = (await GetJson<{
935
+ value: IRestItem[];
936
+ "odata.nextLink": string;
937
+ }>(info.requestUrl, null, {
938
+ allowCache: options.refreshCache !== true,
939
+ jsonMetadata: options.jsonMetadata
940
+ }));
941
+ resultItems = requestResult.value;
942
+ next = requestResult["odata.nextLink"];
943
+ }
944
+ else {
945
+ let requestResult = (await GetJson<{
946
+ d: {
947
+ results: IRestItem[];
948
+ __next?: string;
949
+ };
950
+ }>(info.requestUrl, null, {
951
+ allowCache: options.refreshCache !== true
952
+ }));
953
+ resultItems = requestResult.d.results;
954
+ next = requestResult.d.__next;
955
+ }
956
+
957
+ if (isNotEmptyArray(resultItems))
958
+ items.push(...resultItems);
959
+
960
+ if (info.totalNumberOfItemsToGet > items.length)
961
+ info.requestUrl = next;
962
+ else
963
+ info.requestUrl = null;
964
+
965
+ } while (!isNullOrEmptyString(info.requestUrl));
966
+
967
+ return __fixGetListItemsResults(siteUrl, listIdOrTitle, items, options.foldersBehaviour, info.expandedLookupFields);
968
+ }
969
+
970
+ export function GetListItemsSync(siteUrl: string, listIdOrTitle: string, options: {
971
+ /** Optional, default: 1000. 0: get all items. */
972
+ rowLimit?: number;
973
+ /** Id, Title, Modified, FileLeafRef, FileDirRef, FileRef, FileSystemObjectType */
974
+ columns: (string | IFieldInfoEX)[];
975
+ foldersBehaviour?: GeListItemsFoldersBehaviour;
976
+ /** Optional, request to expand some columns. */
977
+ expand?: string[];
978
+ /** allow to send a filter statement */
979
+ $filter?: string;
980
+ }): IRestItem[] {
981
+ let info = _GetListItemsInfo(siteUrl, listIdOrTitle, options);
982
+
983
+ let items: IRestItem[] = [];
984
+
985
+ do {
986
+ let resultItems: IRestItem[] = [];
987
+ let next: string = null;
988
+ if (info.noMetadata) {
989
+ let requestResult = GetJsonSync<{
990
+ value: IRestItem[];
991
+ "odata.nextLink": string;
992
+ }>(info.requestUrl, null, { allowCache: true });
993
+ if (requestResult.success) {
994
+ resultItems = requestResult.result.value;
995
+ next = requestResult.result["odata.nextLink"];
996
+ }
997
+ }
998
+ else {
999
+ let requestResult = GetJsonSync<{
1000
+ d: { results: IRestItem[]; __next?: string; };
1001
+ }>(info.requestUrl, null, { allowCache: true });
1002
+ if (requestResult.success) {
1003
+ resultItems = requestResult.result.d.results;
1004
+ next = requestResult.result.d.__next;
1005
+ }
1006
+ }
1007
+
1008
+ if (isNotEmptyArray(resultItems))
1009
+ items.push(...resultItems);
1010
+
1011
+ if (info.totalNumberOfItemsToGet > items.length)
1012
+ info.requestUrl = next;
1013
+ else
1014
+ info.requestUrl = null;
1015
+
1016
+ } while (!isNullOrEmptyString(info.requestUrl));
1017
+
1018
+ return __fixGetListItemsResults(siteUrl, listIdOrTitle, items, options.foldersBehaviour, info.expandedLookupFields);
1019
+ }
1020
+
1021
+ function _GetListItemsInfo(siteUrl: string, listIdOrTitle: string, options: {
1022
+ /** Optional, default: 1000. 0: get all items. */
1023
+ rowLimit?: number;
1024
+ /** Id, Title, Modified, FileLeafRef, FileDirRef, FileRef, FileSystemObjectType */
1025
+ columns: (string | IFieldInfoEX)[];
1026
+ /** Optional, request to expand some columns. */
1027
+ expand?: string[];
1028
+ /** allow to change the jsonMetadata for this request, default: verbose */
1029
+ jsonMetadata?: jsonTypes;
1030
+ /** allow to send a filter statement */
1031
+ $filter?: string;
1032
+ }) {
1033
+ siteUrl = GetSiteUrl(siteUrl);
1034
+
1035
+ let url = GetListRestUrl(siteUrl, listIdOrTitle) + `/items`;
1036
+ let queryParams: string[] = [];
1037
+
1038
+ //Issue 8189 expand lookup fields
1039
+ let columns: string[] = [];
1040
+ let expand: string[] = [];
1041
+ let expandedLookupFields: IFieldInfoEX[] = [];
1042
+ options.columns.forEach(c => {
1043
+ if (isString(c)) columns.push(c);
1044
+ else {
1045
+ let internalName = c.InternalName;
1046
+ //Issue 828, 336
1047
+ if (internalName.startsWith("_")) internalName = `OData_${internalName}`;
1048
+
1049
+ let isLookupField = c.TypeAsString === "Lookup" || c.TypeAsString === "LookupMulti";
1050
+ let isUserField = c.TypeAsString === "User" || c.TypeAsString === "UserMulti";
1051
+
1052
+ if (isLookupField || isUserField) {
1053
+ //ISSUE: 1519 - Added lookupField property to able to retrieve value of the additional lookup field key
1054
+ let lookupField = (c as IFieldLookupInfo).LookupField;
1055
+ if (!isNullOrEmptyString(lookupField) && isLookupField) {
1056
+ columns.push(`${internalName}/${lookupField}`);
1057
+ }
1058
+ //we want to expand it
1059
+ columns.push(`${internalName}/Title`);
1060
+ columns.push(`${internalName}/Id`);
1061
+ expand.push(internalName);
1062
+ expandedLookupFields.push(c);
1063
+ }
1064
+ else columns.push(internalName);
1065
+ }
1066
+ });
1067
+ if (isNotEmptyArray(options.expand)) {
1068
+ expand.push(...options.expand);
1069
+ }
1070
+
1071
+ //add the ones we need
1072
+ PushNoDuplicate(columns, "Id");
1073
+ PushNoDuplicate(columns, "FileRef");
1074
+ PushNoDuplicate(columns, "FileSystemObjectType");
1075
+
1076
+ queryParams.push(`$select=${encodeURIComponent(makeUniqueArray(columns).join(','))}`);
1077
+
1078
+ if (isNotEmptyArray(expand))
1079
+ queryParams.push(`$expand=${encodeURIComponent(makeUniqueArray(expand).join(','))}`);
1080
+
1081
+ let batchSize = 2000;
1082
+ let limit = options.rowLimit >= 0 && options.rowLimit < batchSize ? options.rowLimit : batchSize;
1083
+ let totalNumberOfItemsToGet = !isNumber(options.rowLimit) || options.rowLimit < 1 ? 99999 : options.rowLimit > batchSize ? options.rowLimit : limit;
1084
+
1085
+ if (!isNullOrEmptyString(options.$filter))
1086
+ queryParams.push(`$filter=${options.$filter}`);
1087
+ queryParams.push(`$top=${limit}`);
1088
+
1089
+ let requestUrl = url + (queryParams.length > 0 ? '?' + queryParams.join('&') : '');
1090
+ let noMetadata = options.jsonMetadata === jsonTypes.nometadata;
1091
+
1092
+ return { requestUrl, noMetadata, totalNumberOfItemsToGet, expandedLookupFields };
1093
+ }
1094
+
1095
+ /** Find an item by id, even if it is nested in a sub-folder */
1096
+ export function FindListItemById(items: IRestItem[], itemId: number): IRestItem {
1097
+ for (let i = 0; i < items.length; i++) {
1098
+ let current = items[i];
1099
+ if (current.Id === itemId) return current;
1100
+ else if (isNotEmptyArray(current.__Items))//folder? look inside
1101
+ {
1102
+ let nestedResult = FindListItemById(current.__Items, itemId);
1103
+ if (!isNullOrUndefined(nestedResult)) return nestedResult;
1104
+ }
1105
+ }
1106
+ //not found
1107
+ return null;
1108
+ }
1109
+
1110
+ function _getListEventReceiversRequestUrl(siteUrl: string, listIdOrTitle: string) {
1111
+ return GetListRestUrl(siteUrl, listIdOrTitle) + `/EventReceivers`
1112
+ }
1113
+
1114
+ export async function GetListEventReceivers(siteUrl: string, listIdOrTitle: string, refreshCache?: boolean): Promise<ISPEventReceiver[]> {
1115
+ try {
1116
+ let url = _getListEventReceiversRequestUrl(siteUrl, listIdOrTitle);
1117
+ let response = await GetJson<{
1118
+ value: ISPEventReceiver[];
1119
+ }>(url,
1120
+ null, {
1121
+ allowCache: refreshCache !== true,
1122
+ jsonMetadata: jsonTypes.nometadata
1123
+ });
1124
+
1125
+ return !isNullOrUndefined(response) ? response.value : null;
1126
+ } catch {
1127
+ }
1128
+
1129
+ return null;
1130
+ }
1131
+
1132
+ export async function AddListEventReceiver(siteUrl: string, listIdOrTitle: string, eventReceiverDefinition: Pick<ISPEventReceiver, "EventType" | "ReceiverName" | "ReceiverUrl" | "SequenceNumber">): Promise<ISPEventReceiver> {
1133
+ let newEventReceiver: Omit<ISPEventReceiver, "ReceiverId" | "Synchronization"> = {
1134
+ ReceiverAssembly: "",
1135
+ ReceiverClass: "",
1136
+ ...eventReceiverDefinition
1137
+ };
1138
+
1139
+ try {
1140
+ let url = _getListEventReceiversRequestUrl(siteUrl, listIdOrTitle);
1141
+ let response = await GetJson<ISPEventReceiver>(url, JSON.stringify(newEventReceiver), {
1142
+ method: "POST",
1143
+ includeDigestInPost: true,
1144
+ jsonMetadata: jsonTypes.nometadata,
1145
+ headers: {
1146
+ "content-type": contentTypes.json
1147
+ }
1148
+ });
1149
+
1150
+ return !isNullOrUndefined(response) && isValidGuid(response.ReceiverId) ? response : null;
1151
+ } catch {
1152
+ }
1153
+
1154
+ return null;
1155
+ }
1156
+
1157
+ export async function DeleteListEventReceiver(siteUrl: string, listIdOrTitle: string, eventReceiverId: string): Promise<boolean> {
1158
+ try {
1159
+ let url = `${_getListEventReceiversRequestUrl(siteUrl, listIdOrTitle)}('${normalizeGuid(eventReceiverId)}')/deleteObject`;
1160
+ let response = await GetJson<{ "odata.null": boolean }>(url, null, {
1161
+ method: "POST",
1162
+ includeDigestInPost: true,
1163
+ jsonMetadata: jsonTypes.nometadata
1164
+ });
1165
+
1166
+ return !isNullOrUndefined(response) && response["odata.null"] === true;
1167
+ } catch {
1168
+ }
1169
+
1170
+ return false;
1171
+ }
1172
+
1173
+ /** timestamp of changes:
1174
+ * - item updates
1175
+ * - changes to columns
1176
+ * - content types
1177
+ * - list versioning settings
1178
+ * - list title/description
1179
+ * - content approval settings
1180
+ * does not track:
1181
+ * - Changes to views
1182
+ * - changing list/items permissions
1183
+ */
1184
+ export function GetListLastItemModifiedDate(siteUrl: string, listIdOrTitle: string, options: {
1185
+ sync: true;
1186
+ refreshCache?: boolean;
1187
+ /** ignore system changes */
1188
+ userChangesOnly?: boolean;
1189
+ }): string;
1190
+ /** timestamp of changes:
1191
+ * - item updates
1192
+ * - changes to columns
1193
+ * - content types
1194
+ * - list versioning settings
1195
+ * - list title/description
1196
+ * - content approval settings
1197
+ * does not track:
1198
+ * - Changes to views
1199
+ * - changing list/items permissions
1200
+ */
1201
+ export function GetListLastItemModifiedDate(siteUrl: string, listIdOrTitle: string, options?: {
1202
+ sync?: false;
1203
+ refreshCache?: boolean;
1204
+ /** ignore system changes */
1205
+ userChangesOnly?: boolean;
1206
+ }): Promise<string>;
1207
+
1208
+ export function GetListLastItemModifiedDate(siteUrl: string, listIdOrTitle: string, options?: {
1209
+ sync?: boolean;
1210
+ refreshCache?: boolean;
1211
+ /** ignore system changes */
1212
+ userChangesOnly?: boolean;
1213
+ }): string | Promise<string> {
1214
+ siteUrl = GetSiteUrl(siteUrl);
1215
+
1216
+ let endPoint = options && options.userChangesOnly ? 'LastItemUserModifiedDate' : 'LastItemModifiedDate';
1217
+
1218
+ let sync = options && options.sync ? true : false;
1219
+ let caller = sync ? GetJsonSync : GetJson;
1220
+
1221
+ let result = caller<{ value: string; }>(GetListRestUrl(siteUrl, listIdOrTitle) + `/${endPoint}`, null, {
1222
+ allowCache: true,//in memory only
1223
+ jsonMetadata: jsonTypes.nometadata,
1224
+ forceCacheUpdate: options && options.refreshCache === true || false
1225
+ });
1226
+
1227
+ if (isPromise(result))
1228
+ return result.then(r => r.value, () => null);
1229
+ else
1230
+ return result.success ? result.result.value : null;
1231
+ }
1232
+
1233
+ export async function ReloadListLastModified(siteUrl: string, listIdOrTitle: string) {
1234
+ await GetListLastItemModifiedDate(siteUrl, listIdOrTitle, { refreshCache: true });
1235
+ //make sure we do it for both title and id, we don't know how the other callers may use this in their API
1236
+
1237
+ if (!isValidGuid(listIdOrTitle)) {
1238
+ try {
1239
+ var listId = GetListId(siteUrl, listIdOrTitle);
1240
+ await GetListLastItemModifiedDate(siteUrl, listId, { refreshCache: true });
1241
+ } catch (e) { }
1242
+ }
1243
+ else {
1244
+ try {
1245
+ var listTitle = await GetListTitle(siteUrl, listIdOrTitle);
1246
+ await GetListLastItemModifiedDate(siteUrl, listTitle, { refreshCache: true });
1247
+ } catch (e) { }
1248
+ }
1249
+ }
1250
+
1251
+ export async function ListHasUniquePermissions(siteUrl: string, listIdOrTitle: string): Promise<boolean> {
1252
+ let url = `${GetListRestUrl(siteUrl, listIdOrTitle)}/?$select=hasuniqueroleassignments`;
1253
+ let has = await GetJson<{ HasUniqueRoleAssignments: boolean }>(url, undefined, { allowCache: false, jsonMetadata: jsonTypes.nometadata });
1254
+ return has.HasUniqueRoleAssignments === true;
1255
+ }
1256
+ export async function RestoreListPermissionInheritance(siteUrl: string, listIdOrTitle: string): Promise<void> {
1257
+ let url = `${GetListRestUrl(siteUrl, listIdOrTitle)}/ResetRoleInheritance`;
1258
+ await GetJson(url, undefined, { method: "POST", allowCache: false, jsonMetadata: jsonTypes.nometadata, spWebUrl: siteUrl });
1259
+ }
1260
+ export async function BreakListPermissionInheritance(siteUrl: string, listIdOrTitle: string, clear = true): Promise<void> {
1261
+ let url = `${GetListRestUrl(siteUrl, listIdOrTitle)}/breakroleinheritance(copyRoleAssignments=${clear ? 'false' : 'true'}, clearSubscopes=true)`;
1262
+ await GetJson(url, undefined, { method: "POST", allowCache: false, jsonMetadata: jsonTypes.nometadata, spWebUrl: siteUrl });
1263
+ }
1264
+ export async function AssignListPermission(siteUrl: string, listIdOrTitle: string, principalId: number, roleId: number) {
1265
+ let url = `${GetListRestUrl(siteUrl, listIdOrTitle)}/roleassignments/addroleassignment(principalid=${principalId},roleDefId=${roleId})`;
1266
+ await GetJson(url, undefined, { method: "POST", allowCache: false, jsonMetadata: jsonTypes.nometadata, spWebUrl: siteUrl });
1267
+ }
1268
+ export async function RemoveListPermission(siteUrl: string, listIdOrTitle: string, principalId: number, roleId: number) {
1269
+ let url = `${GetListRestUrl(siteUrl, listIdOrTitle)}/roleassignments/removeroleassignment(principalid=${principalId},roleDefId=${roleId})`;
1270
+ await GetJson(url, undefined, { method: "POST", allowCache: false, jsonMetadata: jsonTypes.nometadata, spWebUrl: siteUrl });
1271
+ }
1272
+
1273
+ interface iCreateListResult {
1274
+ AllowContentTypes: boolean,
1275
+ BaseTemplate: ListTemplateTypes,
1276
+ BaseType: BaseTypes,
1277
+ ContentTypesEnabled: boolean,
1278
+ Created: string,
1279
+ DefaultItemOpenUseListSetting: boolean,
1280
+ Description: string,
1281
+ DisableCommenting: boolean,
1282
+ DisableGridEditing: boolean,
1283
+ DocumentTemplateUrl: string,//"/sites/s/cms/CMSLayouts/Forms/template.dotx",
1284
+ DraftVersionVisibility: 0,
1285
+ EnableAttachments: boolean,
1286
+ EnableFolderCreation: boolean,
1287
+ EnableMinorVersions: boolean,
1288
+ EnableModeration: false,
1289
+ EnableRequestSignOff: boolean,
1290
+ EnableVersioning: boolean,
1291
+ EntityTypeName: string,//"CMSLayouts",
1292
+ ExemptFromBlockDownloadOfNonViewableFiles: boolean,
1293
+ FileSavePostProcessingEnabled: boolean,
1294
+ ForceCheckout: boolean,
1295
+ HasExternalDataSource: boolean,
1296
+ Hidden: boolean,
1297
+ Id: string,//"c21d4eb4-70cc-4c95-925a-aa34bb9e01e0",
1298
+ ImagePath: {
1299
+ DecodedUrl: string,//"/_layouts/15/images/itdl.png?rev=47"
1300
+ },
1301
+ ImageUrl: string,//"/_layouts/15/images/itdl.png?rev=47",
1302
+ IsApplicationList: boolean,
1303
+ IsCatalog: boolean,
1304
+ IsPrivate: boolean,
1305
+ ItemCount: 0,
1306
+ LastItemDeletedDate: string,//"2024-02-05T18:26:05Z",
1307
+ LastItemModifiedDate: string,//"2024-02-05T18:26:06Z",
1308
+ LastItemUserModifiedDate: string,//"2024-02-05T18:26:05Z",
1309
+ ListExperienceOptions: ListExperienceOptions,
1310
+ ListItemEntityTypeFullName: string,//"SP.Data.CMSLayoutsItem",
1311
+ MajorVersionLimit: number,//500,
1312
+ MajorWithMinorVersionsLimit: number,//0,
1313
+ MultipleDataList: boolean,
1314
+ NoCrawl: boolean,
1315
+ ParentWebPath: {
1316
+ DecodedUrl: string,//"/sites/s/cms"
1317
+ },
1318
+ ParentWebUrl: string,//"/sites/s/cms",
1319
+ ParserDisabled: boolean,
1320
+ ServerTemplateCanCreateFolders: boolean,
1321
+ TemplateFeatureId: string,//"00bfea71-e717-4e80-aa17-d0c71b360101",
1322
+ Title: string,//"CMSLayouts"
1323
+ }
1324
+ export async function CreateList(siteUrl: string, info: {
1325
+ title: string; description: string;
1326
+ type: BaseTypes; template: ListTemplateTypes;
1327
+ }): Promise<iCreateListResult> {
1328
+ let url = `${GetRestBaseUrl(siteUrl)}/web/lists`;
1329
+ const body = {
1330
+ __metadata: { type: 'SP.List' },
1331
+ AllowContentTypes: false,
1332
+ ContentTypesEnabled: false,
1333
+ BaseTemplate: info.template,
1334
+ BaseType: info.type,
1335
+ Description: info.description,
1336
+ Title: info.title
1337
+ };
1338
+
1339
+ let newList = (await GetJson<{ d: iCreateListResult }>(url, jsonStringify(body))).d;
1340
+ normalizeGuid(newList.Id);
1341
+ return newList;
1342
+ }
1343
+
1344
+ export async function SearchList(siteUrl: string, listIdOrTitle: string, query: string) {
1345
+ let listId = GetListId(siteUrl, listIdOrTitle);
1346
+ let url = `${GetRestBaseUrl(siteUrl)}/search/query?querytext='(${query}*)'&querytemplate='{searchTerms} (NormListID:${listId})'`;
1347
+
1348
+ try {
1349
+ const result = await GetJson<{
1350
+ ElapsedTime: number,
1351
+ PrimaryQueryResult: {
1352
+ CustomResults: [];
1353
+ QueryId: string;//"7fdf01b1-f6f0-4d42-b046-d9db22597084",
1354
+ QueryRuleId: string;// "00000000-0000-0000-0000-000000000000",
1355
+ RefinementResults: null,
1356
+ RelevantResults: {
1357
+ RowCount: number,
1358
+ Table: {
1359
+ Rows: {
1360
+ Cells: {
1361
+ Key:
1362
+ /** "1989637621861439888" "Edm.Int64" */
1363
+ "WorkId"
1364
+ /** "1000.1073372","Edm.Double" */
1365
+ | "Rank"
1366
+ /** "sample md as text","Edm.String" */
1367
+ | "Title"
1368
+ /** "Shai Petel", "Edm.String" */
1369
+ | "Author"
1370
+ /** "91", "Edm.Int64" */
1371
+ | "Size"
1372
+ /** "https://kwizcom.sharepoint.com/sites/s/cms/CMSPages/sample md as text.txt", "Edm.String" */
1373
+ | "Path"
1374
+ /** null, "Null" */
1375
+ | "Description"
1376
+ /** "# hello world! - bullet - bullet | table | col | | ----- | ---- | |table |col | ", "Edm.String" */
1377
+ | "HitHighlightedSummary"
1378
+ /** "https://kwizcom.sharepoint.com/_api/v2.1/drives/b!8NAeO-mocUWbgyMTqcM0Mfh8XKPhn7xOhhMrO5KfJjBs_gXb9j8ZRaLxuppgj0Uk/items/01OBXW4FLU6G4LIT7AX5BK3MXHDICVTIOT/thumbnails/0/c400x99999/content?prefer=noRedirect", "Edm.String" */
1379
+ | "PictureThumbnailURL"
1380
+ /** null,"Null" */
1381
+ | "ServerRedirectedURL"
1382
+ /** null,"Null" */
1383
+ | "ServerRedirectedEmbedURL"
1384
+ /** null,"Null" */
1385
+ | "ServerRedirectedPreviewURL"
1386
+ /** "txt","Edm.String" */
1387
+ | "FileExtension"
1388
+ /** "0x010100CB212272F1372446A2423F0A2BEA12B8", "Edm.String" */
1389
+ | "ContentTypeId"
1390
+ /** "https://kwizcom.sharepoint.com/sites/s/cms/CMSPages/Forms/AllItems.aspx","Edm.String" */
1391
+ | "ParentLink"
1392
+ /** "1","Edm.Int64" */
1393
+ | "ViewsLifeTime"
1394
+ /** "1","Edm.Int64" */
1395
+ | "ViewsRecent"
1396
+ /** "2024-02-22T18:35:48.0000000Z","Edm.DateTime" */
1397
+ | "LastModifiedTime"
1398
+ /** "txt","Edm.String" */
1399
+ | "FileType"
1400
+ /** "1989637621861439888","Edm.Int64" */
1401
+ | "DocId"
1402
+ /** "https://kwizcom.sharepoint.com/sites/s/cms","Edm.String" */
1403
+ | "SPWebUrl"
1404
+ /** "{b4b8f174-e04f-42bf-adb2-e71a0559a1d3}","Edm.String" */
1405
+ | "UniqueId"
1406
+ /** "3b1ed0f0-a8e9-4571-9b83-2313a9c33431","Edm.String" */
1407
+ | "SiteId"
1408
+ /** "a35c7cf8-9fe1-4ebc-8613-2b3b929f2630","Edm.String" */
1409
+ | "WebId"
1410
+ /** "db05fe6c-3ff6-4519-a2f1-ba9a608f4524","Edm.String" */
1411
+ | "ListId"
1412
+ /** "https://kwizcom.sharepoint.com/sites/s/cms/CMSPages/sample md as text.txt","Edm.String" */
1413
+ | "OriginalPath"
1414
+ ;
1415
+ Value: string,
1416
+ ValueType: "Edm.Int64" | "Edm.Double" | "Edm.String" | "Edm.DateTime" | "Null"
1417
+ }[]
1418
+ }[]
1419
+ },
1420
+ TotalRows: number,
1421
+ TotalRowsIncludingDuplicates: number
1422
+ }
1423
+ },
1424
+ }>(url, null, { jsonMetadata: jsonTypes.nometadata });
1425
+ logger.json(result.PrimaryQueryResult.RelevantResults, `search ${query}`);
1426
+ let rows = result.PrimaryQueryResult.RelevantResults.Table.Rows;
1427
+
1428
+ const mapped: (IDictionary<string | Date | number> & {
1429
+ WorkId?: number;
1430
+ Rank?: number;
1431
+ Title?: string;
1432
+ Author?: string;
1433
+ Size?: number;
1434
+ Path?: string;
1435
+ Description?: string;
1436
+ HitHighlightedSummary?: string;
1437
+ PictureThumbnailURL?: string;
1438
+ ServerRedirectedURL?: string;
1439
+ ServerRedirectedEmbedURL?: string;
1440
+ ServerRedirectedPreviewURL?: string;
1441
+ FileExtension?: string;
1442
+ ContentTypeId?: string;
1443
+ ParentLink?: string;
1444
+ ViewsLifeTime?: number;
1445
+ ViewsRecent?: number;
1446
+ LastModifiedTime?: Date;
1447
+ FileType?: string;
1448
+ DocId?: number;
1449
+ SPWebUrl?: string;
1450
+ UniqueId?: string;
1451
+ SiteId?: string;
1452
+ WebId?: string;
1453
+ ListId?: string;
1454
+ OriginalPath?: string;
1455
+ $itemId?: number;
1456
+ })[] = [];
1457
+ rows.forEach(r => {
1458
+ try {
1459
+ const rowValues: IDictionary<string | Date | number> = {};
1460
+ r.Cells.forEach(cell => {
1461
+ rowValues[cell.Key] = cell.ValueType === "Edm.Int64" || cell.ValueType === "Edm.Double"
1462
+ ? parseInt(cell.Value, 10)
1463
+ : cell.ValueType === "Edm.DateTime"
1464
+ ? new Date(cell.Value)
1465
+ : cell.ValueType === "Null"
1466
+ ? ""
1467
+ : cell.Value
1468
+ });
1469
+ let resultPath = isNullOrEmptyString(rowValues.Path) ? "" : (rowValues.Path as string).toLowerCase();
1470
+ let indexOfId = resultPath.toLowerCase().indexOf("id=");
1471
+ let itemId = indexOfId >= 0 ? parseInt(resultPath.slice(indexOfId + 3)) : -1;
1472
+ if (itemId >= 0)
1473
+ rowValues.$itemId = itemId;
1474
+ mapped.push(rowValues);
1475
+ } catch (e) { return null; }
1476
+ });
1477
+
1478
+ return mapped;
1479
+ } catch (e) {
1480
+ logger.error(e);
1481
+ }
1482
+
1483
+ return [];
1484
+ }
1485
+
1486
+ export async function UpdateListExperience(siteUrl: string, listId: string, experience: ListExperienceOptions) {
1487
+ try {
1488
+ let url = GetListRestUrl(siteUrl, listId);
1489
+ let data = {
1490
+ "ListExperienceOptions": experience
1491
+ };
1492
+ let result = await GetJson(url, JSON.stringify(data), {
1493
+ xHttpMethod: "MERGE",
1494
+ jsonMetadata: jsonTypes.nometadata
1495
+ });
1496
+ return isNullOrEmptyString(result);
1497
+ } catch (e) {
1498
+ logger.error(e);
1499
+ }
1500
+ return false;
1501
+ }
1502
+
1503
+ export async function GetListVersionSettings(siteUrlOrId: string, listIdOrTitle: string, options?: { refreshCache?: boolean }): Promise<iListVersionSettings> {
1504
+ let siteUrl = GetSiteUrl(siteUrlOrId);
1505
+
1506
+ if (isNullOrEmptyString(listIdOrTitle)) return null;
1507
+
1508
+ try {
1509
+ const result = await GetJson<iListVersionSettings>(GetListRestUrl(siteUrl, listIdOrTitle) + `?$select=EnableMinorVersions,EnableVersioning,DraftVersionVisibility,MajorWithMinorVersionsLimit,MajorVersionLimit,EnableModeration`, null, {
1510
+ allowCache: options && options.refreshCache ? false : true,
1511
+ jsonMetadata: jsonTypes.nometadata
1512
+ });
1513
+ return result;
1514
+ } catch {
1515
+ const result_1: iListVersionSettings = null;
1516
+ return result_1;
1517
+ }
1518
+ }
1519
+ export async function SetListVersionSettings(siteUrlOrId: string, listIdOrTitle: string, options: {
1520
+ newSettings: Pick<iListVersionSettings, "EnableMinorVersions" | "EnableModeration" | "DraftVersionVisibility">
1521
+ }): Promise<iListVersionSettings> {
1522
+ let siteUrl = GetSiteUrl(siteUrlOrId);
1523
+
1524
+ if (isNullOrEmptyString(listIdOrTitle)) return null;
1525
+
1526
+ try {
1527
+ function updateProp(prop: Partial<iListVersionSettings>) {
1528
+ return GetJson<iListVersionSettings>(GetListRestUrl(siteUrl, listIdOrTitle),
1529
+ jsonStringify(prop), {
1530
+ method: "POST", spWebUrl: siteUrl,
1531
+ xHttpMethod: "MERGE",
1532
+ jsonMetadata: jsonTypes.nometadata
1533
+ });
1534
+ }
1535
+
1536
+ const currentValues = await GetListVersionSettings(siteUrlOrId, listIdOrTitle, { refreshCache: true });
1537
+ //replace undefined props with current values
1538
+ const newSettings: iListVersionSettings = { ...currentValues, ...options.newSettings }
1539
+
1540
+
1541
+ //need to do some of the changes one by one...
1542
+ if (newSettings.EnableMinorVersions) {
1543
+ if (!currentValues.EnableMinorVersions)
1544
+ await updateProp({ EnableMinorVersions: true });
1545
+
1546
+ if (!isNullOrUndefined(newSettings.DraftVersionVisibility)) {
1547
+ if (currentValues.DraftVersionVisibility !== newSettings.DraftVersionVisibility)
1548
+ await updateProp({ DraftVersionVisibility: newSettings.DraftVersionVisibility });
1549
+ }
1550
+ }
1551
+ else {
1552
+ if (currentValues.EnableMinorVersions) {
1553
+ await updateProp({ EnableMinorVersions: false });
1554
+ }
1555
+ }
1556
+
1557
+ if (newSettings.EnableMinorVersions && newSettings.EnableModeration) {
1558
+ if (!currentValues.EnableModeration)
1559
+ await updateProp({ EnableModeration: true });
1560
+ }
1561
+ else {
1562
+ if (currentValues.EnableModeration) {
1563
+ await updateProp({ EnableModeration: false });
1564
+ }
1565
+ }
1566
+
1567
+ return await GetListVersionSettings(siteUrlOrId, listIdOrTitle, { refreshCache: true });
1568
+ } catch {
1569
+ const result: iListVersionSettings = null;
1570
+ return result;
1571
+ }
1572
+ }