@kwiz/common
Version:
KWIZ common utilities and helpers for M365 platform
1,165 lines • 52.4 kB
JavaScript
import { isISODate, promiseLock } from "../../exports-index";
import { sortArray } from "../../helpers/collections.base";
import { jsonStringify } from "../../helpers/json";
import { getGlobal } from "../../helpers/objects";
import { SPBasePermissions, extendFieldInfo } from "../../helpers/sharepoint";
import { normalizeGuid } from "../../helpers/strings";
import { isDate, isNotEmptyArray, isNullOrEmptyArray, isNullOrEmptyString, isNullOrNaN, isNullOrUndefined, isNumeric, isString, isTypeofFullNameNullOrUndefined, isValidGuid } from "../../helpers/typecheckers";
import { makeFullUrl, makeServerRelativeUrl, normalizeUrl } from "../../helpers/url";
import { jsonTypes } from "../../types/rest.types";
import { SPBasePermissionKind } from "../../types/sharepoint.types";
import { WebTypes } from "../../types/sharepoint.utils.types";
import { AutoDiscoverTenantInfo } from "../auth/discovery";
import { ConsoleLogger } from "../consolelogger";
import { toIsoDateFormat } from "../date";
import { GetJson, GetJsonSync, extraLongLocalCache, longLocalCache, mediumLocalCache, noLocalCache, shortLocalCache, weeekLongLocalCache } from "../rest";
import { CONTENT_TYPES_SELECT, CONTENT_TYPES_SELECT_WITH_FIELDS, GetRestBaseUrl, GetSiteUrl, LIST_EXPAND, LIST_SELECT, WEB_SELECT, hasGlobalContext } from "./common";
import { GetListFields, GetListFieldsSync, GetListRestUrl } from "./list";
import { SPTimeZoneIdToIANATimeZoneName } from "./timzone-map";
const logger = ConsoleLogger.get("SharePoint.Rest.Web");
export async function GetSiteInfo(siteUrl) {
siteUrl = GetSiteUrl(siteUrl);
try {
const r = await GetJson(GetRestBaseUrl(siteUrl) + "/site?$select=id,serverRelativeUrl", null, { ...longLocalCache });
var id = normalizeGuid(r.d.Id);
var serverRelativeUrl = normalizeUrl(r.d.ServerRelativeUrl);
if (isNullOrEmptyString(serverRelativeUrl))
serverRelativeUrl = "/"; //can't return "" since it will be treated as current sub site, when tyring to access the root site from a sub-site
return { Id: id, ServerRelativeUrl: serverRelativeUrl };
}
catch {
return null;
}
}
export function GetSiteInfoSync(siteUrl) {
siteUrl = GetSiteUrl(siteUrl);
let result = GetJsonSync(GetRestBaseUrl(siteUrl) + "/site?$select=id,serverRelativeUrl", null, { ...longLocalCache });
if (result.success) {
var id = normalizeGuid(result.result.d.Id);
var serverRelativeUrl = normalizeUrl(result.result.d.ServerRelativeUrl);
return { Id: id, ServerRelativeUrl: serverRelativeUrl };
}
return null;
}
function _getSiteIdFromContext(siteUrl) {
if (hasGlobalContext()) {
//issue 7295
//make sure we return false for /sites/ab/c is not under /sites/a by adding a / at the end
let normalizedWebUrl = normalizeUrl(makeServerRelativeUrl(siteUrl), true).toLowerCase();
let normalizedCurrentSiteUrl = normalizeUrl(_spPageContextInfo.siteServerRelativeUrl, true).toLowerCase();
//test cases
//if (!testSub("/", "/hello")) console.error("1");
//if (testSub("/", "/sites/hello")) console.error("2");
//if (testSub("/sites/a", "/sites/b")) console.error("3");
//if (!testSub("/sites/a", "/sites/a/b")) console.error("4");
//if (!testSub("/", "/")) console.error("5");
//if (!testSub("/sites/a", "/sites/a")) console.error("6");
//if (testSub("/sites/a", "/hello")) console.error("7");
if (isNullOrUndefined(siteUrl)
|| normalizedCurrentSiteUrl === "/" && !normalizedWebUrl.startsWith("/sites")
|| normalizedCurrentSiteUrl !== "/" && normalizedWebUrl.startsWith(normalizedCurrentSiteUrl)) {
if (!isNullOrEmptyString(_spPageContextInfo.siteId)) {
return normalizeGuid(_spPageContextInfo.siteId);
}
}
}
return null;
}
/** Get tenant id lower case no {} */
export function GetTenantId() {
if (!isTypeofFullNameNullOrUndefined("_spPageContextInfo")) {
return normalizeGuid(_spPageContextInfo.aadTenantId);
}
let info = AutoDiscoverTenantInfo(true);
if (!isNullOrUndefined(info) && isValidGuid(info.idOrName)) {
return normalizeGuid(info.idOrName);
}
return null;
}
/** Get tenant id lower case no {} */
export function GetPortalUrl() {
return _spPageContextInfo.portalUrl;
}
/** Get site id lower case no {} */
export async function GetSiteId(siteUrl) {
let siteId = _getSiteIdFromContext(siteUrl);
if (!isNullOrEmptyString(siteId)) {
return siteId;
}
return GetSiteInfo(siteUrl).then((info) => {
if (!isNullOrUndefined(info) && !isNullOrEmptyString(info.Id)) {
return normalizeGuid(info.Id);
}
return null;
}).catch(() => {
return null;
});
}
/** Get site id lower case no {} */
export function GetSiteIdSync(siteUrl) {
let siteId = _getSiteIdFromContext(siteUrl);
if (!isNullOrEmptyString(siteId)) {
return siteId;
}
let result = GetSiteInfoSync(siteUrl);
return !isNullOrUndefined(result) ? normalizeGuid(result.Id) : null;
}
/** Get root web id lower case no {} */
export function GetRootWebInfo(siteUrl) {
siteUrl = GetSiteUrl(siteUrl);
return GetJson(GetRestBaseUrl(siteUrl) + "/site/rootWeb?$select=id,serverRelativeUrl", null, { ...longLocalCache })
.then(r => {
var id = normalizeGuid(r.d.Id);
var serverRelativeUrl = normalizeUrl(r.d.ServerRelativeUrl);
//console.log("site id: " + id);
return { Id: id, ServerRelativeUrl: serverRelativeUrl };
})
.catch(() => null);
}
/** Return the web Title */
export function GetWebTitle(siteUrl) {
siteUrl = GetSiteUrl(siteUrl);
return GetJson(GetRestBaseUrl(siteUrl) + `/web/Title`, null, { ...shortLocalCache })
.then(r => {
return r.d.Title;
})
.catch(() => null);
}
function _getWebIdRequestUrl(siteUrl) {
return `${GetRestBaseUrl(siteUrl)}/web/Id`;
}
/** Return the web id */
export function GetWebId(siteUrl) {
return GetJson(_getWebIdRequestUrl(siteUrl), null, { ...longLocalCache })
.then(r => {
return normalizeGuid(r.d.Id);
})
.catch(() => null);
}
/** Return the web id */
export function GetWebIdSync(siteUrl) {
let syncResult = GetJsonSync(_getWebIdRequestUrl(siteUrl), null, { ...longLocalCache });
if (syncResult.success)
return syncResult.result.d.Id;
else
return null;
}
/** Return the web id */
export async function IsRootWeb(siteUrl) {
siteUrl = GetSiteUrl(siteUrl);
let webId = await GetWebId(siteUrl);
let rootWeb = await GetRootWebInfo(siteUrl);
return webId === rootWeb.Id;
}
export function UserHasAllPermissions(siteUrl, permissions) {
siteUrl = GetSiteUrl(siteUrl);
return GetJson(GetRestBaseUrl(siteUrl) + `/web/EffectiveBasePermissions`, null, { ...shortLocalCache })
.then(r => {
var effectivePermissions = new SPBasePermissions(r.d.EffectiveBasePermissions);
return permissions.every((perm) => {
return effectivePermissions.has(perm);
});
})
.catch(() => null);
}
export function UserHasManageSitePermissions(siteUrl) {
siteUrl = GetSiteUrl(siteUrl);
if (!isTypeofFullNameNullOrUndefined("_spPageContextInfo")) {
if (siteUrl.startsWith(_spPageContextInfo.siteServerRelativeUrl))
if (_spPageContextInfo.isSiteAdmin || _spPageContextInfo["isSiteOwner"])
return Promise.resolve(true);
}
return GetJson(GetRestBaseUrl(siteUrl) + `/web/EffectiveBasePermissions`, null, { ...shortLocalCache })
.then(r => {
return new SPBasePermissions(r.d.EffectiveBasePermissions).has(SPBasePermissionKind.ManageWeb);
})
.catch(() => null);
}
function _getContentTypesRequestUrl(siteUrl, options = {}) {
const { fromRootWeb, includeFields, listIdOrTitle } = options;
let query = `$select=${includeFields === true ? CONTENT_TYPES_SELECT : CONTENT_TYPES_SELECT_WITH_FIELDS}${includeFields === true ? "&$expand=Fields" : ""}`;
if (!isNullOrEmptyString(listIdOrTitle)) {
return `${GetListRestUrl(siteUrl, listIdOrTitle)}/contenttypes?${query}`;
}
else if (fromRootWeb) {
return `${GetRestBaseUrl(siteUrl)}/site/rootweb/contenttypes?${query}`;
}
else {
return `${GetRestBaseUrl(siteUrl)}/web/contenttypes?${query}`;
}
}
function _postProcessGetContentTypes(contentTypes, options = {}, allListFields) {
const { ignoreHidden, ignoreFolders, includeFields } = options;
if (!isNullOrEmptyArray(contentTypes)) {
if (ignoreFolders === true || ignoreHidden === true) {
contentTypes = contentTypes.filter(rr => {
if (options.ignoreFolders && rr.StringId.startsWith('0x0120'))
return false;
if (options.ignoreHidden && rr.Hidden)
return false;
return true;
});
}
if (includeFields === true) {
contentTypes.forEach((result) => {
if (!isNullOrEmptyArray(result.Fields)) {
result.Fields = result.Fields.map((field) => {
return extendFieldInfo(field, allListFields || result.Fields);
});
}
});
}
return contentTypes;
}
return null;
}
export async function GetContentTypes(siteUrl, options = {}, refreshCache = false) {
let url = _getContentTypesRequestUrl(siteUrl, options);
let allListFields = null;
if (options.includeFields) {
allListFields = await GetListFields(siteUrl, options.listIdOrTitle);
}
return GetJson(url, null, { allowCache: refreshCache !== true, jsonMetadata: jsonTypes.nometadata })
.then(result => {
if (!isNullOrUndefined(result)) {
return _postProcessGetContentTypes(result.value, options, allListFields);
}
return null;
})
.catch(() => null);
}
export function GetContentTypesSync(siteUrl, options = {}, refreshCache = false) {
let url = _getContentTypesRequestUrl(siteUrl, options);
let allListFields = null;
if (options.includeFields) {
allListFields = GetListFieldsSync(siteUrl, options.listIdOrTitle);
}
let result = GetJsonSync(url, null, { allowCache: refreshCache !== true, jsonMetadata: jsonTypes.nometadata });
if (!isNullOrUndefined(result) && result.success === true && !isNullOrUndefined(result.result)) {
return _postProcessGetContentTypes(result.result.value, options, allListFields);
}
return null;
}
function _getListsRequestUrl(siteUrl, options) {
let select = LIST_SELECT;
let expand = LIST_EXPAND;
if (options.includeRootFolders === true) {
select += ",RootFolder/Name,RootFolder/ServerRelativeUrl";
expand += ",RootFolder";
}
if (options.includeViews === true) {
expand += ",Views";
}
return GetRestBaseUrl(siteUrl) + `/web/lists?$select=${select}&$expand=${expand}`;
}
function _postProcessGetLists(lists, options = {}) {
lists = lists || [];
if (options && options.includeViews) {
lists.forEach(l => {
if (isNullOrEmptyArray(l.Views)) {
l.Views = [];
}
l.Views.forEach(v => { v.Id = normalizeGuid(v.Id); });
});
}
lists.forEach((list) => {
if (list.EffectiveBasePermissions
&& (isString(list.EffectiveBasePermissions.High)
|| isString(list.EffectiveBasePermissions.Low))) {
list.EffectiveBasePermissions = {
High: Number(list.EffectiveBasePermissions.High),
Low: Number(list.EffectiveBasePermissions.Low)
};
}
});
return lists;
}
export function GetLists(siteUrl, options = {}) {
let url = _getListsRequestUrl(siteUrl, options);
return GetJson(url, null, { allowCache: true, jsonMetadata: jsonTypes.nometadata })
.then(result => {
return _postProcessGetLists(result.value, options);
})
.catch(() => []);
}
export function GetListsSync(siteUrl, options = {}) {
let url = _getListsRequestUrl(siteUrl, options);
let response = GetJsonSync(url, null, { ...shortLocalCache, jsonMetadata: jsonTypes.nometadata });
if (response && response.success && response.result && isNotEmptyArray(response.result.value)) {
return _postProcessGetLists(response.result.value, options);
}
return [];
}
/**
* Get all sub webs. Results will be cached in memory and sorted
* @param siteUrl the starting URL you want to get the sites for
* @param allowAppWebs send true if you would like to inlucde app webs as well
*/
export async function GetAllSubWebs(siteUrl, options) {
siteUrl = GetSiteUrl(siteUrl);
let sites = [];
options = options || {};
var currentSite;
var queryFailed = false;
try {
currentSite = await GetWebInfo(siteUrl);
let queryFilter = '';
if (!options.allSiteCollections) {
//filter by site id
let siteId = await GetSiteId(siteUrl);
queryFilter = `SiteId:${siteId}`;
}
//Issue 6735 missing WebId for some customer (US, government GCC tenant will not return WebId)
let queryUrl = `${GetRestBaseUrl(siteUrl)}/search/query?querytext=%27${queryFilter}(contentclass:STS_Site)%20(contentclass:STS_Web)%27&trimduplicates=false&rowlimit=5000&selectproperties=%27Title,Url,WebTemplate,WebId%27`;
let response = await GetJson(queryUrl, null, { ...shortLocalCache });
let results = response && response.d && response.d.query && response.d.query.PrimaryQueryResult;
let addedSites = [];
if (results && results.RelevantResults.RowCount >= 0) {
let allPropsFound = false;
results.RelevantResults.Table.Rows.results.forEach(row => {
let Title = null;
let Url = null;
let WebId = null;
let WebTemplate = null;
let skip = false;
for (var i = 0; i < row.Cells.results.length; i++) {
let cell = row.Cells.results[i];
let value = isNullOrEmptyString(cell.Value) ? "" : cell.Value;
switch (cell.Key) {
case "WebTemplate":
WebTemplate = value;
if (!options.allowAppWebs && value === "APP")
skip = true;
break;
case "Title":
Title = value;
break;
case "WebId":
WebId = normalizeGuid(value);
break;
case "Url":
if (addedSites.indexOf(value.toLowerCase()) >= 0) {
//duplicate, skip
skip = true;
}
else {
Url = value;
}
break;
}
if (skip)
break; //stop the cells loop
allPropsFound =
Title !== null &&
Url !== null &&
WebId !== null &&
WebTemplate !== null;
if (allPropsFound)
break;
}
if (!skip && allPropsFound) //don't skip, and we found all needed props
{
sites.push({
Title: Title,
ServerRelativeUrl: makeServerRelativeUrl(Url),
WebId: WebId,
WebTemplate: WebTemplate,
WebType: WebTemplate === "APP" ? WebTypes.App :
WebTemplate === "GROUP" ? WebTypes.Group :
WebTemplate === "STS" ? WebTypes.Team :
WebTypes.Other
});
}
});
}
//Issue 7161
if (sites.length === 1 || (!isNullOrUndefined(currentSite) && !sites.filter((site) => {
return site.WebId !== currentSite.WebId;
})[0])) {
queryFailed = true;
}
}
catch (e) {
queryFailed = true;
}
if (queryFailed) {
// Igor: Issue #7702
if (_spPageContextInfo && _spPageContextInfo.siteServerRelativeUrl.toLowerCase() !== siteUrl.toLowerCase()) {
//siteUrl = _spPageContextInfo.siteServerRelativeUrl;
//currentSite = await GetWebInfo(siteUrl);
//Kevin: Issue 1028
//The user may not have permission to the site collection root web. Instead of overwirting the currentSite/siteUrl,
//we make a request for the site collection root web. If we get a valid response, replace currentSite/siteUrl with
//the site collection root web info.
let currentSiteCollection = await GetWebInfo(_spPageContextInfo.siteServerRelativeUrl);
if (currentSiteCollection && !isNullOrEmptyString(currentSiteCollection.ServerRelativeUrl)) {
currentSite = currentSiteCollection;
siteUrl = _spPageContextInfo.siteServerRelativeUrl;
}
}
//add current site
if (currentSite && (options.allowAppWebs || currentSite.WebType !== WebTypes.App)) {
sites.push(currentSite);
}
//Issue 6651
//add sub sites
//if the query failed, we can't rely on search to get the subwebs
var currentSiteSubSites = await __getSubSites(siteUrl, options.allowAppWebs);
if (isNotEmptyArray(currentSiteSubSites)) {
sites = [...sites, ...currentSiteSubSites];
}
}
var webIds = [];
var filteredSites = [];
for (let site of sites) {
if (webIds.indexOf(site.WebId) === -1) {
webIds.push(site.WebId);
filteredSites.push(site);
}
}
sortArray(filteredSites, s => s.ServerRelativeUrl);
return filteredSites;
}
export async function __getSubSites(siteUrl, allowAppWebs) {
siteUrl = GetSiteUrl(siteUrl);
let sites = [];
//try {
//maybe search is not wokring? use regular REST API
let restUrl = `${GetRestBaseUrl(siteUrl)}/web/getsubwebsfilteredforcurrentuser(nwebtemplatefilter=-1,nconfigurationfilter=0)?$Select=Title,ServerRelativeUrl,Id,WebTemplate`;
let result = await GetJson(restUrl, null, { ...shortLocalCache });
if (result && result.d && isNotEmptyArray(result.d.results)) {
let results = (allowAppWebs) ? result.d.results : result.d.results.filter(s => s.WebTemplate !== "APP");
let promises = [];
results.forEach(s => {
sites.push({
Title: s.Title,
ServerRelativeUrl: s.ServerRelativeUrl,
WebId: s.Id,
WebTemplate: s.WebTemplate,
WebType: s.WebTemplate === "APP" ? WebTypes.App :
s.WebTemplate === "GROUP" ? WebTypes.Group :
s.WebTemplate === "STS" ? WebTypes.Team :
WebTypes.Other
});
promises.push(__getSubSites(s.ServerRelativeUrl, allowAppWebs));
});
//loop and add all sub sites
let allSubs = await Promise.all(promises);
allSubs.forEach(subSubs => {
sites.push(...subSubs);
});
}
//}
//catch {
//}
return sites;
}
function _getWebInfoByIdRequestUrl(siteUrl, webId) {
return `${GetRestBaseUrl(siteUrl)}/site/openWebById('${webId}')?$Select=${WEB_SELECT}`;
}
function _getCurrentWebInfoRequestUrl(siteUrl) {
return `${GetRestBaseUrl(siteUrl)}/web?$Select=${WEB_SELECT}`;
}
function _postProcessGetWebInfo(webInfo) {
if (!isNullOrUndefined(webInfo)) {
return {
Title: webInfo.Title,
ServerRelativeUrl: webInfo.ServerRelativeUrl,
WebId: webInfo.Id,
WebTemplate: webInfo.WebTemplate,
WebType: GetWebType(webInfo.WebTemplate),
Description: webInfo.Description,
SiteLogoUrl: webInfo.SiteLogoUrl
};
}
return null;
}
export async function GetWebInfo(siteUrl, webId, refreshCache) {
let webInfoResponse = null;
try {
if (!isNullOrEmptyString(webId) && isValidGuid(webId)) {
webId = normalizeGuid(webId);
let currentWebId = await GetWebId(siteUrl);
if (currentWebId !== webId) {
let url = _getWebInfoByIdRequestUrl(siteUrl, webId);
webInfoResponse = await GetJson(url, null, {
method: "POST", spWebUrl: GetSiteUrl(siteUrl), ...shortLocalCache,
jsonMetadata: jsonTypes.nometadata,
allowCache: refreshCache !== true
});
}
}
if (isNullOrUndefined(webInfoResponse)) {
let url = _getCurrentWebInfoRequestUrl(siteUrl);
webInfoResponse = await GetJson(url, null, {
...shortLocalCache,
jsonMetadata: jsonTypes.nometadata,
allowCache: refreshCache !== true
});
}
}
catch (e) { }
return _postProcessGetWebInfo(webInfoResponse);
}
export function GetWebInfoSync(siteUrl, webId) {
let webInfoResponse = null;
if (!isNullOrEmptyString(webId) && isValidGuid(webId)) {
webId = normalizeGuid(webId);
let currentWebId = GetWebIdSync(siteUrl);
if (currentWebId !== webId) {
let url = _getWebInfoByIdRequestUrl(siteUrl, webId);
let syncResult = GetJsonSync(url, null, {
method: "POST", spWebUrl: GetSiteUrl(siteUrl), ...shortLocalCache,
jsonMetadata: jsonTypes.nometadata
});
if (syncResult.success) {
webInfoResponse = syncResult.result;
}
}
}
if (isNullOrUndefined(webInfoResponse)) {
let url = _getCurrentWebInfoRequestUrl(siteUrl);
let syncResult = GetJsonSync(url, null, {
...shortLocalCache,
jsonMetadata: jsonTypes.nometadata
});
if (syncResult.success) {
webInfoResponse = syncResult.result;
}
}
return _postProcessGetWebInfo(webInfoResponse);
}
export async function GetWebRoleDefinitions(siteUrl) {
return GetJson(GetRestBaseUrl(siteUrl) + `/web/RoleDefinitions?filter=Hidden ne true`, null, { ...longLocalCache })
.then(r => {
return r.d.results || [];
})
.catch(() => []);
}
;
/** get roles for site or list */
export async function GetRoleAssignments(siteUrl, listIdOrTitle, itemId) {
const url = `${isNullOrEmptyString(listIdOrTitle) ? GetRestBaseUrl(siteUrl) + "/web" : GetListRestUrl(siteUrl, listIdOrTitle)}/${isNullOrNaN(itemId) ? '' : `items(${itemId})/`}roleassignments?$expand=Member/users,RoleDefinitionBindings`;
const result = await GetJson(url, undefined, { jsonMetadata: jsonTypes.nometadata });
return result.value;
}
/** Web sub webs for the selected site */
export async function GetSubWebs(siteUrl, options) {
return GetJson(GetRestBaseUrl(siteUrl) + `/web/webs${options && options.allowAppWebs ? "" : "&$filter=WebTemplate ne 'APP'"}`, null, { ...shortLocalCache })
.then(r => {
return r.d.results;
})
.catch(() => []);
}
/** Web sub webs for the selected site */
export async function GetAppTiles(siteUrl) {
//Issue 933 this api does not work in a classic app web
if (hasGlobalContext() && _spPageContextInfo.isAppWeb) {
logger.warn('GetAppTiles does not work in an app web');
return null;
}
return GetJson(GetRestBaseUrl(siteUrl) + "/web/AppTiles?$filter=AppType%20eq%203&$select=Title,ProductId", null, { ...shortLocalCache, jsonMetadata: jsonTypes.nometadata })
.then(r => {
return isNotEmptyArray(r.value) ? r.value.map(t => {
return {
Title: t.Title,
ProductId: normalizeGuid(t.ProductId)
};
}) : [];
})
.catch(() => []);
}
/** Web sub webs for the selected site */
export function GetAppTilesSync(siteUrl) {
siteUrl = GetSiteUrl(siteUrl);
//Issue 933 this api does not work in a classic app web
if (hasGlobalContext() && _spPageContextInfo.isAppWeb) {
logger.warn('GetAppTiles does not work in an app web');
return null;
}
let r = GetJsonSync(GetRestBaseUrl(siteUrl) + "/web/AppTiles?$filter=AppType%20eq%203&$select=Title,ProductId", null, { ...shortLocalCache, jsonMetadata: jsonTypes.nometadata });
return r.success && r.result && isNotEmptyArray(r.result.value) ? r.result.value.map(t => {
return {
Title: t.Title,
ProductId: normalizeGuid(t.ProductId)
};
}) : [];
}
function GetWebType(WebTemplate) {
return WebTemplate === "APP" ? WebTypes.App :
WebTemplate === "GROUP" ? WebTypes.Group :
WebTemplate === "STS" ? WebTypes.Team :
WebTypes.Other;
}
export async function GetServerTimeZone(siteUrl) {
siteUrl = GetSiteUrl(siteUrl);
let getTimeZoneUrl = `${GetRestBaseUrl(siteUrl)}/web/regionalSettings/timeZone`;
let result = await GetJson(getTimeZoneUrl, null, { ...extraLongLocalCache });
if (result && result.d && !isNullOrUndefined(result.d)) {
return result.d;
}
else
return null;
}
export function GetServerTimeZoneSync(siteUrl) {
siteUrl = GetSiteUrl(siteUrl);
let getTimeZoneUrl = `${GetRestBaseUrl(siteUrl)}/web/regionalSettings/timeZone`;
let response = GetJsonSync(getTimeZoneUrl, null, { ...extraLongLocalCache });
if (response && response.result.d && !isNullOrUndefined(response.result.d)) {
return response.result.d;
}
else
return null;
}
/**
* to be used when parsing string date to date object in JavaScript like so:
* var clientTimezoneOffset = new Date().getTimezoneOffset() * 60 * 1000;
* var clientDate = new Date(value);
* var serverDate = new Date(clientDate.getTime() + clientTimezoneOffset + GetServerTimeOffset);
* We must send a date in, since places like Israel have different offset for specific dates (GMT+2 or GMT+3)
* or just call SPServerLocalTimeToUTCDate
*/
async function GetServerTimeOffset(siteUrl, date) {
siteUrl = GetSiteUrl(siteUrl);
let dateStr = toIsoDateFormat(date, { zeroTime: true, omitZ: true });
let inputDate = new Date(dateStr);
let getTimeZoneOffsetUrl = `${GetRestBaseUrl(siteUrl)}/web/regionalSettings/timeZone/localTimeToUTC(@date)?@date='${encodeURIComponent(dateStr)}'`;
let result = await GetJson(getTimeZoneOffsetUrl, null, { ...weeekLongLocalCache, jsonMetadata: jsonTypes.nometadata });
if (result && !isNullOrEmptyString(result.value)) {
let resultDate = new Date(result.value.slice(0, result.value.length - 1)); //remove Z and get as date.
return (resultDate.getTime() - inputDate.getTime());
}
else
return 0;
}
/**
* to be used when parsing string date to date object in JavaScript like so:
* var clientTimezoneOffset = new Date().getTimezoneOffset() * 60 * 1000;
* var clientDate = new Date(value);
* var serverDate = new Date(clientDate.getTime() + clientTimezoneOffset + GetServerTimeOffset);
* We must send a date in, since places like Israel have different offset for specific dates (GMT+2 or GMT+3)
* or just call SPServerLocalTimeToUTCDate
*/
function GetServerTimeOffsetSync(siteUrl, date) {
siteUrl = GetSiteUrl(siteUrl);
let dateStr = toIsoDateFormat(date, { zeroTime: true, omitZ: true });
let inputDate = new Date(dateStr);
let getTimeZoneOffsetUrl = `${GetRestBaseUrl(siteUrl)}/web/regionalSettings/timeZone/localTimeToUTC(@date)?@date='${encodeURIComponent(dateStr)}'`;
let result = GetJsonSync(getTimeZoneOffsetUrl, null, { ...weeekLongLocalCache, jsonMetadata: jsonTypes.nometadata });
if (result && result.success && !isNullOrEmptyString(result.result.value)) {
let resultDate = new Date(result.result.value.slice(0, result.result.value.length - 1)); //remove Z and get as date.
return (resultDate.getTime() - inputDate.getTime());
}
else
return 0;
}
/** get date yyyy:MM:ddTHH:mm:ss NO ZED, or a date object created in the server local time, and return a date object of the corrected UTC time */
export async function SPServerLocalTimeToUTCDate(siteUrl, date) {
//used in 7700
if (isNullOrEmptyString(date))
return null;
siteUrl = GetSiteUrl(siteUrl);
if (!isDate(date))
date = new Date(date);
let serverTimeOffset = await GetServerTimeOffset(siteUrl, date);
return _SPServerLocalTimeToUTCDate(date, serverTimeOffset);
}
/** get date yyyy:MM:ddTHH:mm:ss NO ZED, or a date object created in the server local time, and return a date object of the corrected UTC time */
export function SPServerLocalTimeToUTCDateSync(siteUrl, date) {
//used in 7700
if (isNullOrEmptyString(date))
return null;
siteUrl = GetSiteUrl(siteUrl);
if (!isDate(date))
date = new Date(date);
let serverTimeOffset = GetServerTimeOffsetSync(siteUrl, date);
return _SPServerLocalTimeToUTCDate(date, serverTimeOffset);
}
function _SPServerLocalTimeToUTCDate(date, serverTimeOffset) {
let localTimeOffset = date.getTimezoneOffset() * 60000;
return new Date(serverTimeOffset - localTimeOffset + date.getTime());
}
/** get date yyyy:MM:ddTHH:mm:ss NO ZED
* returns yyyy:MM:ddTHH:mm:ssZ
* expensive, but works. for faster bulk parsing use toIsoDateFormat(new Date(GetServerTimeOffset + date.getTime()))
* or: SPServerLocalTimeToUTCDate
*/
export async function SPServerLocalTimeToUTC(siteUrl, date) {
siteUrl = GetSiteUrl(siteUrl);
if (isDate(date)) {
date = toIsoDateFormat(date, { omitZ: true });
}
let restUrl = `${GetRestBaseUrl(siteUrl)}/web/regionalSettings/timeZone/localTimeToUTC(@date)?@date='${encodeURIComponent(date)}'`;
let result = await GetJson(restUrl, null, { ...weeekLongLocalCache, jsonMetadata: jsonTypes.nometadata });
return result && result.value || null;
}
/**
* convert date in ISO format (yyyy:MM:ddTHH:mm:ss) or SPServerLocalTime (5/27/2020 11:34, 5-27-2020 11:34)
* returns date in ISO UTC (yyyy:MM:ddTHH:mm:ssZ)
* expensive, but works. for faster bulk parsing use toIsoDateFormat(new Date(GetServerTimeOffset + date.getTime()))
* or: SPServerLocalTimeToUTCDateSync
*/
export function SPServerLocalTimeToUTCSync(siteUrl, date) {
siteUrl = GetSiteUrl(siteUrl);
if (isDate(date)) {
date = toIsoDateFormat(date, { omitZ: true });
}
let restUrl = `${GetRestBaseUrl(siteUrl)}/web/regionalSettings/timeZone/localTimeToUTC(@date)?@date='${encodeURIComponent(date)}'`;
let result = GetJsonSync(restUrl, null, { ...weeekLongLocalCache, jsonMetadata: jsonTypes.nometadata });
return result.success && result.result.value || null;
}
/** get utc date yyyy:MM:ddTHH:mm:ssZ
* returns yyyy:MM:ddTHH:mm:ss NO ZED
* expensive, but works. for faster bulk parsing use toIsoDateFormat(new Date(date.getTime()-GetServerTimeOffset,{omitZ:true}))
*/
export async function UTCToSPServerLocalTime(siteUrl, date) {
siteUrl = GetSiteUrl(siteUrl);
if (isDate(date)) {
date = toIsoDateFormat(date);
}
let supportedLocale = _getSupportedLocaleForUTCToSPServerTime();
if (!isNullOrEmptyString(supportedLocale)) {
try {
let regionalSettings = await GetServerTimeZone(siteUrl);
let timeZone = SPTimeZoneIdToIANATimeZoneName[`${regionalSettings.Id}`];
if (!isNullOrEmptyString(timeZone)) {
let result = _UTCDateStringToSPServerLocalDateString(date, timeZone, supportedLocale);
if (!isNullOrEmptyString(result)) {
return result;
}
}
}
catch {
}
}
let restUrl = `${GetRestBaseUrl(siteUrl)}/web/regionalSettings/timeZone/utcToLocalTime(@date)?@date='${encodeURIComponent(date)}'`;
let result = await GetJson(restUrl, null, { ...longLocalCache, jsonMetadata: jsonTypes.nometadata });
return result && result.value || null;
}
/** get utc date yyyy:MM:ddTHH:mm:ssZ
* returns yyyy:MM:ddTHH:mm:ss NO ZED
* expensive, but works. for faster bulk parsing use toIsoDateFormat(new Date(date.getTime()-GetServerTimeOffset,{omitZ:true}))
*/
export function UTCToSPServerLocalTimeSync(siteUrl, date) {
siteUrl = GetSiteUrl(siteUrl);
if (isDate(date)) {
date = toIsoDateFormat(date);
}
let supportedLocale = _getSupportedLocaleForUTCToSPServerTime();
if (!isNullOrEmptyString(supportedLocale)) {
try {
let regionalSettings = GetServerTimeZoneSync(siteUrl);
let timeZone = SPTimeZoneIdToIANATimeZoneName[`${regionalSettings.Id}`];
if (!isNullOrEmptyString(timeZone)) {
let result = _UTCDateStringToSPServerLocalDateString(date, timeZone, supportedLocale);
if (!isNullOrEmptyString(result)) {
return result;
}
}
}
catch {
}
}
let restUrl = `${GetRestBaseUrl(siteUrl)}/web/regionalSettings/timeZone/utcToLocalTime(@date)?@date='${encodeURIComponent(date)}'`;
let result = GetJsonSync(restUrl, null, { ...longLocalCache, jsonMetadata: jsonTypes.nometadata });
return result.success && result.result.value || null;
}
function _getSupportedLocaleForUTCToSPServerTime() {
try {
let supportedLocales = Intl.DateTimeFormat.supportedLocalesOf(["en-CA", "sv-SE"]);
return supportedLocales[0];
}
catch {
}
return null;
}
function _UTCDateStringToSPServerLocalDateString(utcISODate, targetIanaTimeZone, supportedLocale) {
let formatter = new Intl.DateTimeFormat(supportedLocale, {
timeZone: targetIanaTimeZone,
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
fractionalSecondDigits: 3,
hour12: false
});
let value1 = formatter.format(new Date(utcISODate));
if (supportedLocale.toLowerCase() === "en-ca") {
value1 = value1.replace(",", "").replace(" ", "T").split(".000")[0];
}
else {
value1 = value1.replace(",", ".").replace(" ", "T").split(".000")[0];
}
return isISODate(value1) ? value1 : null;
}
export function SPServerLocalTimeSync(siteUrl) {
siteUrl = GetSiteUrl(siteUrl);
var clientNowServerDeltas = getGlobal("ClientNowServerDeltas");
var clientNowServerDelta = clientNowServerDeltas[siteUrl];
var now = new Date();
if (isNullOrUndefined(clientNowServerDelta)) {
var local = UTCToSPServerLocalTimeSync(siteUrl, now.toISOString());
clientNowServerDelta = (+now - +(new Date(local)));
clientNowServerDeltas[siteUrl] = clientNowServerDelta;
}
var newdate = new Date(+now - clientNowServerDelta);
return toIsoDateFormat(newdate, { omitZ: true });
}
export async function SPServerLocalTime(siteUrl) {
siteUrl = GetSiteUrl(siteUrl);
var clientNowServerDeltas = getGlobal("ClientNowServerDeltas");
var clientNowServerDelta = clientNowServerDeltas[siteUrl];
var now = new Date();
if (isNullOrUndefined(clientNowServerDelta)) {
var local = await UTCToSPServerLocalTime(siteUrl, now.toISOString());
clientNowServerDelta = (+now - +(new Date(local)));
clientNowServerDeltas[siteUrl] = clientNowServerDelta;
}
var newdate = new Date(+now - clientNowServerDelta);
return toIsoDateFormat(newdate, { omitZ: true });
}
export async function SPServerLocalToday(siteUrl) {
siteUrl = GetSiteUrl(siteUrl);
return promiseLock(`SPServerLocalToday)_${siteUrl}`, async () => {
return SPServerLocalTime(siteUrl);
}, 30000);
}
export function GetContextWebInformationSync(siteUrl) {
var siteId = null;
if (_spPageContextInfo && _spPageContextInfo.isAppWeb) {
//inside an app web you can't get the contextinfo for any other site
siteUrl = _spPageContextInfo.webServerRelativeUrl;
siteId = _spPageContextInfo.siteId;
}
else {
siteId = GetSiteIdSync(siteUrl);
if (isNullOrEmptyString(siteId)) {
return null;
}
}
let result = GetJsonSync(`${GetRestBaseUrl(siteUrl)}/contextinfo`, null, {
method: "POST",
maxAge: 5 * 60,
includeDigestInPost: false,
allowCache: true,
postCacheKey: `GetContextWebInformation_${normalizeGuid(siteId)}`
});
if (result && result.success) {
return result.result.d.GetContextWebInformation;
}
else {
return null;
}
}
function _getCustomActionsBaseRestUrl(siteUrl, options = {}) {
const { listId, actionId } = { ...options };
let restUrl = `${GetRestBaseUrl(siteUrl)}/web`;
if (!isNullOrEmptyString(listId)) {
restUrl += `/lists('${normalizeGuid(listId)}')`;
}
restUrl += `/UserCustomActions`;
if (!isNullOrEmptyString(actionId)) {
restUrl += `('${actionId}')`;
}
return restUrl;
}
function _parseCustomActionReponse(action) {
if (isNullOrUndefined(action)) {
return action;
}
if (!isNullOrUndefined(action.Rights)) {
if (isNumeric(action.Rights.High)) {
action.Rights.High = Number(action.Rights.High);
}
if (isNumeric(action.Rights.Low)) {
action.Rights.Low = Number(action.Rights.Low);
}
}
return action;
}
function _convertCustomActionToPostData(action) {
//The rest end point expects the rights in string format for some odd reason despite IBasePermissions being stored
//as High/Low numbers and the methods using numbers (ie. SPBasePermission). Even EffectiveBasePermissions on
//a list are stored using numbers.
let hasRights = !isNullOrUndefined(action.Rights);
let partialAction;
if (hasRights) {
partialAction = {
Rights: {
High: `${action.Rights.High}`,
Low: `${action.Rights.Low}`
}
};
delete action.Rights;
}
let data = { ...action, ...partialAction };
return data;
}
/** Get UserCustomActions for web/list */
export async function GetUserCustomActions(siteUrl, listId, allowCache = true) {
let restUrl = _getCustomActionsBaseRestUrl(siteUrl, { listId: listId });
let cacheOptions = allowCache === true ? shortLocalCache : { allowCache: false };
let restOptions = {
jsonMetadata: jsonTypes.nometadata,
...cacheOptions
};
try {
let response = await GetJson(restUrl, null, restOptions);
if (!isNullOrUndefined(response) && !isNullOrEmptyArray(response.value)) {
return response.value.map(_parseCustomActionReponse);
}
}
catch {
}
return [];
}
/** Get UserCustomAction by id from web/list */
export async function GetUserCustomActionById(siteUrl, customActionId, listId, allowCache = true) {
let restUrl = _getCustomActionsBaseRestUrl(siteUrl, { listId: listId, actionId: customActionId });
let cacheOptions = allowCache === true ? shortLocalCache : { allowCache: false };
let restOptions = {
jsonMetadata: jsonTypes.nometadata,
...cacheOptions
};
try {
let response = await GetJson(restUrl, null, restOptions);
if (!isNullOrUndefined(response)) {
return _parseCustomActionReponse(response);
}
}
catch {
}
return null;
}
/** Get UserCustomAction by name from web/list */
export async function GetUserCustomActionByName(siteUrl, name, listId, allowCache = true) {
let restUrl = `${_getCustomActionsBaseRestUrl(siteUrl, { listId: listId })}?$filter=Name eq '${encodeURIComponent(name)}'`;
let cacheOptions = allowCache === true ? shortLocalCache : { allowCache: false };
let restOptions = {
jsonMetadata: jsonTypes.nometadata,
...cacheOptions
};
try {
let response = await GetJson(restUrl, null, restOptions);
if (!isNullOrUndefined(response) && !isNullOrEmptyArray(response.value)) {
return response.value.map(_parseCustomActionReponse);
}
}
catch {
}
return [];
}
/** Add UserCustomAction to web/list */
export async function AddUserCustomAction(siteUrl, userCustomActionInfo, listId) {
let restUrl = _getCustomActionsBaseRestUrl(siteUrl, { listId: listId });
let restOptions = {
jsonMetadata: jsonTypes.nometadata,
method: "POST",
includeDigestInPost: true
};
try {
let data = _convertCustomActionToPostData(userCustomActionInfo);
let response = await GetJson(restUrl, JSON.stringify(data), restOptions);
if (!isNullOrUndefined(response)) {
return _parseCustomActionReponse(response);
}
}
catch {
}
return null;
}
/** Update UserCustomAction to web/list */
export async function UpdateUserCustomAction(siteUrl, customActionId, userCustomActionInfo, listId) {
let restUrl = _getCustomActionsBaseRestUrl(siteUrl, { listId: listId, actionId: customActionId });
let restOptions = {
jsonMetadata: jsonTypes.nometadata,
method: "POST",
xHttpMethod: "MERGE",
includeDigestInPost: true
};
try {
let data = _convertCustomActionToPostData(userCustomActionInfo);
let result = await GetJson(restUrl, JSON.stringify(data), restOptions);
return !isNullOrUndefined(result) && result["odata.null"] === true || isNullOrEmptyString(result);
}
catch {
}
return false;
}
/** Delete UserCustomAction from web/list */
export async function DeleteUserCustomAction(siteUrl, customActionId, listId) {
let restUrl = _getCustomActionsBaseRestUrl(siteUrl, { listId: listId, actionId: customActionId });
let restOptions = {
jsonMetadata: jsonTypes.nometadata,
method: "POST",
xHttpMethod: "DELETE",
includeDigestInPost: true
};
try {
let result = await GetJson(restUrl, null, restOptions);
return !isNullOrUndefined(result) && result["odata.null"] === true || isNullOrEmptyString(result);
}
catch {
}
return false;
}
/** Get web regional settings */
export async function GetRegionalSettings(siteUrl) {
siteUrl = GetSiteUrl(siteUrl);
let restUrl = `${GetRestBaseUrl(siteUrl)}/web/regionalSettings`;
try {
let result = await GetJson(restUrl, null, { ...mediumLocalCache, jsonMetadata: jsonTypes.nometadata });
return result;
}
catch {
}
return null;
}
/** Get all web properties */
export async function GetAllWebProperties(siteUrl) {
siteUrl = GetSiteUrl(siteUrl);
let restUrl = `${GetRestBaseUrl(siteUrl)}/web/AllProperties`;
try {
let result = await GetJson(restUrl, null, { ...shortLocalCache, jsonMetadata: jsonTypes.nometadata });
return result;
}
catch {
}
return null;
}
/** Get web property by name */
export async function GetWebPropertyByName(name, siteUrl) {
siteUrl = GetSiteUrl(siteUrl);
let restUrl = `${GetRestBaseUrl(siteUrl)}/web/AllProperties?$select=${name}`;
try {
let result = await GetJson(restUrl, null, { ...shortLocalCache, jsonMetadata: jsonTypes.nometadata });
if (!isNullOrUndefined(result) && !isNullOrUndefined(result[name])) {
return result[name];
}
}
catch {
}
return null;
}
export function getFormDigest(serverRelativeWebUrl) {
var contextWebInformation = GetContextWebInformationSync(serverRelativeWebUrl);
return contextWebInformation && contextWebInformation.FormDigestValue || null;
}
export function ensureLegacyProps(pageContext) {
try {
let isContextOk = (ctx) => !isNullOrUndefined(ctx) && !isNullOrUndefined(ctx.webServerRelativeUrl);
let getLegacyContext = (ctx) => !isNullOrUndefined(ctx) && !isNullOrUndefined(ctx.legacyPageContext) ? ctx.legacyPageContext : null;
let getContext = (ctx) => isContextOk(ctx) ? ctx : getLegacyContext(ctx);
if (isTypeofFullNameNullOrUndefined("_spPageContextInfo") || !isContextOk(_spPageContextInfo)) {
logger.info(`_spPageContextInfo ${isTypeofFullNameNullOrUndefined("_spPageContextInfo") ? 'is missing' : 'is broken'}, wrapping with our property`);
//bug in SPFx during inplace left navigation will put an SPFx object into this global. Correct it using the setter.
let _currentContext = pageContext.legacyPageContext;
Object.defineProperty(window, "_spPageContextInfo", {
set: (newContext) => {
if (!isNullOrUndefined(newContext)) {
if (isContextOk(newContext)) {
logger.debug("Context ok");
_currentContext = newContext;
}
else {
let legacy = getLegacyContext(newContext);
if (!isNullOrUndefined(legacy)) {
logger.error("Context NOT ok - using legacy context");
_currentContext = legacy;
}
else
logger.error("Context NOT ok - no legacy context either.");
}
}
},
get: () => getContext(_currentContext)
});
}
}
catch (ex) {
}
}
export async function WebHasUniquePermissions(siteUrl) {
let url = `${GetRestBaseUrl(siteUrl)}/web?$select=hasuniqueroleassignments`;
let has = await GetJson(url, undefined, { allowCache: false, jsonMetadata: jsonTypes.nometadata });
return has.HasUniqueRoleAssignments === true;
}
export async function RestoreWebPermissionInheritance(siteUrl) {
let url = `${GetRestBaseUrl(siteUrl)}/web/ResetRoleInheritance`;
await GetJson(url, undefined, { method: "POST", allowCache: false, jsonMetadata: jsonTypes.nometadata, spWebUrl: siteUrl });
}
export async function BreakWebPermissionInheritance(siteUrl, clear = true) {
let url = `${GetRestBaseUrl(siteUrl)}/web/breakroleinheritance(copyRoleAssignments=${clear ? 'false' : 'true'}, clearSubscopes=true)`;
await GetJson(url, undefined, { method: "POST", allowCache: false, jsonMetadata: jsonTypes.nometadata, spWebUrl: siteUrl });
}
export async function AssignWebPermission(siteUrl, principalId, roleId) {
let url = `${GetRestBaseUrl(siteUrl)}/web/roleassignments/addroleassignment(principalid=${principalId},roleDefId=${roleId})`;
await GetJson(url, undefined, { method: "POST", allowCache: false, jsonMetadata: jsonTypes.nometadata, spWebUrl: siteUrl });
}
export async function RemoveWebPermission(siteUrl, principalId, roleId) {
let url = `${GetRestBaseUrl(siteUrl)}/web/roleassignments/removeroleassignment(principalid=${principalId},roleDefId=${roleId})`;
await GetJson(url, undefined, { method: "POST", allowCache: false, jsonMetadata: jsonTypes.nometadata, spWebUrl: siteUrl });
}
/** set a user as site admin - rejects/throws if not successful */
export async function SetUserAsSiteAdmin(siteUrl, userId) {
const url = `${GetRestBaseUrl(siteUrl)}/web/getuserbyid(${userId})`;
await GetJson(url, jsonStringify({
"__metadata": { "type": "SP.User" },
"IsSiteAdmin": true
}), { method: 'POST', xHttpMethod: 'MERGE' });
return true;
}
/** get all the rentention labels (compliance tags) for a site */
export async function GetAvailableTagsForSite(siteUrlOrId) {
let siteUrl = GetSiteUrl(siteUrlOrId);
try {
let url = `${siteUrl}_api/SP.CompliancePolicy.SPPolicyStoreProxy.GetAvailableTagsForSite(siteUrl=@a1)?@a1='${encodeURIComponent(makeFullUrl(siteUrl))}'`;
let result = await GetJson(url, null, {
jsonMetadata: jsonTypes.nometadata
});
return result.value;
}
catch {
return [];
}
}
/** get all the rentention labels (compliance tags) for a site */
export function GetAvailableTagsForSiteSync(siteUrlOrId) {
let siteUrl = GetSiteUrl(siteUrlOrId);
try {
let url = `${siteUrl}_api/SP.CompliancePolicy.SPPolicyStoreProxy.GetAvailableTagsForSite(siteUrl=@a1)?@a1='${encodeURIComponent(makeFullUrl(siteUrl))}'`;
let response = GetJsonSync(url, null, {
jsonMetadata: jsonTypes.nometadata
});
return response.success ? response.result.value : [];
}
catch {
return [];
}
}
export async function GetActiveFeatures(siteUrlOrId, allowCache = true) {
let siteUrl = GetSiteUrl(siteUrlOrId);
try {
let url = `${siteUrl}/_api/web/features?$select=DisplayName,DefinitionId`;
let response = await GetJson(url, null, {
method: "GET",
jsonMetadata: jsonTypes.nometadata,
includeDigestInGet: true,
...(allowCache === true ? mediumLocalCache : noLocalCache)
});
return response.value;
}
catch {
}
return null;
}
export async function ActivateFeature(siteUrlOrId, id) {
let siteUrl = GetSiteUrl(siteUrlOrId);
try {
id = normalizeGuid(id);
let url = `${siteUrl}/_api/web/features/add('${id}')`;
let response = await GetJson(url, null, {
method: "POST",
jsonMetadata