2017-11-13 12:35:44 +00:00
|
|
|
// (C) Copyright 2015 Martin Dougiamas
|
|
|
|
//
|
|
|
|
// 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 { Injector } from '@angular/core';
|
|
|
|
import { TranslateService } from '@ngx-translate/core';
|
|
|
|
import { HttpClient } from '@angular/common/http';
|
|
|
|
import { SQLiteDB } from './sqlitedb';
|
2018-03-01 15:55:49 +00:00
|
|
|
import { CoreAppProvider } from '@providers/app';
|
|
|
|
import { CoreDbProvider } from '@providers/db';
|
|
|
|
import { CoreEventsProvider } from '@providers/events';
|
|
|
|
import { CoreFileProvider } from '@providers/file';
|
|
|
|
import { CoreLoggerProvider } from '@providers/logger';
|
|
|
|
import { CoreWSProvider, CoreWSPreSets, CoreWSFileUploadOptions } from '@providers/ws';
|
|
|
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
|
|
|
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
|
|
|
import { CoreTimeUtilsProvider } from '@providers/utils/time';
|
|
|
|
import { CoreUrlUtilsProvider } from '@providers/utils/url';
|
|
|
|
import { CoreUtilsProvider } from '@providers/utils/utils';
|
|
|
|
import { CoreConstants } from '@core/constants';
|
2017-11-13 12:35:44 +00:00
|
|
|
import { CoreConfigConstants } from '../configconstants';
|
|
|
|
import { Md5 } from 'ts-md5/dist/md5';
|
2017-12-08 07:23:52 +00:00
|
|
|
import { InAppBrowserObject } from '@ionic-native/in-app-browser';
|
2017-11-13 12:35:44 +00:00
|
|
|
|
2018-01-15 07:22:00 +00:00
|
|
|
/**
|
|
|
|
* PreSets accepted by the WS call.
|
|
|
|
*/
|
2017-11-13 12:35:44 +00:00
|
|
|
export interface CoreSiteWSPreSets {
|
2018-01-15 07:22:00 +00:00
|
|
|
/**
|
|
|
|
* Get the value from the cache if it's still valid.
|
|
|
|
* @type {boolean}
|
|
|
|
*/
|
|
|
|
getFromCache?: boolean;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Save the result to the cache.
|
|
|
|
* @type {boolean}
|
|
|
|
*/
|
|
|
|
saveToCache?: boolean;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Ignore cache expiration.
|
|
|
|
* @type {boolean}
|
|
|
|
*/
|
|
|
|
omitExpires?: boolean;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Use the cache when a request fails. Defaults to true.
|
|
|
|
* @type {boolean}
|
|
|
|
*/
|
|
|
|
emergencyCache?: boolean;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Extra key to add to the cache when storing this call, to identify the entry.
|
|
|
|
* @type {string}
|
|
|
|
*/
|
|
|
|
cacheKey?: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Whether it should use cache key to retrieve the cached data instead of the request params.
|
|
|
|
* @type {boolean}
|
|
|
|
*/
|
|
|
|
getCacheUsingCacheKey?: boolean;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Same as getCacheUsingCacheKey, but for emergency cache.
|
|
|
|
* @type {boolean}
|
|
|
|
*/
|
|
|
|
getEmergencyCacheUsingCacheKey?: boolean;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Whether it should only be 1 entry for this cache key (all entries with same key will be deleted).
|
|
|
|
* @type {boolean}
|
|
|
|
*/
|
|
|
|
uniqueCacheKey?: boolean;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Whether to filter WS response (moodlewssettingfilter). Defaults to true.
|
|
|
|
* @type {boolean}
|
|
|
|
*/
|
|
|
|
filter?: boolean;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Whether to rewrite URLs (moodlewssettingfileurl). Defaults to true.
|
|
|
|
* @type {boolean}
|
|
|
|
*/
|
|
|
|
rewriteurls?: boolean;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Defaults to true. Set to false when the expected response is null.
|
|
|
|
* @type {boolean}
|
|
|
|
*/
|
|
|
|
responseExpected?: boolean;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Defaults to 'object'. Use it when you expect a type that's not an object|array.
|
|
|
|
* @type {string}
|
|
|
|
*/
|
|
|
|
typeExpected?: string;
|
2018-01-29 09:05:20 +00:00
|
|
|
}
|
2017-11-13 12:35:44 +00:00
|
|
|
|
2018-01-15 07:22:00 +00:00
|
|
|
/**
|
|
|
|
* Response of checking local_mobile status.
|
|
|
|
*/
|
2017-11-13 12:35:44 +00:00
|
|
|
export interface LocalMobileResponse {
|
2018-01-15 07:22:00 +00:00
|
|
|
/**
|
|
|
|
* Code to identify the authentication method to use.
|
|
|
|
* @type {number}
|
|
|
|
*/
|
|
|
|
code: number;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Name of the service to use.
|
|
|
|
* @type {string}
|
|
|
|
*/
|
|
|
|
service?: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Code of the warning message.
|
|
|
|
* @type {string}
|
|
|
|
*/
|
|
|
|
warning?: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Whether core SSO is supported.
|
|
|
|
* @type {boolean}
|
|
|
|
*/
|
|
|
|
coreSupported?: boolean;
|
2017-11-13 12:35:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Class that represents a site (combination of site + user).
|
|
|
|
* It will have all the site data and provide utility functions regarding a site.
|
2017-11-20 15:19:21 +00:00
|
|
|
* To add tables to the site's database, please use CoreSitesProvider.createTablesFromSchema. This will make sure that
|
|
|
|
* the tables are created in all the sites, not just the current one.
|
2017-11-13 12:35:44 +00:00
|
|
|
*/
|
|
|
|
export class CoreSite {
|
|
|
|
// List of injected services. This class isn't injectable, so it cannot use DI.
|
|
|
|
protected appProvider;
|
|
|
|
protected dbProvider;
|
|
|
|
protected domUtils;
|
|
|
|
protected eventsProvider;
|
|
|
|
protected fileProvider;
|
|
|
|
protected http;
|
|
|
|
protected textUtils;
|
|
|
|
protected timeUtils;
|
|
|
|
protected translate;
|
|
|
|
protected utils;
|
|
|
|
protected urlUtils;
|
|
|
|
protected wsProvider;
|
|
|
|
|
|
|
|
// Variables for the database.
|
2017-11-14 07:58:20 +00:00
|
|
|
protected WS_CACHE_TABLE = 'wscache';
|
2017-11-13 12:35:44 +00:00
|
|
|
protected tableSchema = {
|
2017-11-14 07:58:20 +00:00
|
|
|
name: this.WS_CACHE_TABLE,
|
2017-11-13 12:35:44 +00:00
|
|
|
columns: [
|
|
|
|
{
|
|
|
|
name: 'id',
|
|
|
|
type: 'TEXT',
|
|
|
|
primaryKey: true
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'data',
|
|
|
|
type: 'TEXT'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'key',
|
|
|
|
type: 'TEXT'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'expirationTime',
|
|
|
|
type: 'INTEGER'
|
|
|
|
}
|
|
|
|
]
|
|
|
|
};
|
|
|
|
|
|
|
|
// Rest of variables.
|
|
|
|
protected logger;
|
|
|
|
protected db: SQLiteDB;
|
|
|
|
protected cleanUnicode = false;
|
|
|
|
protected lastAutoLogin = 0;
|
|
|
|
protected moodleReleases = {
|
2018-01-29 09:05:20 +00:00
|
|
|
3.1: 2016052300,
|
|
|
|
3.2: 2016120500,
|
|
|
|
3.3: 2017051503,
|
|
|
|
3.4: 2017111300
|
2017-11-13 12:35:44 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a site.
|
|
|
|
*
|
|
|
|
* @param {Injector} injector Angular injector to prevent having to pass all the required services.
|
|
|
|
* @param {string} id Site ID.
|
|
|
|
* @param {string} siteUrl Site URL.
|
2017-11-14 07:58:20 +00:00
|
|
|
* @param {string} [token] Site's WS token.
|
|
|
|
* @param {any} [info] Site info.
|
2017-11-13 12:35:44 +00:00
|
|
|
* @param {string} [privateToken] Private token.
|
|
|
|
* @param {any} [config] Site public config.
|
|
|
|
* @param {boolean} [loggedOut] Whether user is logged out.
|
|
|
|
*/
|
2017-11-14 07:58:20 +00:00
|
|
|
constructor(injector: Injector, public id: string, public siteUrl: string, public token?: string, public infos?: any,
|
2017-11-13 12:35:44 +00:00
|
|
|
public privateToken?: string, public config?: any, public loggedOut?: boolean) {
|
|
|
|
// Inject the required services.
|
2018-01-29 09:05:20 +00:00
|
|
|
const logger = injector.get(CoreLoggerProvider);
|
2017-11-13 12:35:44 +00:00
|
|
|
this.appProvider = injector.get(CoreAppProvider);
|
|
|
|
this.dbProvider = injector.get(CoreDbProvider);
|
|
|
|
this.domUtils = injector.get(CoreDomUtilsProvider);
|
|
|
|
this.eventsProvider = injector.get(CoreEventsProvider);
|
|
|
|
this.fileProvider = injector.get(CoreFileProvider);
|
|
|
|
this.http = injector.get(HttpClient);
|
|
|
|
this.textUtils = injector.get(CoreTextUtilsProvider);
|
|
|
|
this.timeUtils = injector.get(CoreTimeUtilsProvider);
|
|
|
|
this.translate = injector.get(TranslateService);
|
|
|
|
this.utils = injector.get(CoreUtilsProvider);
|
|
|
|
this.urlUtils = injector.get(CoreUrlUtilsProvider);
|
|
|
|
this.wsProvider = injector.get(CoreWSProvider);
|
|
|
|
|
|
|
|
this.logger = logger.getInstance('CoreWSProvider');
|
|
|
|
|
|
|
|
if (this.id) {
|
|
|
|
this.initDB();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initialize the database.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
initDB(): void {
|
2017-11-13 12:35:44 +00:00
|
|
|
this.db = this.dbProvider.getDB('Site-' + this.id);
|
|
|
|
this.db.createTableFromSchema(this.tableSchema);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get site ID.
|
|
|
|
*
|
|
|
|
* @return {string} Site ID.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
getId(): string {
|
2017-11-13 12:35:44 +00:00
|
|
|
return this.id;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get site URL.
|
|
|
|
*
|
|
|
|
* @return {string} Site URL.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
getURL(): string {
|
2017-11-13 12:35:44 +00:00
|
|
|
return this.siteUrl;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get site token.
|
|
|
|
*
|
|
|
|
* @return {string} Site token.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
getToken(): string {
|
2017-11-13 12:35:44 +00:00
|
|
|
return this.token;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get site info.
|
|
|
|
*
|
|
|
|
* @return {any} Site info.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
getInfo(): any {
|
2017-11-13 12:35:44 +00:00
|
|
|
return this.infos;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get site private token.
|
|
|
|
*
|
|
|
|
* @return {string} Site private token.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
getPrivateToken(): string {
|
2017-11-13 12:35:44 +00:00
|
|
|
return this.privateToken;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get site DB.
|
|
|
|
*
|
|
|
|
* @return {SQLiteDB} Site DB.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
getDb(): SQLiteDB {
|
2017-11-13 12:35:44 +00:00
|
|
|
return this.db;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get site user's ID.
|
|
|
|
*
|
|
|
|
* @return {number} User's ID.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
getUserId(): number {
|
2017-11-13 12:35:44 +00:00
|
|
|
if (typeof this.infos != 'undefined' && typeof this.infos.userid != 'undefined') {
|
|
|
|
return this.infos.userid;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get site Course ID for frontpage course. If not declared it will return 1 as default.
|
|
|
|
*
|
|
|
|
* @return {number} Site Home ID.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
getSiteHomeId(): number {
|
2017-11-13 12:35:44 +00:00
|
|
|
return this.infos && this.infos.siteid || 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set site ID.
|
|
|
|
*
|
|
|
|
* @param {string} New ID.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
setId(id: string): void {
|
2017-11-13 12:35:44 +00:00
|
|
|
this.id = id;
|
|
|
|
this.initDB();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set site token.
|
|
|
|
*
|
|
|
|
* @param {string} New token.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
setToken(token: string): void {
|
2017-11-13 12:35:44 +00:00
|
|
|
this.token = token;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set site private token.
|
|
|
|
*
|
|
|
|
* @param {string} privateToken New private token.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
setPrivateToken(privateToken: string): void {
|
2017-11-13 12:35:44 +00:00
|
|
|
this.privateToken = privateToken;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if user logged out from the site and needs to authenticate again.
|
|
|
|
*
|
|
|
|
* @return {boolean} Whether is logged out.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
isLoggedOut(): boolean {
|
2017-11-13 12:35:44 +00:00
|
|
|
return !!this.loggedOut;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set site info.
|
|
|
|
*
|
|
|
|
* @param {any} New info.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
setInfo(infos: any): void {
|
2017-11-13 12:35:44 +00:00
|
|
|
this.infos = infos;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set site config.
|
|
|
|
*
|
|
|
|
* @param {any} Config.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
setConfig(config: any): void {
|
2017-11-13 12:35:44 +00:00
|
|
|
this.config = config;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set site logged out.
|
|
|
|
*
|
|
|
|
* @param {boolean} loggedOut True if logged out and needs to authenticate again, false otherwise.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
setLoggedOut(loggedOut: boolean): void {
|
2017-11-13 12:35:44 +00:00
|
|
|
this.loggedOut = !!loggedOut;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Can the user access their private files?
|
|
|
|
*
|
|
|
|
* @return {boolean} Whether can access my files.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
canAccessMyFiles(): boolean {
|
2017-11-13 12:35:44 +00:00
|
|
|
const infos = this.getInfo();
|
2018-01-29 09:05:20 +00:00
|
|
|
|
2017-11-13 12:35:44 +00:00
|
|
|
return infos && (typeof infos.usercanmanageownfiles === 'undefined' || infos.usercanmanageownfiles);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Can the user download files?
|
|
|
|
*
|
|
|
|
* @return {boolean} Whether can download files.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
canDownloadFiles(): boolean {
|
2017-11-13 12:35:44 +00:00
|
|
|
const infos = this.getInfo();
|
2018-01-29 09:05:20 +00:00
|
|
|
|
2017-11-13 12:35:44 +00:00
|
|
|
return infos && infos.downloadfiles;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Can the user use an advanced feature?
|
|
|
|
*
|
|
|
|
* @param {string} feature The name of the feature.
|
|
|
|
* @param {boolean} [whenUndefined=true] The value to return when the parameter is undefined.
|
|
|
|
* @return {boolean} Whether can use advanced feature.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
canUseAdvancedFeature(feature: string, whenUndefined: boolean = true): boolean {
|
|
|
|
const infos = this.getInfo();
|
|
|
|
let canUse = true;
|
2017-11-13 12:35:44 +00:00
|
|
|
|
|
|
|
if (typeof infos.advancedfeatures === 'undefined') {
|
|
|
|
canUse = whenUndefined;
|
|
|
|
} else {
|
2018-01-29 09:05:20 +00:00
|
|
|
for (const i in infos.advancedfeatures) {
|
|
|
|
const item = infos.advancedfeatures[i];
|
2017-11-13 12:35:44 +00:00
|
|
|
if (item.name === feature && parseInt(item.value, 10) === 0) {
|
|
|
|
canUse = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return canUse;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Can the user upload files?
|
|
|
|
*
|
|
|
|
* @return {boolean} Whether can upload files.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
canUploadFiles(): boolean {
|
2017-11-13 12:35:44 +00:00
|
|
|
const infos = this.getInfo();
|
2018-01-29 09:05:20 +00:00
|
|
|
|
2017-11-13 12:35:44 +00:00
|
|
|
return infos && infos.uploadfiles;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fetch site info from the Moodle site.
|
|
|
|
*
|
|
|
|
* @return {Promise<any>} A promise to be resolved when the site info is retrieved.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
fetchSiteInfo(): Promise<any> {
|
|
|
|
// The get_site_info WS call won't be cached.
|
|
|
|
const preSets = {
|
2017-11-13 12:35:44 +00:00
|
|
|
getFromCache: false,
|
|
|
|
saveToCache: false
|
2018-01-29 09:05:20 +00:00
|
|
|
};
|
2017-11-13 12:35:44 +00:00
|
|
|
|
|
|
|
// Reset clean Unicode to check if it's supported again.
|
|
|
|
this.cleanUnicode = false;
|
|
|
|
|
|
|
|
return this.read('core_webservice_get_site_info', {}, preSets);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Read some data from the Moodle site using WS. Requests are cached by default.
|
|
|
|
*
|
|
|
|
* @param {string} method WS method to use.
|
|
|
|
* @param {any} data Data to send to the WS.
|
|
|
|
* @param {CoreSiteWSPreSets} [preSets] Extra options.
|
|
|
|
* @return {Promise<any>} Promise resolved with the response, rejected with CoreWSError if it fails.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
read(method: string, data: any, preSets?: CoreSiteWSPreSets): Promise<any> {
|
2017-11-13 12:35:44 +00:00
|
|
|
preSets = preSets || {};
|
|
|
|
if (typeof preSets.getFromCache == 'undefined') {
|
|
|
|
preSets.getFromCache = true;
|
|
|
|
}
|
|
|
|
if (typeof preSets.saveToCache == 'undefined') {
|
|
|
|
preSets.saveToCache = true;
|
|
|
|
}
|
2018-01-29 09:05:20 +00:00
|
|
|
|
2017-11-13 12:35:44 +00:00
|
|
|
return this.request(method, data, preSets);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sends some data to the Moodle site using WS. Requests are NOT cached by default.
|
|
|
|
*
|
|
|
|
* @param {string} method WS method to use.
|
|
|
|
* @param {any} data Data to send to the WS.
|
|
|
|
* @param {CoreSiteWSPreSets} [preSets] Extra options.
|
|
|
|
* @return {Promise<any>} Promise resolved with the response, rejected with CoreWSError if it fails.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
write(method: string, data: any, preSets?: CoreSiteWSPreSets): Promise<any> {
|
2017-11-13 12:35:44 +00:00
|
|
|
preSets = preSets || {};
|
|
|
|
if (typeof preSets.getFromCache == 'undefined') {
|
|
|
|
preSets.getFromCache = false;
|
|
|
|
}
|
|
|
|
if (typeof preSets.saveToCache == 'undefined') {
|
|
|
|
preSets.saveToCache = false;
|
|
|
|
}
|
|
|
|
if (typeof preSets.emergencyCache == 'undefined') {
|
|
|
|
preSets.emergencyCache = false;
|
|
|
|
}
|
2018-01-29 09:05:20 +00:00
|
|
|
|
2017-11-13 12:35:44 +00:00
|
|
|
return this.request(method, data, preSets);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* WS request to the site.
|
|
|
|
*
|
|
|
|
* @param {string} method The WebService method to be called.
|
|
|
|
* @param {any} data Arguments to pass to the method.
|
|
|
|
* @param {CoreSiteWSPreSets} preSets Extra options.
|
|
|
|
* @param {boolean} [retrying] True if we're retrying the call for some reason. This is to prevent infinite loops.
|
|
|
|
* @return {Promise<any>} Promise resolved with the response, rejected with CoreWSError if it fails.
|
|
|
|
* @description
|
|
|
|
*
|
|
|
|
* Sends a webservice request to the site. This method will automatically add the
|
|
|
|
* required parameters and pass it on to the low level API in CoreWSProvider.call().
|
|
|
|
*
|
|
|
|
* Caching is also implemented, when enabled this method will returned a cached version of the request if the
|
|
|
|
* data hasn't expired.
|
|
|
|
*
|
|
|
|
* This method is smart which means that it will try to map the method to a compatibility one if need be, usually this
|
|
|
|
* means that it will fallback on the 'local_mobile_' prefixed function if it is available and the non-prefixed is not.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
request(method: string, data: any, preSets: CoreSiteWSPreSets, retrying?: boolean): Promise<any> {
|
|
|
|
const initialToken = this.token;
|
2017-11-13 12:35:44 +00:00
|
|
|
data = data || {};
|
|
|
|
|
|
|
|
// Check if the method is available, use a prefixed version if possible.
|
|
|
|
// We ignore this check when we do not have the site info, as the list of functions is not loaded yet.
|
|
|
|
if (this.getInfo() && !this.wsAvailable(method, false)) {
|
2018-01-12 13:28:46 +00:00
|
|
|
const compatibilityMethod = CoreConstants.WS_PREFIX + method;
|
2017-11-13 12:35:44 +00:00
|
|
|
if (this.wsAvailable(compatibilityMethod, false)) {
|
|
|
|
this.logger.info(`Using compatibility WS method '${compatibilityMethod}'`);
|
|
|
|
method = compatibilityMethod;
|
|
|
|
} else {
|
|
|
|
this.logger.error(`WS function '${method}' is not available, even in compatibility mode.`);
|
2018-01-29 09:05:20 +00:00
|
|
|
|
2018-03-08 12:25:20 +00:00
|
|
|
return Promise.reject(this.utils.createFakeWSError('core.wsfunctionnotavailable', true));
|
2017-11-13 12:35:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-29 09:05:20 +00:00
|
|
|
const wsPreSets: CoreWSPreSets = {
|
2017-11-13 12:35:44 +00:00
|
|
|
wsToken: this.token,
|
|
|
|
siteUrl: this.siteUrl,
|
|
|
|
cleanUnicode: this.cleanUnicode,
|
|
|
|
typeExpected: preSets.typeExpected,
|
|
|
|
responseExpected: preSets.responseExpected
|
|
|
|
};
|
|
|
|
|
|
|
|
if (wsPreSets.cleanUnicode && this.textUtils.hasUnicodeData(data)) {
|
|
|
|
// Data will be cleaned, notify the user.
|
2017-12-08 14:53:27 +00:00
|
|
|
this.domUtils.showToast('core.unicodenotsupported', true, 3000);
|
2017-11-13 12:35:44 +00:00
|
|
|
} else {
|
|
|
|
// No need to clean data in this call.
|
|
|
|
wsPreSets.cleanUnicode = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Enable text filtering by default.
|
|
|
|
data.moodlewssettingfilter = preSets.filter === false ? false : true;
|
|
|
|
data.moodlewssettingfileurl = preSets.rewriteurls === false ? false : true;
|
|
|
|
|
|
|
|
// Convert the values to string before starting the cache process.
|
|
|
|
try {
|
|
|
|
data = this.wsProvider.convertValuesToString(data, wsPreSets.cleanUnicode);
|
|
|
|
} catch (e) {
|
|
|
|
// Empty cleaned text found.
|
2018-03-08 12:25:20 +00:00
|
|
|
return Promise.reject(this.utils.createFakeWSError('core.unicodenotsupportedcleanerror', true));
|
2017-11-13 12:35:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return this.getFromCache(method, data, preSets).catch(() => {
|
|
|
|
// Do not pass those options to the core WS factory.
|
|
|
|
return this.wsProvider.call(method, data, wsPreSets).then((response) => {
|
|
|
|
if (preSets.saveToCache) {
|
|
|
|
this.saveToCache(method, data, response, preSets);
|
|
|
|
}
|
|
|
|
|
2018-01-29 09:05:20 +00:00
|
|
|
// We pass back a clone of the original object, this may prevent errors if in the callback the object is modified.
|
2018-01-03 12:11:05 +00:00
|
|
|
return this.utils.clone(response);
|
2017-11-13 12:35:44 +00:00
|
|
|
}).catch((error) => {
|
|
|
|
if (error.errorcode == 'invalidtoken' ||
|
2018-01-29 09:05:20 +00:00
|
|
|
(error.errorcode == 'accessexception' && error.message.indexOf('Invalid token - token expired') > -1)) {
|
2017-11-13 12:35:44 +00:00
|
|
|
if (initialToken !== this.token && !retrying) {
|
|
|
|
// Token has changed, retry with the new token.
|
|
|
|
return this.request(method, data, preSets, true);
|
|
|
|
} else if (this.appProvider.isSSOAuthenticationOngoing()) {
|
|
|
|
// There's an SSO authentication ongoing, wait for it to finish and try again.
|
|
|
|
return this.appProvider.waitForSSOAuthentication().then(() => {
|
|
|
|
return this.request(method, data, preSets, true);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// Session expired, trigger event.
|
2017-12-28 12:25:09 +00:00
|
|
|
this.eventsProvider.trigger(CoreEventsProvider.SESSION_EXPIRED, {}, this.id);
|
2017-11-13 12:35:44 +00:00
|
|
|
// Change error message. We'll try to get data from cache.
|
2017-12-08 14:53:27 +00:00
|
|
|
error.message = this.translate.instant('core.lostconnection');
|
2017-11-13 12:35:44 +00:00
|
|
|
} else if (error.errorcode === 'userdeleted') {
|
|
|
|
// User deleted, trigger event.
|
2018-01-29 09:05:20 +00:00
|
|
|
this.eventsProvider.trigger(CoreEventsProvider.USER_DELETED, { params: data }, this.id);
|
2017-12-08 14:53:27 +00:00
|
|
|
error.message = this.translate.instant('core.userdeleted');
|
2018-01-29 09:05:20 +00:00
|
|
|
|
2017-11-13 12:35:44 +00:00
|
|
|
return Promise.reject(error);
|
|
|
|
} else if (error.errorcode === 'forcepasswordchangenotice') {
|
|
|
|
// Password Change Forced, trigger event.
|
2017-12-28 12:25:09 +00:00
|
|
|
this.eventsProvider.trigger(CoreEventsProvider.PASSWORD_CHANGE_FORCED, {}, this.id);
|
2017-12-08 14:53:27 +00:00
|
|
|
error.message = this.translate.instant('core.forcepasswordchangenotice');
|
2018-01-29 09:05:20 +00:00
|
|
|
|
2017-11-13 12:35:44 +00:00
|
|
|
return Promise.reject(error);
|
|
|
|
} else if (error.errorcode === 'usernotfullysetup') {
|
|
|
|
// User not fully setup, trigger event.
|
2017-12-28 12:25:09 +00:00
|
|
|
this.eventsProvider.trigger(CoreEventsProvider.USER_NOT_FULLY_SETUP, {}, this.id);
|
2017-12-08 14:53:27 +00:00
|
|
|
error.message = this.translate.instant('core.usernotfullysetup');
|
2018-01-29 09:05:20 +00:00
|
|
|
|
2017-11-13 12:35:44 +00:00
|
|
|
return Promise.reject(error);
|
|
|
|
} else if (error.errorcode === 'sitepolicynotagreed') {
|
|
|
|
// Site policy not agreed, trigger event.
|
2017-12-28 12:25:09 +00:00
|
|
|
this.eventsProvider.trigger(CoreEventsProvider.SITE_POLICY_NOT_AGREED, {}, this.id);
|
2017-12-08 14:53:27 +00:00
|
|
|
error.message = this.translate.instant('core.sitepolicynotagreederror');
|
2018-01-29 09:05:20 +00:00
|
|
|
|
2017-11-13 12:35:44 +00:00
|
|
|
return Promise.reject(error);
|
|
|
|
} else if (error.errorcode === 'dmlwriteexception' && this.textUtils.hasUnicodeData(data)) {
|
|
|
|
if (!this.cleanUnicode) {
|
|
|
|
// Try again cleaning unicode.
|
|
|
|
this.cleanUnicode = true;
|
2018-01-29 09:05:20 +00:00
|
|
|
|
2017-11-13 12:35:44 +00:00
|
|
|
return this.request(method, data, preSets);
|
|
|
|
}
|
|
|
|
// This should not happen.
|
2017-12-08 14:53:27 +00:00
|
|
|
error.message = this.translate.instant('core.unicodenotsupported');
|
2018-01-29 09:05:20 +00:00
|
|
|
|
2017-11-13 12:35:44 +00:00
|
|
|
return Promise.reject(error);
|
|
|
|
} else if (typeof preSets.emergencyCache !== 'undefined' && !preSets.emergencyCache) {
|
|
|
|
this.logger.debug(`WS call '${method}' failed. Emergency cache is forbidden, rejecting.`);
|
2018-01-29 09:05:20 +00:00
|
|
|
|
2017-11-13 12:35:44 +00:00
|
|
|
return Promise.reject(error);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.logger.debug(`WS call '${method}' failed. Trying to use the emergency cache.`);
|
|
|
|
preSets.omitExpires = true;
|
|
|
|
preSets.getFromCache = true;
|
2018-01-29 09:05:20 +00:00
|
|
|
|
2017-11-13 12:35:44 +00:00
|
|
|
return this.getFromCache(method, data, preSets, true).catch(() => {
|
|
|
|
return Promise.reject(error);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if a WS is available in this site.
|
|
|
|
*
|
|
|
|
* @param {string} method WS name.
|
|
|
|
* @param {boolean} [checkPrefix=true] When true also checks with the compatibility prefix.
|
|
|
|
* @return {boolean} Whether the WS is available.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
wsAvailable(method: string, checkPrefix: boolean = true): boolean {
|
2017-11-13 12:35:44 +00:00
|
|
|
if (typeof this.infos == 'undefined') {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (let i = 0; i < this.infos.functions.length; i++) {
|
|
|
|
const func = this.infos.functions[i];
|
|
|
|
if (func.name == method) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Let's try again with the compatibility prefix.
|
|
|
|
if (checkPrefix) {
|
2018-01-12 13:28:46 +00:00
|
|
|
return this.wsAvailable(CoreConstants.WS_PREFIX + method, false);
|
2017-11-13 12:35:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get cache ID.
|
|
|
|
*
|
|
|
|
* @param {string} method The WebService method.
|
|
|
|
* @param {any} data Arguments to pass to the method.
|
|
|
|
* @return {string} Cache ID.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
protected getCacheId(method: string, data: any): string {
|
|
|
|
return <string> Md5.hashAsciiStr(method + ':' + this.utils.sortAndStringify(data));
|
2017-11-13 12:35:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get a WS response from cache.
|
|
|
|
*
|
|
|
|
* @param {string} method The WebService method to be called.
|
|
|
|
* @param {any} data Arguments to pass to the method.
|
|
|
|
* @param {CoreSiteWSPreSets} preSets Extra options.
|
|
|
|
* @param {boolean} emergency Whether it's an "emergency" cache call (WS call failed).
|
|
|
|
* @return {Promise<any>} Promise resolved with the WS response.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
protected getFromCache(method: string, data: any, preSets: CoreSiteWSPreSets, emergency?: boolean): Promise<any> {
|
|
|
|
const id = this.getCacheId(method, data);
|
|
|
|
let promise;
|
2017-11-13 12:35:44 +00:00
|
|
|
|
|
|
|
if (!this.db || !preSets.getFromCache) {
|
|
|
|
return Promise.reject(null);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (preSets.getCacheUsingCacheKey || (emergency && preSets.getEmergencyCacheUsingCacheKey)) {
|
2018-01-29 09:05:20 +00:00
|
|
|
promise = this.db.getRecords(this.WS_CACHE_TABLE, { key: preSets.cacheKey }).then((entries) => {
|
2017-11-13 12:35:44 +00:00
|
|
|
if (!entries.length) {
|
|
|
|
// Cache key not found, get by params sent.
|
2018-01-29 09:05:20 +00:00
|
|
|
return this.db.getRecord(this.WS_CACHE_TABLE, { id: id });
|
2017-11-13 12:35:44 +00:00
|
|
|
} else if (entries.length > 1) {
|
|
|
|
// More than one entry found. Search the one with same ID as this call.
|
|
|
|
for (let i = 0, len = entries.length; i < len; i++) {
|
2018-01-29 09:05:20 +00:00
|
|
|
const entry = entries[i];
|
2017-11-13 12:35:44 +00:00
|
|
|
if (entry.id == id) {
|
|
|
|
return entry;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-01-29 09:05:20 +00:00
|
|
|
|
2017-11-13 12:35:44 +00:00
|
|
|
return entries[0];
|
|
|
|
});
|
|
|
|
} else {
|
2018-01-29 09:05:20 +00:00
|
|
|
promise = this.db.getRecord(this.WS_CACHE_TABLE, { id: id });
|
2017-11-13 12:35:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return promise.then((entry) => {
|
|
|
|
const now = Date.now();
|
|
|
|
|
|
|
|
preSets.omitExpires = preSets.omitExpires || !this.appProvider.isOnline();
|
|
|
|
|
|
|
|
if (!preSets.omitExpires) {
|
|
|
|
if (now > entry.expirationTime) {
|
|
|
|
this.logger.debug('Cached element found, but it is expired');
|
2018-01-29 09:05:20 +00:00
|
|
|
|
2017-11-13 12:35:44 +00:00
|
|
|
return Promise.reject(null);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (typeof entry != 'undefined' && typeof entry.data != 'undefined') {
|
|
|
|
const expires = (entry.expirationTime - now) / 1000;
|
|
|
|
this.logger.info(`Cached element found, id: ${id} expires in ${expires} seconds`);
|
2018-01-29 09:05:20 +00:00
|
|
|
|
2017-11-13 12:35:44 +00:00
|
|
|
return JSON.parse(entry.data);
|
|
|
|
}
|
|
|
|
|
|
|
|
return Promise.reject(null);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Save a WS response to cache.
|
|
|
|
*
|
|
|
|
* @param {string} method The WebService method.
|
|
|
|
* @param {any} data Arguments to pass to the method.
|
|
|
|
* @param {any} response The WS response.
|
|
|
|
* @param {CoreSiteWSPreSets} preSets Extra options.
|
|
|
|
* @return {Promise<any>} Promise resolved when the response is saved.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
protected saveToCache(method: string, data: any, response: any, preSets: CoreSiteWSPreSets): Promise<any> {
|
|
|
|
const id = this.getCacheId(method, data),
|
2017-11-13 12:35:44 +00:00
|
|
|
entry: any = {
|
|
|
|
id: id,
|
|
|
|
data: JSON.stringify(response)
|
2018-01-29 09:05:20 +00:00
|
|
|
};
|
|
|
|
let cacheExpirationTime = CoreConfigConstants.cache_expiration_time,
|
|
|
|
promise;
|
2017-11-13 12:35:44 +00:00
|
|
|
|
|
|
|
if (!this.db) {
|
|
|
|
return Promise.reject(null);
|
|
|
|
} else {
|
|
|
|
if (preSets.uniqueCacheKey) {
|
|
|
|
// Cache key must be unique, delete all entries with same cache key.
|
|
|
|
promise = this.deleteFromCache(method, data, preSets, true).catch(() => {
|
|
|
|
// Ignore errors.
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
promise = Promise.resolve();
|
|
|
|
}
|
|
|
|
|
|
|
|
return promise.then(() => {
|
|
|
|
cacheExpirationTime = isNaN(cacheExpirationTime) ? 300000 : cacheExpirationTime;
|
|
|
|
entry.expirationTime = new Date().getTime() + cacheExpirationTime;
|
|
|
|
if (preSets.cacheKey) {
|
|
|
|
entry.key = preSets.cacheKey;
|
|
|
|
}
|
2018-01-29 09:05:20 +00:00
|
|
|
|
|
|
|
return this.db.insertOrUpdateRecord(this.WS_CACHE_TABLE, entry, { id: id });
|
2017-11-13 12:35:44 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Delete a WS cache entry or entries.
|
|
|
|
*
|
|
|
|
* @param {string} method The WebService method to be called.
|
|
|
|
* @param {any} data Arguments to pass to the method.
|
|
|
|
* @param {CoreSiteWSPreSets} preSets Extra options.
|
|
|
|
* @param {boolean} [allCacheKey] True to delete all entries with the cache key, false to delete only by ID.
|
|
|
|
* @return {Promise<any>} Promise resolved when the entries are deleted.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
protected deleteFromCache(method: string, data: any, preSets: CoreSiteWSPreSets, allCacheKey?: boolean): Promise<any> {
|
2017-11-13 12:35:44 +00:00
|
|
|
const id = this.getCacheId(method, data);
|
|
|
|
|
|
|
|
if (!this.db) {
|
|
|
|
return Promise.reject(null);
|
|
|
|
} else {
|
|
|
|
if (allCacheKey) {
|
2018-01-29 09:05:20 +00:00
|
|
|
return this.db.deleteRecords(this.WS_CACHE_TABLE, { key: preSets.cacheKey });
|
2017-11-13 12:35:44 +00:00
|
|
|
} else {
|
2018-01-29 09:05:20 +00:00
|
|
|
return this.db.deleteRecords(this.WS_CACHE_TABLE, { id: id });
|
2017-11-13 12:35:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Uploads a file using Cordova File API.
|
|
|
|
*
|
|
|
|
* @param {string} filePath File path.
|
|
|
|
* @param {CoreWSFileUploadOptions} options File upload options.
|
2018-01-09 07:50:08 +00:00
|
|
|
* @param {Function} [onProgress] Function to call on progress.
|
2017-11-13 12:35:44 +00:00
|
|
|
* @return {Promise<any>} Promise resolved when uploaded.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
uploadFile(filePath: string, options: CoreWSFileUploadOptions, onProgress?: (event: ProgressEvent) => any): Promise<any> {
|
2017-11-13 12:35:44 +00:00
|
|
|
if (!options.fileArea) {
|
|
|
|
options.fileArea = 'draft';
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.wsProvider.uploadFile(filePath, options, {
|
|
|
|
siteUrl: this.siteUrl,
|
|
|
|
wsToken: this.token
|
2018-01-09 07:50:08 +00:00
|
|
|
}, onProgress);
|
2017-11-13 12:35:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Invalidates all the cache entries.
|
|
|
|
*
|
|
|
|
* @return {Promise<any>} Promise resolved when the cache entries are invalidated.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
invalidateWsCache(): Promise<any> {
|
2017-11-13 12:35:44 +00:00
|
|
|
if (!this.db) {
|
|
|
|
return Promise.reject(null);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.logger.debug('Invalidate all the cache for site: ' + this.id);
|
2018-01-29 09:05:20 +00:00
|
|
|
|
|
|
|
return this.db.updateRecords(this.WS_CACHE_TABLE, { expirationTime: 0 });
|
2017-11-13 12:35:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Invalidates all the cache entries with a certain key.
|
|
|
|
*
|
|
|
|
* @param {string} key Key to search.
|
|
|
|
* @return {Promise<any>} Promise resolved when the cache entries are invalidated.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
invalidateWsCacheForKey(key: string): Promise<any> {
|
2017-11-13 12:35:44 +00:00
|
|
|
if (!this.db) {
|
|
|
|
return Promise.reject(null);
|
|
|
|
}
|
|
|
|
if (!key) {
|
|
|
|
return Promise.resolve();
|
|
|
|
}
|
|
|
|
|
|
|
|
this.logger.debug('Invalidate cache for key: ' + key);
|
2018-01-29 09:05:20 +00:00
|
|
|
|
|
|
|
return this.db.updateRecords(this.WS_CACHE_TABLE, { expirationTime: 0 }, { key: key });
|
2017-11-13 12:35:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Invalidates all the cache entries in an array of keys.
|
|
|
|
*
|
|
|
|
* @param {string[]} keys Keys to search.
|
|
|
|
* @return {Promise<any>} Promise resolved when the cache entries are invalidated.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
invalidateMultipleWsCacheForKey(keys: string[]): Promise<any> {
|
2017-11-13 12:35:44 +00:00
|
|
|
if (!this.db) {
|
|
|
|
return Promise.reject(null);
|
|
|
|
}
|
|
|
|
if (!keys || !keys.length) {
|
|
|
|
return Promise.resolve();
|
|
|
|
}
|
|
|
|
|
2018-01-29 09:05:20 +00:00
|
|
|
const promises = [];
|
2017-11-13 12:35:44 +00:00
|
|
|
|
|
|
|
this.logger.debug('Invalidating multiple cache keys');
|
|
|
|
keys.forEach((key) => {
|
|
|
|
promises.push(this.invalidateWsCacheForKey(key));
|
|
|
|
});
|
|
|
|
|
|
|
|
return Promise.all(promises);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Invalidates all the cache entries whose key starts with a certain value.
|
|
|
|
*
|
|
|
|
* @param {string} key Key to search.
|
|
|
|
* @return {Promise} Promise resolved when the cache entries are invalidated.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
invalidateWsCacheForKeyStartingWith(key: string): Promise<any> {
|
2017-11-13 12:35:44 +00:00
|
|
|
if (!this.db) {
|
|
|
|
return Promise.reject(null);
|
|
|
|
}
|
|
|
|
if (!key) {
|
|
|
|
return Promise.resolve();
|
|
|
|
}
|
|
|
|
|
|
|
|
this.logger.debug('Invalidate cache for key starting with: ' + key);
|
2018-01-29 09:05:20 +00:00
|
|
|
|
|
|
|
const sql = 'UPDATE ' + this.WS_CACHE_TABLE + ' SET expirationTime=0 WHERE key LIKE ?';
|
|
|
|
|
|
|
|
return this.db.execute(sql, [key + '%']);
|
2017-11-13 12:35:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generic function for adding the wstoken to Moodle urls and for pointing to the correct script.
|
|
|
|
* Uses CoreUtilsProvider.fixPluginfileURL, passing site's token.
|
|
|
|
*
|
|
|
|
* @param {string} url The url to be fixed.
|
|
|
|
* @return {string} Fixed URL.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
fixPluginfileURL(url: string): string {
|
2017-11-13 12:35:44 +00:00
|
|
|
return this.urlUtils.fixPluginfileURL(url, this.token);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Deletes site's DB.
|
|
|
|
*
|
|
|
|
* @return {Promise<any>} Promise to be resolved when the DB is deleted.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
deleteDB(): Promise<any> {
|
2017-11-13 12:35:44 +00:00
|
|
|
return this.dbProvider.deleteDB('Site-' + this.id);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Deletes site's folder.
|
|
|
|
*
|
|
|
|
* @return {Promise<any>} Promise to be resolved when the DB is deleted.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
deleteFolder(): Promise<any> {
|
2017-11-13 12:35:44 +00:00
|
|
|
if (this.fileProvider.isAvailable()) {
|
|
|
|
const siteFolder = this.fileProvider.getSiteFolder(this.id);
|
2018-01-29 09:05:20 +00:00
|
|
|
|
2017-11-13 12:35:44 +00:00
|
|
|
return this.fileProvider.removeDir(siteFolder).catch(() => {
|
|
|
|
// Ignore any errors, $mmFS.removeDir fails if folder doesn't exists.
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
return Promise.resolve();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get space usage of the site.
|
|
|
|
*
|
|
|
|
* @return {Promise<number>} Promise resolved with the site space usage (size).
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
getSpaceUsage(): Promise<number> {
|
2017-11-13 12:35:44 +00:00
|
|
|
if (this.fileProvider.isAvailable()) {
|
|
|
|
const siteFolderPath = this.fileProvider.getSiteFolder(this.id);
|
2018-01-29 09:05:20 +00:00
|
|
|
|
2017-11-13 12:35:44 +00:00
|
|
|
return this.fileProvider.getDirectorySize(siteFolderPath).catch(() => {
|
|
|
|
return 0;
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
return Promise.resolve(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the URL to the documentation of the app, based on Moodle version and current language.
|
|
|
|
*
|
2017-12-07 12:29:42 +00:00
|
|
|
* @param {string} [page] Docs page to go to.
|
|
|
|
* @return {Promise<string>} Promise resolved with the Moodle docs URL.
|
2017-11-13 12:35:44 +00:00
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
getDocsUrl(page?: string): Promise<string> {
|
2017-11-13 12:35:44 +00:00
|
|
|
const release = this.infos.release ? this.infos.release : undefined;
|
2018-01-29 09:05:20 +00:00
|
|
|
|
2017-11-13 12:35:44 +00:00
|
|
|
return this.urlUtils.getDocsUrl(release, page);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if the local_mobile plugin is installed in the Moodle site.
|
|
|
|
*
|
|
|
|
* @param {boolean} [retrying] True if we're retrying the check.
|
|
|
|
* @return {Promise<LocalMobileResponse>} Promise resolved when the check is done.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
checkLocalMobilePlugin(retrying?: boolean): Promise<LocalMobileResponse> {
|
2017-11-13 12:35:44 +00:00
|
|
|
const checkUrl = this.siteUrl + '/local/mobile/check.php',
|
|
|
|
service = CoreConfigConstants.wsextservice;
|
|
|
|
|
|
|
|
if (!service) {
|
|
|
|
// External service not defined.
|
2018-01-29 09:05:20 +00:00
|
|
|
return Promise.resolve({ code: 0 });
|
2017-11-13 12:35:44 +00:00
|
|
|
}
|
|
|
|
|
2018-01-29 09:05:20 +00:00
|
|
|
const observable = this.http.post(checkUrl, { service: service }).timeout(CoreConstants.WS_TIMEOUT);
|
|
|
|
|
2017-11-13 12:35:44 +00:00
|
|
|
return this.utils.observableToPromise(observable).then((data: any) => {
|
|
|
|
if (typeof data != 'undefined' && data.errorcode === 'requirecorrectaccess') {
|
|
|
|
if (!retrying) {
|
|
|
|
this.siteUrl = this.urlUtils.addOrRemoveWWW(this.siteUrl);
|
2018-01-29 09:05:20 +00:00
|
|
|
|
2017-11-13 12:35:44 +00:00
|
|
|
return this.checkLocalMobilePlugin(true);
|
|
|
|
} else {
|
|
|
|
return Promise.reject(data.error);
|
|
|
|
}
|
2018-01-29 09:05:20 +00:00
|
|
|
} else if (typeof data == 'undefined' || typeof data.code == 'undefined') {
|
|
|
|
// The local_mobile returned something we didn't expect. Let's assume it's not installed.
|
|
|
|
return { code: 0, warning: 'core.login.localmobileunexpectedresponse' };
|
2017-11-13 12:35:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const code = parseInt(data.code, 10);
|
|
|
|
if (data.error) {
|
|
|
|
switch (code) {
|
|
|
|
case 1:
|
|
|
|
// Site in maintenance mode.
|
2017-12-08 14:53:27 +00:00
|
|
|
return Promise.reject(this.translate.instant('core.login.siteinmaintenance'));
|
2017-11-13 12:35:44 +00:00
|
|
|
case 2:
|
|
|
|
// Web services not enabled.
|
2017-12-08 14:53:27 +00:00
|
|
|
return Promise.reject(this.translate.instant('core.login.webservicesnotenabled'));
|
2017-11-13 12:35:44 +00:00
|
|
|
case 3:
|
|
|
|
// Extended service not enabled, but the official is enabled.
|
2018-01-29 09:05:20 +00:00
|
|
|
return { code: 0 };
|
2017-11-13 12:35:44 +00:00
|
|
|
case 4:
|
|
|
|
// Neither extended or official services enabled.
|
2017-12-08 14:53:27 +00:00
|
|
|
return Promise.reject(this.translate.instant('core.login.mobileservicesnotenabled'));
|
2017-11-13 12:35:44 +00:00
|
|
|
default:
|
2017-12-08 14:53:27 +00:00
|
|
|
return Promise.reject(this.translate.instant('core.unexpectederror'));
|
2017-11-13 12:35:44 +00:00
|
|
|
}
|
|
|
|
} else {
|
2018-01-29 09:05:20 +00:00
|
|
|
return { code: code, service: service, coresupported: !!data.coresupported };
|
2017-11-13 12:35:44 +00:00
|
|
|
}
|
|
|
|
}, () => {
|
2018-01-29 09:05:20 +00:00
|
|
|
return { code: 0 };
|
2017-11-13 12:35:44 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if local_mobile has been installed in Moodle.
|
|
|
|
*
|
|
|
|
* @return {boolean} Whether the App is able to use local_mobile plugin for this site.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
checkIfAppUsesLocalMobile(): boolean {
|
2017-11-13 12:35:44 +00:00
|
|
|
let appUsesLocalMobile = false;
|
|
|
|
|
|
|
|
if (!this.infos || !this.infos.functions) {
|
|
|
|
return appUsesLocalMobile;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.infos.functions.forEach((func) => {
|
2018-01-12 13:28:46 +00:00
|
|
|
if (func.name.indexOf(CoreConstants.WS_PREFIX) != -1) {
|
2017-11-13 12:35:44 +00:00
|
|
|
appUsesLocalMobile = true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return appUsesLocalMobile;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if local_mobile has been installed in Moodle but the app is not using it.
|
|
|
|
*
|
|
|
|
* @return {Promise<any>} Promise resolved it local_mobile was added, rejected otherwise.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
checkIfLocalMobileInstalledAndNotUsed(): Promise<any> {
|
2017-11-13 12:35:44 +00:00
|
|
|
const appUsesLocalMobile = this.checkIfAppUsesLocalMobile();
|
|
|
|
|
|
|
|
if (appUsesLocalMobile) {
|
|
|
|
// App already uses local_mobile, it wasn't added.
|
|
|
|
return Promise.reject(null);
|
|
|
|
}
|
|
|
|
|
2018-01-29 09:05:20 +00:00
|
|
|
return this.checkLocalMobilePlugin().then((data: LocalMobileResponse): any => {
|
2017-11-13 12:35:44 +00:00
|
|
|
if (typeof data.service == 'undefined') {
|
2018-01-29 09:05:20 +00:00
|
|
|
// The local_mobile NOT installed. Reject.
|
2017-11-13 12:35:44 +00:00
|
|
|
return Promise.reject(null);
|
|
|
|
}
|
2018-01-29 09:05:20 +00:00
|
|
|
|
2017-11-13 12:35:44 +00:00
|
|
|
return data;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if a URL belongs to this site.
|
|
|
|
*
|
|
|
|
* @param {string} url URL to check.
|
|
|
|
* @return {boolean} Whether the URL belongs to this site.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
containsUrl(url: string): boolean {
|
2017-11-13 12:35:44 +00:00
|
|
|
if (!url) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const siteUrl = this.urlUtils.removeProtocolAndWWW(this.siteUrl);
|
|
|
|
url = this.urlUtils.removeProtocolAndWWW(url);
|
2018-01-29 09:05:20 +00:00
|
|
|
|
2017-11-13 12:35:44 +00:00
|
|
|
return url.indexOf(siteUrl) == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the public config of this site.
|
|
|
|
*
|
|
|
|
* @return {Promise<any>} Promise resolved with public config. Rejected with an object if error, see CoreWSProvider.callAjax.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
getPublicConfig(): Promise<any> {
|
|
|
|
return this.wsProvider.callAjax('tool_mobile_get_public_config', {}, { siteUrl: this.siteUrl }).then((config) => {
|
2017-11-13 12:35:44 +00:00
|
|
|
// Use the wwwroot returned by the server.
|
|
|
|
if (config.httpswwwroot) {
|
|
|
|
this.siteUrl = config.httpswwwroot;
|
|
|
|
}
|
2018-01-29 09:05:20 +00:00
|
|
|
|
2017-11-13 12:35:44 +00:00
|
|
|
return config;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Open a URL in browser using auto-login in the Moodle site if available.
|
|
|
|
*
|
|
|
|
* @param {string} url The URL to open.
|
|
|
|
* @param {string} [alertMessage] If defined, an alert will be shown before opening the browser.
|
|
|
|
* @return {Promise<any>} Promise resolved when done, rejected otherwise.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
openInBrowserWithAutoLogin(url: string, alertMessage?: string): Promise<any> {
|
2017-11-13 12:35:44 +00:00
|
|
|
return this.openWithAutoLogin(false, url, undefined, alertMessage);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Open a URL in browser using auto-login in the Moodle site if available and the URL belongs to the site.
|
|
|
|
*
|
|
|
|
* @param {string} url The URL to open.
|
|
|
|
* @param {string} [alertMessage] If defined, an alert will be shown before opening the browser.
|
|
|
|
* @return {Promise<any>} Promise resolved when done, rejected otherwise.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
openInBrowserWithAutoLoginIfSameSite(url: string, alertMessage?: string): Promise<any> {
|
2017-11-13 12:35:44 +00:00
|
|
|
return this.openWithAutoLoginIfSameSite(false, url, undefined, alertMessage);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Open a URL in inappbrowser using auto-login in the Moodle site if available.
|
|
|
|
*
|
|
|
|
* @param {string} url The URL to open.
|
|
|
|
* @param {any} [options] Override default options passed to InAppBrowser.
|
|
|
|
* @param {string} [alertMessage] If defined, an alert will be shown before opening the inappbrowser.
|
2017-12-08 07:23:52 +00:00
|
|
|
* @return {Promise<InAppBrowserObject>} Promise resolved when done.
|
2017-11-13 12:35:44 +00:00
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
openInAppWithAutoLogin(url: string, options?: any, alertMessage?: string): Promise<InAppBrowserObject> {
|
2017-11-13 12:35:44 +00:00
|
|
|
return this.openWithAutoLogin(true, url, options, alertMessage);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Open a URL in inappbrowser using auto-login in the Moodle site if available and the URL belongs to the site.
|
|
|
|
*
|
|
|
|
* @param {string} url The URL to open.
|
|
|
|
* @param {object} [options] Override default options passed to inappbrowser.
|
|
|
|
* @param {string} [alertMessage] If defined, an alert will be shown before opening the inappbrowser.
|
2017-12-08 07:23:52 +00:00
|
|
|
* @return {Promise<InAppBrowserObject>} Promise resolved when done.
|
2017-11-13 12:35:44 +00:00
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
openInAppWithAutoLoginIfSameSite(url: string, options?: any, alertMessage?: string): Promise<InAppBrowserObject> {
|
2017-11-13 12:35:44 +00:00
|
|
|
return this.openWithAutoLoginIfSameSite(true, url, options, alertMessage);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Open a URL in browser or InAppBrowser using auto-login in the Moodle site if available.
|
|
|
|
*
|
|
|
|
* @param {boolean} inApp True to open it in InAppBrowser, false to open in browser.
|
|
|
|
* @param {string} url The URL to open.
|
|
|
|
* @param {object} [options] Override default options passed to $cordovaInAppBrowser#open.
|
|
|
|
* @param {string} [alertMessage] If defined, an alert will be shown before opening the browser/inappbrowser.
|
2017-12-08 07:23:52 +00:00
|
|
|
* @return {Promise<InAppBrowserObject>} Promise resolved when done. Resolve param is returned only if inApp=true.
|
2017-11-13 12:35:44 +00:00
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
openWithAutoLogin(inApp: boolean, url: string, options?: any, alertMessage?: string): Promise<InAppBrowserObject> {
|
2017-11-13 12:35:44 +00:00
|
|
|
// Convenience function to open the URL.
|
2018-01-29 09:05:20 +00:00
|
|
|
const open = (url): Promise<any> => {
|
|
|
|
return new Promise<InAppBrowserObject>((resolve, reject): void => {
|
2017-12-08 07:23:52 +00:00
|
|
|
if (modal) {
|
|
|
|
modal.dismiss();
|
|
|
|
}
|
2017-11-13 12:35:44 +00:00
|
|
|
|
2017-12-08 07:23:52 +00:00
|
|
|
if (alertMessage) {
|
2018-01-29 09:05:20 +00:00
|
|
|
const alert = this.domUtils.showAlert(this.translate.instant('core.notice'), alertMessage, undefined, 3000);
|
2017-12-08 07:23:52 +00:00
|
|
|
alert.onDidDismiss(() => {
|
|
|
|
if (inApp) {
|
|
|
|
resolve(this.utils.openInApp(url, options));
|
|
|
|
} else {
|
|
|
|
resolve(this.utils.openInBrowser(url));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} else {
|
2017-11-24 11:31:09 +00:00
|
|
|
if (inApp) {
|
2017-12-08 07:23:52 +00:00
|
|
|
resolve(this.utils.openInApp(url, options));
|
2017-11-24 11:31:09 +00:00
|
|
|
} else {
|
2017-12-08 07:23:52 +00:00
|
|
|
resolve(this.utils.openInBrowser(url));
|
2017-11-24 11:31:09 +00:00
|
|
|
}
|
2017-11-13 12:35:44 +00:00
|
|
|
}
|
2017-12-08 07:23:52 +00:00
|
|
|
});
|
2017-11-13 12:35:44 +00:00
|
|
|
};
|
|
|
|
|
2018-01-29 09:05:20 +00:00
|
|
|
if (!this.privateToken || !this.wsAvailable('tool_mobile_get_autologin_key') ||
|
|
|
|
(this.lastAutoLogin && this.timeUtils.timestamp() - this.lastAutoLogin < CoreConstants.SECONDS_MINUTE * 6)) {
|
2017-11-13 12:35:44 +00:00
|
|
|
// No private token, WS not available or last auto-login was less than 6 minutes ago.
|
|
|
|
// Open the final URL without auto-login.
|
2017-12-08 07:23:52 +00:00
|
|
|
return Promise.resolve(open(url));
|
2017-11-13 12:35:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const userId = this.getUserId(),
|
|
|
|
params = {
|
|
|
|
privatetoken: this.privateToken
|
|
|
|
},
|
|
|
|
modal = this.domUtils.showModalLoading();
|
|
|
|
|
|
|
|
// Use write to not use cache.
|
|
|
|
return this.write('tool_mobile_get_autologin_key', params).then((data) => {
|
2018-01-29 09:05:20 +00:00
|
|
|
if (!data.autologinurl || !data.key) {
|
2017-11-13 12:35:44 +00:00
|
|
|
// Not valid data, open the final URL without auto-login.
|
2017-12-08 07:23:52 +00:00
|
|
|
return open(url);
|
2017-11-13 12:35:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
this.lastAutoLogin = this.timeUtils.timestamp();
|
|
|
|
|
2017-12-08 07:23:52 +00:00
|
|
|
return open(data.autologinurl + '?userid=' + userId + '&key=' + data.key + '&urltogo=' + url);
|
2017-11-13 12:35:44 +00:00
|
|
|
}).catch(() => {
|
|
|
|
// Couldn't get autologin key, open the final URL without auto-login.
|
2017-12-08 07:23:52 +00:00
|
|
|
return open(url);
|
2017-11-13 12:35:44 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Open a URL in browser or InAppBrowser using auto-login in the Moodle site if available and the URL belongs to the site.
|
|
|
|
*
|
|
|
|
* @param {boolean} inApp True to open it in InAppBrowser, false to open in browser.
|
|
|
|
* @param {string} url The URL to open.
|
|
|
|
* @param {object} [options] Override default options passed to inappbrowser.
|
|
|
|
* @param {string} [alertMessage] If defined, an alert will be shown before opening the browser/inappbrowser.
|
2017-12-08 07:23:52 +00:00
|
|
|
* @return {Promise<InAppBrowserObject>} Promise resolved when done. Resolve param is returned only if inApp=true.
|
2017-11-13 12:35:44 +00:00
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
openWithAutoLoginIfSameSite(inApp: boolean, url: string, options?: any, alertMessage?: string): Promise<InAppBrowserObject> {
|
2017-11-13 12:35:44 +00:00
|
|
|
if (this.containsUrl(url)) {
|
|
|
|
return this.openWithAutoLogin(inApp, url, options, alertMessage);
|
|
|
|
} else {
|
|
|
|
if (inApp) {
|
|
|
|
this.utils.openInApp(url, options);
|
|
|
|
} else {
|
|
|
|
this.utils.openInBrowser(url);
|
|
|
|
}
|
2018-01-29 09:05:20 +00:00
|
|
|
|
2017-12-08 07:23:52 +00:00
|
|
|
return Promise.resolve(null);
|
2017-11-13 12:35:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the config of this site.
|
|
|
|
* It is recommended to use getStoredConfig instead since it's faster and doesn't use network.
|
|
|
|
*
|
|
|
|
* @param {string} [name] Name of the setting to get. If not set or false, all settings will be returned.
|
|
|
|
* @param {boolean} [ignoreCache] True if it should ignore cached data.
|
|
|
|
* @return {Promise<any>} Promise resolved with site config.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
getConfig(name?: string, ignoreCache?: boolean): Promise<any> {
|
|
|
|
const preSets: CoreSiteWSPreSets = {
|
2017-11-13 12:35:44 +00:00
|
|
|
cacheKey: this.getConfigCacheKey()
|
2018-01-29 09:05:20 +00:00
|
|
|
};
|
2017-11-13 12:35:44 +00:00
|
|
|
|
|
|
|
if (ignoreCache) {
|
|
|
|
preSets.getFromCache = false;
|
|
|
|
preSets.emergencyCache = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.read('tool_mobile_get_config', {}, preSets).then((config) => {
|
|
|
|
if (name) {
|
|
|
|
// Return the requested setting.
|
2018-01-29 09:05:20 +00:00
|
|
|
for (const x in config.settings) {
|
2017-11-13 12:35:44 +00:00
|
|
|
if (config.settings[x].name == name) {
|
|
|
|
return config.settings[x].value;
|
|
|
|
}
|
|
|
|
}
|
2018-01-29 09:05:20 +00:00
|
|
|
|
2017-11-13 12:35:44 +00:00
|
|
|
return Promise.reject(null);
|
|
|
|
} else {
|
|
|
|
// Return all settings in the same array.
|
2018-01-29 09:05:20 +00:00
|
|
|
const settings = {};
|
2017-11-13 12:35:44 +00:00
|
|
|
config.settings.forEach((setting) => {
|
|
|
|
settings[setting.name] = setting.value;
|
|
|
|
});
|
2018-01-29 09:05:20 +00:00
|
|
|
|
2017-11-13 12:35:44 +00:00
|
|
|
return settings;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Invalidates config WS call.
|
|
|
|
*
|
|
|
|
* @return {Promise<any>} Promise resolved when the data is invalidated.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
invalidateConfig(): Promise<any> {
|
2017-11-13 12:35:44 +00:00
|
|
|
return this.invalidateWsCacheForKey(this.getConfigCacheKey());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get cache key for getConfig WS calls.
|
|
|
|
*
|
|
|
|
* @return {string} Cache key.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
protected getConfigCacheKey(): string {
|
2017-11-13 12:35:44 +00:00
|
|
|
return 'tool_mobile_get_config';
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the stored config of this site.
|
|
|
|
*
|
|
|
|
* @param {string} [name] Name of the setting to get. If not set, all settings will be returned.
|
|
|
|
* @return {any} Site config or a specific setting.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
getStoredConfig(name?: string): any {
|
2017-11-13 12:35:44 +00:00
|
|
|
if (!this.config) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (name) {
|
|
|
|
return this.config[name];
|
|
|
|
} else {
|
|
|
|
return this.config;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if a certain feature is disabled in the site.
|
|
|
|
*
|
|
|
|
* @param {string} name Name of the feature to check.
|
|
|
|
* @return {boolean} Whether it's disabled.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
isFeatureDisabled(name: string): boolean {
|
2017-11-13 12:35:44 +00:00
|
|
|
const disabledFeatures = this.getStoredConfig('tool_mobile_disabledfeatures');
|
|
|
|
if (!disabledFeatures) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const regEx = new RegExp('(,|^)' + this.textUtils.escapeForRegex(name) + '(,|$)', 'g');
|
2018-01-29 09:05:20 +00:00
|
|
|
|
2017-11-13 12:35:44 +00:00
|
|
|
return !!disabledFeatures.match(regEx);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if the site version is greater than one or several versions.
|
|
|
|
* This function accepts a string or an array of strings. If array, the last version must be the highest.
|
|
|
|
*
|
|
|
|
* @param {string | string[]} versions Version or list of versions to check.
|
|
|
|
* @return {boolean} Whether it's greater or equal, false otherwise.
|
|
|
|
* @description
|
|
|
|
* If a string is supplied (e.g. '3.2.1'), it will check if the site version is greater or equal than this version.
|
|
|
|
*
|
|
|
|
* If an array of versions is supplied, it will check if the site version is greater or equal than the last version,
|
|
|
|
* or if it's higher or equal than any of the other releases supplied but lower than the next major release. The last
|
|
|
|
* version of the array must be the highest version.
|
|
|
|
* For example, if the values supplied are ['3.0.5', '3.2.3', '3.3.1'] the function will return true if the site version
|
|
|
|
* is either:
|
|
|
|
* - Greater or equal than 3.3.1.
|
|
|
|
* - Greater or equal than 3.2.3 but lower than 3.3.
|
|
|
|
* - Greater or equal than 3.0.5 but lower than 3.1.
|
|
|
|
*
|
|
|
|
* This function only accepts versions from 2.4.0 and above. If any of the versions supplied isn't found, it will assume
|
|
|
|
* it's the last released major version.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
isVersionGreaterEqualThan(versions: string | string[]): boolean {
|
2017-11-13 12:35:44 +00:00
|
|
|
const siteVersion = parseInt(this.getInfo().version, 10);
|
|
|
|
|
|
|
|
if (Array.isArray(versions)) {
|
|
|
|
if (!versions.length) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (let i = 0; i < versions.length; i++) {
|
2018-01-29 09:05:20 +00:00
|
|
|
const versionNumber = this.getVersionNumber(versions[i]);
|
2017-11-13 12:35:44 +00:00
|
|
|
if (i == versions.length - 1) {
|
|
|
|
// It's the last version, check only if site version is greater than this one.
|
|
|
|
return siteVersion >= versionNumber;
|
|
|
|
} else {
|
|
|
|
// Check if site version if bigger than this number but lesser than next major.
|
|
|
|
if (siteVersion >= versionNumber && siteVersion < this.getNextMajorVersionNumber(versions[i])) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (typeof versions == 'string') {
|
|
|
|
// Compare with this version.
|
|
|
|
return siteVersion >= this.getVersionNumber(versions);
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get a version number from a release version.
|
|
|
|
* If release version is valid but not found in the list of Moodle releases, it will use the last released major version.
|
|
|
|
*
|
|
|
|
* @param {string} version Release version to convert to version number.
|
|
|
|
* @return {number} Version number, 0 if invalid.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
protected getVersionNumber(version: string): number {
|
|
|
|
const data = this.getMajorAndMinor(version);
|
2017-11-13 12:35:44 +00:00
|
|
|
|
|
|
|
if (!data) {
|
|
|
|
// Invalid version.
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (typeof this.moodleReleases[data.major] == 'undefined') {
|
|
|
|
// Major version not found. Use the last one.
|
|
|
|
data.major = Object.keys(this.moodleReleases).slice(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.moodleReleases[data.major] + data.minor;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Given a release version, return the major and minor versions.
|
|
|
|
*
|
|
|
|
* @param {string} version Release version (e.g. '3.1.0').
|
|
|
|
* @return {object} Object with major and minor. Returns false if invalid version.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
protected getMajorAndMinor(version: string): any {
|
2017-11-13 12:35:44 +00:00
|
|
|
const match = version.match(/(\d)+(?:\.(\d)+)?(?:\.(\d)+)?/);
|
2018-01-29 09:05:20 +00:00
|
|
|
if (!match || !match[1]) {
|
2017-11-13 12:35:44 +00:00
|
|
|
// Invalid version.
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
2018-01-29 09:05:20 +00:00
|
|
|
major: match[1] + '.' + (match[2] || '0'),
|
|
|
|
minor: parseInt(match[3], 10) || 0
|
|
|
|
};
|
2017-11-13 12:35:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Given a release version, return the next major version number.
|
|
|
|
*
|
|
|
|
* @param {string} version Release version (e.g. '3.1.0').
|
|
|
|
* @return {number} Next major version number.
|
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
protected getNextMajorVersionNumber(version: string): number {
|
|
|
|
const data = this.getMajorAndMinor(version),
|
2017-11-13 12:35:44 +00:00
|
|
|
releases = Object.keys(this.moodleReleases);
|
2018-01-29 09:05:20 +00:00
|
|
|
let position;
|
2017-11-13 12:35:44 +00:00
|
|
|
|
|
|
|
if (!data) {
|
|
|
|
// Invalid version.
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
position = releases.indexOf(data.major);
|
|
|
|
|
2018-01-29 09:05:20 +00:00
|
|
|
if (position == -1 || position == releases.length - 1) {
|
2017-11-13 12:35:44 +00:00
|
|
|
// Major version not found or it's the last one. Use the last one.
|
|
|
|
return this.moodleReleases[releases[position]];
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.moodleReleases[releases[position + 1]];
|
|
|
|
}
|
|
|
|
}
|