923 lines
29 KiB
TypeScript

// (C) Copyright 2015 Moodle Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { InAppBrowserObject, InAppBrowserOptions } from '@awesome-cordova-plugins/in-app-browser';
import { CoreNetwork } from '@services/network';
import { CoreDB } from '@services/db';
import { CoreEventData, CoreEvents } from '@singletons/events';
import { CoreFile } from '@services/file';
import {
CoreWS,
CoreWSFileUploadOptions,
CoreWSExternalWarning,
CoreWSUploadFileResult,
} from '@services/ws';
import { CoreDomUtils } from '@services/utils/dom';
import { CoreTextUtils } from '@services/utils/text';
import { CoreTimeUtils } from '@services/utils/time';
import { CoreUrl } from '@singletons/url';
import { CoreUtils, CoreUtilsOpenInBrowserOptions } from '@services/utils/utils';
import { CoreConstants } from '@/core/constants';
import { SQLiteDB } from '@classes/sqlitedb';
import { CoreError } from '@classes/errors/error';
import { CoreLogger } from '@singletons/logger';
import { Translate } from '@singletons';
import { CoreIonLoadingElement } from '../ion-loading';
import { CoreSites, CoreSitesReadingStrategy } from '@services/sites';
import { asyncInstance, AsyncInstance } from '../../utils/async-instance';
import { CoreDatabaseTable } from '../database/database-table';
import { CoreDatabaseCachingStrategy } from '../database/database-table-proxy';
import {
CONFIG_TABLE,
CoreSiteConfigDBRecord,
CoreSiteLastViewedDBPrimaryKeys,
CoreSiteLastViewedDBRecord,
CoreSiteWSCacheRecord,
LAST_VIEWED_PRIMARY_KEYS,
LAST_VIEWED_TABLE,
WS_CACHE_TABLE,
} from '@services/database/sites';
import { map } from 'rxjs/operators';
import { CoreFilepool } from '@services/filepool';
import { CoreSiteInfo } from './unauthenticated-site';
import { CoreAuthenticatedSite, CoreAuthenticatedSiteOptionalData, CoreSiteWSPreSets, WSObservable } from './authenticated-site';
import { firstValueFrom } from 'rxjs';
import { CorePlatform } from '@services/platform';
import { CoreLoadings } from '@services/loadings';
/**
* Class that represents a site (combination of site + user).
* It will have all the site data and provide utility functions regarding a site.
*/
export class CoreSite extends CoreAuthenticatedSite {
id: string;
config?: CoreSiteConfig;
loggedOut?: boolean;
protected db!: SQLiteDB;
protected cacheTable: AsyncInstance<CoreDatabaseTable<CoreSiteWSCacheRecord>>;
protected configTable: AsyncInstance<CoreDatabaseTable<CoreSiteConfigDBRecord, 'name', never>>;
protected lastViewedTable: AsyncInstance<CoreDatabaseTable<CoreSiteLastViewedDBRecord, CoreSiteLastViewedDBPrimaryKeys>>;
protected lastAutoLogin = 0;
protected tokenPluginFileWorks?: boolean;
protected tokenPluginFileWorksPromise?: Promise<boolean>;
protected oauthId?: number;
/**
* Create a site.
*
* @param id Site ID.
* @param siteUrl Site URL.
* @param token Site's WS token.
* @param otherData Other data.
*/
constructor(
id: string,
siteUrl: string,
token: string,
otherData: CoreSiteOptionalData = {},
) {
super(siteUrl, token, otherData);
this.id = id;
this.config = otherData.config;
this.loggedOut = otherData.loggedOut;
this.logger = CoreLogger.getInstance('CoreSite');
this.cacheTable = asyncInstance(() => CoreSites.getSiteTable(WS_CACHE_TABLE, {
siteId: this.getId(),
database: this.getDb(),
config: { cachingStrategy: CoreDatabaseCachingStrategy.None },
}));
this.configTable = asyncInstance(() => CoreSites.getSiteTable<CoreSiteConfigDBRecord, 'name', never>(CONFIG_TABLE, {
siteId: this.getId(),
database: this.getDb(),
config: { cachingStrategy: CoreDatabaseCachingStrategy.Eager },
primaryKeyColumns: ['name'],
rowIdColumn: null,
}));
this.lastViewedTable = asyncInstance(() => CoreSites.getSiteTable(LAST_VIEWED_TABLE, {
siteId: this.getId(),
database: this.getDb(),
config: { cachingStrategy: CoreDatabaseCachingStrategy.Eager },
primaryKeyColumns: [...LAST_VIEWED_PRIMARY_KEYS],
}));
this.setInfo(otherData.info);
this.calculateOfflineDisabled();
this.db = CoreDB.getDB('Site-' + this.id);
}
/**
* Get site ID.
*
* @returns Site ID.
*/
getId(): string {
return this.id;
}
/**
* Get site DB.
*
* @returns Site DB.
*/
getDb(): SQLiteDB {
return this.db;
}
/**
* Check if user logged out from the site and needs to authenticate again.
*
* @returns Whether is logged out.
*/
isLoggedOut(): boolean {
return !!this.loggedOut;
}
/**
* Get OAuth ID.
*
* @returns OAuth ID.
*/
getOAuthId(): number | undefined {
return this.oauthId;
}
/**
* Set site config.
*
* @param config Config.
*/
setConfig(config: CoreSiteConfig): void {
if (config) {
config.tool_mobile_disabledfeatures = CoreTextUtils.treatDisabledFeatures(config.tool_mobile_disabledfeatures);
}
this.config = config;
this.calculateOfflineDisabled();
}
/**
* Set site logged out.
*
* @param loggedOut True if logged out and needs to authenticate again, false otherwise.
*/
setLoggedOut(loggedOut: boolean): void {
this.loggedOut = !!loggedOut;
}
/**
* Set OAuth ID.
*
* @param oauthId OAuth ID.
*/
setOAuthId(oauthId: number | undefined): void {
this.oauthId = oauthId;
}
/**
* Check if the user authenticated in the site using an OAuth method.
*
* @returns Whether the user authenticated in the site using an OAuth method.
*/
isOAuth(): boolean {
return this.oauthId != null && this.oauthId !== undefined;
}
/**
* @inheritdoc
*/
protected async getCacheEntryById(id: string): Promise<CoreSiteWSCacheRecord> {
return this.cacheTable.getOneByPrimaryKey({ id });
}
/**
* @inheritdoc
*/
protected async getCacheEntriesByKey(key: string): Promise<CoreSiteWSCacheRecord[]> {
return this.cacheTable.getMany({ key });
}
/**
* @inheritdoc
*/
protected async storeCacheEntry(entry: CoreSiteWSCacheRecord): Promise<void> {
await this.cacheTable.insert(entry);
}
/**
* @inheritdoc
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
protected async deleteFromCache(method: string, data: any, preSets: CoreSiteWSPreSets, allCacheKey?: boolean): Promise<void> {
if (allCacheKey) {
await this.cacheTable.delete({ key: preSets.cacheKey });
} else {
await this.cacheTable.deleteByPrimaryKey({ id: this.getCacheId(method, data) });
}
}
/**
* Gets the size of cached data for a specific component or component instance.
*
* @param component Component name
* @param componentId Optional component id (if not included, returns sum for whole component)
* @returns Promise resolved when we have calculated the size
*/
async getComponentCacheSize(component: string, componentId?: number): Promise<number> {
const params: Array<string | number> = [component];
let extraClause = '';
if (componentId !== undefined && componentId !== null) {
params.push(componentId);
extraClause = ' AND componentId = ?';
}
return this.cacheTable.reduce(
{
sql: 'SUM(length(data))',
js: (size, record) => size + record.data.length,
jsInitialValue: 0,
},
{
sql: 'WHERE component = ?' + extraClause,
sqlParams: params,
js: record => record.component === component && (params.length === 1 || record.componentId === componentId),
},
);
}
/**
* Deletes WS cache entries for all methods relating to a specific component (and
* optionally component id).
*
* @param component Component name.
* @param componentId Component id.
* @returns Promise resolved when the entries are deleted.
*/
async deleteComponentFromCache(component: string, componentId?: number): Promise<void> {
if (!component) {
return;
}
const params = { component };
if (componentId) {
params['componentId'] = componentId;
}
await this.cacheTable.delete(params);
}
/*
* Uploads a file using Cordova File API.
*
* @param filePath File path.
* @param options File upload options.
* @param onProgress Function to call on progress.
* @returns Promise resolved when uploaded.
*/
uploadFile(
filePath: string,
options: CoreWSFileUploadOptions,
onProgress?: (event: ProgressEvent) => void,
): Promise<CoreWSUploadFileResult> {
if (!options.fileArea) {
options.fileArea = 'draft';
}
return CoreWS.uploadFile(filePath, options, {
siteUrl: this.siteUrl,
wsToken: this.token || '',
}, onProgress);
}
/**
* Invalidates all caches related to the site.
*/
async invalidateCaches(): Promise<void> {
await Promise.all([
CoreFilepool.invalidateAllFiles(this.getId()),
this.invalidateWsCache(),
]);
}
/**
* @inheritdoc
*/
async invalidateWsCache(): Promise<void> {
this.logger.debug('Invalidate all the cache for site: ' + this.id);
try {
await this.cacheTable.update({ expirationTime: 0 });
} finally {
CoreEvents.trigger(CoreEvents.WS_CACHE_INVALIDATED, {}, this.getId());
}
}
/**
* @inheritdoc
*/
async invalidateWsCacheForKey(key: string): Promise<void> {
if (!key) {
return;
}
this.logger.debug('Invalidate cache for key: ' + key);
await this.cacheTable.update({ expirationTime: 0 }, { key });
}
/**
* @inheritdoc
*/
async invalidateWsCacheForKeyStartingWith(key: string): Promise<void> {
if (!key) {
return;
}
this.logger.debug('Invalidate cache for key starting with: ' + key);
await this.cacheTable.updateWhere({ expirationTime: 0 }, {
sql: 'key LIKE ?',
sqlParams: [key + '%'],
js: record => !!record.key?.startsWith(key),
});
}
/**
* Check if tokenpluginfile can be used, and fix the URL afterwards.
*
* @param url The url to be fixed.
* @returns Promise resolved with the fixed URL.
*/
checkAndFixPluginfileURL(url: string): Promise<string> {
return this.checkTokenPluginFile(url).then(() => this.fixPluginfileURL(url));
}
/**
* Generic function for adding the wstoken to Moodle urls and for pointing to the correct script.
* Uses CoreUtilsProvider.fixPluginfileURL, passing site's token.
*
* @param url The url to be fixed.
* @returns Fixed URL.
*/
fixPluginfileURL(url: string): string {
const accessKey = this.tokenPluginFileWorks || this.tokenPluginFileWorks === undefined ?
this.infos && this.infos.userprivateaccesskey : undefined;
return CoreUrl.fixPluginfileURL(url, this.token || '', this.siteUrl, accessKey);
}
/**
* Deletes site's DB.
*
* @returns Promise to be resolved when the DB is deleted.
*/
async deleteDB(): Promise<void> {
await CoreDB.deleteDB('Site-' + this.id);
}
/**
* Deletes site's folder.
*
* @returns Promise to be resolved when the DB is deleted.
*/
async deleteFolder(): Promise<void> {
if (!CoreFile.isAvailable() || !this.id) {
return;
}
const siteFolder = CoreFile.getSiteFolder(this.id);
// Ignore any errors, removeDir fails if folder doesn't exists.
await CoreUtils.ignoreErrors(CoreFile.removeDir(siteFolder));
}
/**
* Get space usage of the site.
*
* @returns Promise resolved with the site space usage (size).
*/
getSpaceUsage(): Promise<number> {
if (CoreFile.isAvailable() && this.id) {
const siteFolderPath = CoreFile.getSiteFolder(this.id);
return CoreFile.getDirectorySize(siteFolderPath).catch(() => 0);
} else {
return Promise.resolve(0);
}
}
/**
* Gets an approximation of the cache table usage of the site.
*
* Currently this is just the total length of the data fields in the cache table.
*
* @returns Promise resolved with the total size of all data in the cache table (bytes)
*/
async getCacheUsage(): Promise<number> {
return this.cacheTable.reduce({
sql: 'SUM(length(data))',
js: (size, record) => size + record.data.length,
jsInitialValue: 0,
});
}
/**
* Gets a total of the file and cache usage.
*
* @returns Promise with the total of getSpaceUsage and getCacheUsage
*/
async getTotalUsage(): Promise<number> {
const space = await this.getSpaceUsage();
const cache = await this.getCacheUsage();
return space + cache;
}
/**
* Check if GET method is supported for AJAX calls.
*
* @returns Whether it's supported.
*/
protected isAjaxGetSupported(): boolean {
return !!this.getInfo() && this.isVersionGreaterEqualThan('3.8');
}
/**
* Open a URL in browser using auto-login in the Moodle site if available.
*
* @param url The URL to open.
* @param alertMessage If defined, an alert will be shown before opening the browser.
* @param options Other options.
* @returns Promise resolved when done, rejected otherwise.
*/
async openInBrowserWithAutoLogin(
url: string,
alertMessage?: string,
options: CoreUtilsOpenInBrowserOptions = {},
): Promise<void> {
await this.openWithAutoLogin(false, url, options, alertMessage);
}
/**
* Open a URL in inappbrowser using auto-login in the Moodle site if available.
*
* @param url The URL to open.
* @param options Override default options passed to InAppBrowser.
* @param alertMessage If defined, an alert will be shown before opening the inappbrowser.
* @returns Promise resolved when done.
*/
async openInAppWithAutoLogin(url: string, options?: InAppBrowserOptions, alertMessage?: string): Promise<InAppBrowserObject> {
const iabInstance = <InAppBrowserObject> await this.openWithAutoLogin(true, url, options, alertMessage);
return iabInstance;
}
/**
* Open a URL in browser or InAppBrowser using auto-login in the Moodle site if available.
*
* @param inApp True to open it in InAppBrowser, false to open in browser.
* @param url The URL to open.
* @param options Override default options passed to $cordovaInAppBrowser#open.
* @param alertMessage If defined, an alert will be shown before opening the browser/inappbrowser.
* @returns Promise resolved when done. Resolve param is returned only if inApp=true.
*/
async openWithAutoLogin(
inApp: boolean,
url: string,
options: InAppBrowserOptions & CoreUtilsOpenInBrowserOptions = {},
alertMessage?: string,
): Promise<InAppBrowserObject | void> {
// Get the URL to open.
const autoLoginUrl = await this.getAutoLoginUrl(url);
if (alertMessage) {
// Show an alert first.
const alert = await CoreDomUtils.showAlert(
Translate.instant('core.notice'),
alertMessage,
undefined,
3000,
);
await alert.onDidDismiss();
options.showBrowserWarning = false; // A warning already shown, no need to show another.
}
options.originalUrl = url;
// Open the URL.
if (inApp) {
if (
options.clearsessioncache === undefined && autoLoginUrl !== url &&
(
CoreConstants.CONFIG.clearIABSessionWhenAutoLogin === 'all' ||
(CoreConstants.CONFIG.clearIABSessionWhenAutoLogin === 'android' && CorePlatform.isAndroid()) ||
(CoreConstants.CONFIG.clearIABSessionWhenAutoLogin === 'ios' && CorePlatform.isIOS())
)
) {
options.clearsessioncache = 'yes';
}
return CoreUtils.openInApp(autoLoginUrl, options);
} else {
return CoreUtils.openInBrowser(autoLoginUrl, options);
}
}
/**
* Get the config of this site.
* It is recommended to use getStoredConfig instead since it's faster and doesn't use network.
*
* @param name Name of the setting to get. If not set or false, all settings will be returned.
* @param ignoreCache True if it should ignore cached data.
* @returns Promise resolved with site config.
*/
getConfig(name?: undefined, ignoreCache?: boolean): Promise<CoreSiteConfig>;
getConfig(name: string, ignoreCache?: boolean): Promise<string>;
getConfig(name?: string, ignoreCache?: boolean): Promise<string | CoreSiteConfig> {
return firstValueFrom(
this.getConfigObservable(<string> name, ignoreCache ? CoreSitesReadingStrategy.ONLY_NETWORK : undefined),
);
}
/**
* Get the config of this site.
* It is recommended to use getStoredConfig instead since it's faster and doesn't use network.
*
* @param name Name of the setting to get. If not set or false, all settings will be returned.
* @param readingStrategy Reading strategy.
* @returns Observable returning site config.
*/
getConfigObservable(name?: undefined, readingStrategy?: CoreSitesReadingStrategy): WSObservable<CoreSiteConfig>;
getConfigObservable(name: string, readingStrategy?: CoreSitesReadingStrategy): WSObservable<string>;
getConfigObservable(name?: string, readingStrategy?: CoreSitesReadingStrategy): WSObservable<string | CoreSiteConfig> {
const preSets: CoreSiteWSPreSets = {
cacheKey: this.getConfigCacheKey(),
...CoreSites.getReadingStrategyPreSets(readingStrategy),
};
return this.readObservable<CoreSiteConfigResponse>('tool_mobile_get_config', {}, preSets).pipe(map(config => {
if (name) {
// Return the requested setting.
for (const x in config.settings) {
if (config.settings[x].name == name) {
return String(config.settings[x].value);
}
}
throw new CoreError('Site config not found: ' + name);
} else {
// Return all settings in the same array.
const settings: CoreSiteConfig = {};
config.settings.forEach((setting) => {
settings[setting.name] = String(setting.value);
});
return settings;
}
}));
}
/**
* Invalidates config WS call.
*
* @returns Promise resolved when the data is invalidated.
*/
async invalidateConfig(): Promise<void> {
await this.invalidateWsCacheForKey(this.getConfigCacheKey());
}
/**
* Get cache key for getConfig WS calls.
*
* @returns Cache key.
*/
protected getConfigCacheKey(): string {
return 'tool_mobile_get_config';
}
/**
* Get the stored config of this site.
*
* @param name Name of the setting to get. If not set, all settings will be returned.
* @returns Site config or a specific setting.
*/
getStoredConfig(): CoreSiteConfig | undefined;
getStoredConfig(name: string): string | undefined;
getStoredConfig(name?: string): string | CoreSiteConfig | undefined {
if (!this.config) {
return;
}
if (name) {
return this.config[name];
} else {
return this.config;
}
}
/**
* @inheritdoc
*/
protected getDisabledFeatures(): string | undefined {
return this.config ? this.getStoredConfig('tool_mobile_disabledfeatures') : super.getDisabledFeatures();
}
/**
* @inheritdoc
*/
protected triggerSiteEvent<Fallback = unknown, Event extends string = string>(
eventName: Event,
data?: CoreEventData<Event, Fallback>,
): void {
CoreEvents.trigger(eventName, data, this.id);
}
/**
* Calculate if offline is disabled in the site.
*/
calculateOfflineDisabled(): void {
this.offlineDisabled = this.isFeatureDisabled('NoDelegate_CoreOffline');
}
/**
* Get whether offline is disabled in the site.
*
* @returns Whether it's disabled.
*/
isOfflineDisabled(): boolean {
return this.offlineDisabled;
}
/**
* Given a URL, convert it to a URL that will auto-login if supported.
*
* @param url The URL to convert.
* @param showModal Whether to show a loading modal.
* @returns Promise resolved with the converted URL.
*/
async getAutoLoginUrl(url: string, showModal: boolean = true): Promise<string> {
if (!this.privateToken) {
// No private token, don't change the URL.
return url;
}
if (!this.containsUrl(url)) {
// URL doesn't belong to the site, don't auto login.
return url;
}
if (this.lastAutoLogin > 0) {
const timeBetweenRequests = await CoreUtils.ignoreErrors(
this.getConfig('tool_mobile_autologinmintimebetweenreq'),
CoreConstants.SECONDS_MINUTE * 6,
);
if (CoreTimeUtils.timestamp() - this.lastAutoLogin < Number(timeBetweenRequests)) {
// Not enough time has passed since last auto login.
return url;
}
}
const userId = this.getUserId();
const params = {
privatetoken: this.privateToken,
};
let modal: CoreIonLoadingElement | undefined;
if (showModal) {
modal = await CoreLoadings.show();
}
try {
// Use write to not use cache.
const data = await this.write<CoreSiteAutologinKeyResult>('tool_mobile_get_autologin_key', params);
if (!data.autologinurl || !data.key) {
// Not valid data, return the same URL.
return url;
}
this.lastAutoLogin = CoreTimeUtils.timestamp();
return data.autologinurl + '?userid=' + userId + '&key=' + data.key + '&urltogo=' + encodeURIComponent(url);
} catch (error) {
// Couldn't get autologin key, return the same URL.
return url;
} finally {
modal?.dismiss();
}
}
/**
* Deletes a site setting.
*
* @param name The config name.
* @returns Promise resolved when done.
*/
async deleteSiteConfig(name: string): Promise<void> {
await this.configTable.deleteByPrimaryKey({ name });
}
/**
* Get a site setting on local device.
*
* @param name The config name.
* @param defaultValue Default value to use if the entry is not found.
* @returns Resolves upon success along with the config data. Reject on failure.
*/
async getLocalSiteConfig<T extends number | string>(name: string, defaultValue?: T): Promise<T> {
try {
const entry = await this.configTable.getOneByPrimaryKey({ name });
return <T> entry.value;
} catch (error) {
if (defaultValue !== undefined) {
return defaultValue;
}
throw error;
}
}
/**
* Set a site setting on local device.
*
* @param name The config name.
* @param value The config value. Can only store number or strings.
* @returns Promise resolved when done.
*/
async setLocalSiteConfig(name: string, value: number | string): Promise<void> {
await this.configTable.insert({ name, value });
}
/*
* Check if tokenpluginfile script works in the site.
*
* @param url URL to check.
* @returns Promise resolved with boolean: whether it works or not.
*/
checkTokenPluginFile(url: string): Promise<boolean> {
if (!CoreUrl.canUseTokenPluginFile(url, this.siteUrl, this.infos && this.infos.userprivateaccesskey)) {
// Cannot use tokenpluginfile.
return Promise.resolve(false);
} else if (this.tokenPluginFileWorks !== undefined) {
// Already checked.
return Promise.resolve(this.tokenPluginFileWorks);
} else if (this.tokenPluginFileWorksPromise) {
// Check ongoing, use the same promise.
return this.tokenPluginFileWorksPromise;
} else if (!CoreNetwork.isOnline()) {
// Not online, cannot check it. Assume it's working, but don't save the result.
return Promise.resolve(true);
}
url = this.fixPluginfileURL(url);
this.tokenPluginFileWorksPromise = CoreWS.urlWorks(url).then((result) => {
this.tokenPluginFileWorks = result;
return result;
});
return this.tokenPluginFileWorksPromise;
}
/**
* Deletes last viewed records based on some conditions.
*
* @param conditions Conditions.
* @returns Promise resolved when done.
*/
async deleteLastViewed(conditions?: Partial<CoreSiteLastViewedDBRecord>): Promise<void> {
await this.lastViewedTable.delete(conditions);
}
/**
* Get a last viewed record for a component+id.
*
* @param component The component.
* @param id ID.
* @returns Resolves with last viewed record, undefined if not found.
*/
async getLastViewed(component: string, id: number): Promise<CoreSiteLastViewedDBRecord | undefined> {
try {
return await this.lastViewedTable.getOneByPrimaryKey({ component, id });
} catch {
// Not found.
}
}
/**
* Get several last viewed for a certain component.
*
* @param component The component.
* @param ids IDs. If not provided or empty, return all last viewed for a component.
* @returns Resolves with last viewed records, undefined if error.
*/
async getComponentLastViewed(component: string, ids: number[] = []): Promise<CoreSiteLastViewedDBRecord[] | undefined> {
try {
if (!ids.length) {
return await this.lastViewedTable.getMany({ component });
}
return await this.lastViewedTable.getManyWhere({
sql: `id IN (${ids.map(() => '?').join(', ')}) AND component = ?`,
sqlParams: [...ids, component],
js: (record) => record.component === component && ids.includes(record.id),
});
} catch {
// Not found.
}
}
/**
* Store a last viewed record.
*
* @param component The component.
* @param id ID.
* @param value Last viewed item value.
* @param options Options.
* @returns Promise resolved when done.
*/
async storeLastViewed(
component: string,
id: number,
value: string | number,
options: CoreSiteStoreLastViewedOptions = {},
): Promise<void> {
await this.lastViewedTable.insert({
component,
id,
value: String(value),
data: options.data,
timeaccess: options.timeaccess ?? Date.now(),
});
}
}
/**
* Optional data to create a site.
*/
export type CoreSiteOptionalData = CoreAuthenticatedSiteOptionalData & {
info?: CoreSiteInfo;
config?: CoreSiteConfig;
loggedOut?: boolean;
};
/**
* Result of WS tool_mobile_get_config.
*/
export type CoreSiteConfigResponse = {
settings: { // Settings.
name: string; // The name of the setting.
value: string | number; // The value of the setting.
}[];
warnings?: CoreWSExternalWarning[];
};
/**
* Site config indexed by name.
*/
export type CoreSiteConfig = Record<string, string> & {
supportavailability?: string; // String representation of CoreSiteConfigSupportAvailability.
searchbanner?: string; // Search banner text.
searchbannerenable?: string; // Whether search banner is enabled.
};
/**
* Result of WS tool_mobile_get_autologin_key.
*/
export type CoreSiteAutologinKeyResult = {
key: string; // Auto-login key for a single usage with time expiration.
autologinurl: string; // Auto-login URL.
warnings?: CoreWSExternalWarning[];
};
/**
* Options for storeLastViewed.
*/
export type CoreSiteStoreLastViewedOptions = {
data?: string; // Other data.
timeaccess?: number; // Accessed time. If not set, current time.
};