UNPKG

@kwiz/common

Version:

KWIZ common utilities and helpers for M365 platform

1,165 lines 52.4 kB
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