2017-11-14 07:58:20 +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 { Injectable } from '@angular/core';
|
|
|
|
import { HttpClient } from '@angular/common/http';
|
|
|
|
import { TranslateService } from '@ngx-translate/core';
|
|
|
|
import { CoreAppProvider } from './app';
|
|
|
|
import { CoreEventsProvider } from './events';
|
|
|
|
import { CoreLoggerProvider } from './logger';
|
|
|
|
import { CoreSitesFactoryProvider } from './sites-factory';
|
|
|
|
import { CoreUrlUtilsProvider } from './utils/url';
|
|
|
|
import { CoreUtilsProvider } from './utils/utils';
|
|
|
|
import { CoreConstants } from '../core/constants';
|
|
|
|
import { CoreConfigConstants } from '../configconstants';
|
|
|
|
import { CoreSite } from '../classes/site';
|
|
|
|
import { SQLiteDB } from '../classes/sqlitedb';
|
|
|
|
import { Md5 } from 'ts-md5/dist/md5';
|
|
|
|
|
|
|
|
export interface CoreSiteCheckResponse {
|
|
|
|
code: number; // Code to identify the authentication method to use.
|
|
|
|
siteUrl: string; // Site url to use (might have changed during the process).
|
|
|
|
service: string; // Service used.
|
|
|
|
warning?: string; // Code of the warning message to show to the user.
|
|
|
|
config?: any; // Site public config (if available).
|
|
|
|
};
|
|
|
|
|
|
|
|
export interface CoreSiteUserTokenResponse {
|
|
|
|
token: string; // User token.
|
|
|
|
siteUrl: string; // Site URL to use.
|
|
|
|
privateToken?: string; // User private token.
|
|
|
|
};
|
|
|
|
|
|
|
|
export interface CoreSiteBasicInfo {
|
|
|
|
id: string;
|
|
|
|
siteUrl: string;
|
|
|
|
fullName: string;
|
|
|
|
siteName: string;
|
|
|
|
avatar: string;
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Service to manage and interact with sites.
|
2017-11-20 15:19:21 +00:00
|
|
|
* It allows creating tables in the databases of all sites. Each service or component should be responsible of creating
|
|
|
|
* their own database tables. Example:
|
|
|
|
*
|
|
|
|
* constructor(sitesProvider: CoreSitesProvider) {
|
|
|
|
* this.sitesProvider.createTableFromSchema(this.tableSchema);
|
|
|
|
*
|
|
|
|
* This provider will automatically create the tables in the databases of all the instantiated sites, and also to the
|
|
|
|
* databases of sites instantiated from now on.
|
2017-11-14 07:58:20 +00:00
|
|
|
*/
|
|
|
|
@Injectable()
|
|
|
|
export class CoreSitesProvider {
|
|
|
|
// Variables for the database.
|
|
|
|
protected SITES_TABLE = 'sites';
|
|
|
|
protected CURRENT_SITE_TABLE = 'current_site';
|
2017-11-20 15:19:21 +00:00
|
|
|
protected appTablesSchema = [
|
2017-11-14 07:58:20 +00:00
|
|
|
{
|
|
|
|
name: this.SITES_TABLE,
|
|
|
|
columns: [
|
|
|
|
{
|
|
|
|
name: 'id',
|
|
|
|
type: 'TEXT',
|
|
|
|
primaryKey: true
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'siteUrl',
|
|
|
|
type: 'TEXT',
|
|
|
|
notNull: true
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'token',
|
|
|
|
type: 'TEXT'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'info',
|
|
|
|
type: 'TEXT'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'privateToken',
|
|
|
|
type: 'TEXT'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'config',
|
|
|
|
type: 'TEXT'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'loggedOut',
|
|
|
|
type: 'INTEGER'
|
|
|
|
}
|
|
|
|
]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: this.CURRENT_SITE_TABLE,
|
|
|
|
columns: [
|
|
|
|
{
|
|
|
|
name: 'id',
|
|
|
|
type: 'INTEGER',
|
|
|
|
primaryKey: true
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'siteId',
|
|
|
|
type: 'TEXT',
|
|
|
|
notNull: true,
|
|
|
|
unique: true
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
];
|
|
|
|
|
|
|
|
// Constants to validate a site version.
|
|
|
|
protected VALID_VERSION = 1;
|
|
|
|
protected LEGACY_APP_VERSION = 0;
|
|
|
|
protected INVALID_VERSION = -1;
|
|
|
|
|
|
|
|
protected logger;
|
|
|
|
protected services = {};
|
|
|
|
protected sessionRestored = false;
|
|
|
|
protected currentSite: CoreSite;
|
|
|
|
protected sites: {[s: string]: CoreSite} = {};
|
|
|
|
protected appDB: SQLiteDB;
|
2017-11-20 15:19:21 +00:00
|
|
|
protected siteTablesSchemas = []; // Schemas for site tables. Other providers can add schemas in here.
|
2017-11-14 07:58:20 +00:00
|
|
|
|
|
|
|
constructor(logger: CoreLoggerProvider, private http: HttpClient, private sitesFactory: CoreSitesFactoryProvider,
|
|
|
|
private appProvider: CoreAppProvider, private utils: CoreUtilsProvider, private translate: TranslateService,
|
|
|
|
private eventsProvider: CoreEventsProvider, private urlUtils: CoreUrlUtilsProvider) {
|
|
|
|
this.logger = logger.getInstance('CoreSitesProvider');
|
|
|
|
|
|
|
|
this.appDB = appProvider.getDB();
|
2017-11-20 15:19:21 +00:00
|
|
|
this.appDB.createTablesFromSchema(this.appTablesSchema);
|
2017-11-14 07:58:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the demo data for a certain "name" if it is a demo site.
|
|
|
|
*
|
|
|
|
* @param {string} name Name of the site to check.
|
|
|
|
* @return {any} Site data if it's a demo site, undefined otherwise.
|
|
|
|
*/
|
|
|
|
getDemoSiteData(name: string) {
|
|
|
|
const demoSites = CoreConfigConstants.demo_sites;
|
|
|
|
if (typeof demoSites != 'undefined' && typeof demoSites[name] != 'undefined') {
|
|
|
|
return demoSites[name];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if a site is valid and if it has specifics settings for authentication (like force to log in using the browser).
|
|
|
|
* It will test both protocols if the first one fails: http and https.
|
|
|
|
*
|
|
|
|
* @param {string} siteUrl URL of the site to check.
|
|
|
|
* @param {string} [protocol=https://] Protocol to use first.
|
|
|
|
* @return {Promise<CoreSiteCheckResponse>} A promise resolved when the site is checked.
|
|
|
|
*/
|
|
|
|
checkSite(siteUrl: string, protocol = 'https://') : Promise<CoreSiteCheckResponse> {
|
|
|
|
// formatURL adds the protocol if is missing.
|
|
|
|
siteUrl = this.urlUtils.formatURL(siteUrl);
|
|
|
|
|
|
|
|
if (!this.urlUtils.isHttpURL(siteUrl)) {
|
|
|
|
return Promise.reject(this.translate.instant('mm.login.invalidsite'));
|
|
|
|
} else if (!this.appProvider.isOnline()) {
|
|
|
|
return Promise.reject(this.translate.instant('mm.core.networkerrormsg'));
|
|
|
|
} else {
|
|
|
|
return this.checkSiteWithProtocol(siteUrl, protocol).catch((error) => {
|
|
|
|
// Do not continue checking if a critical error happened.
|
|
|
|
if (error.critical) {
|
|
|
|
return Promise.reject(error.error);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Retry with the other protocol.
|
|
|
|
protocol = protocol == 'https://' ? 'http://' : 'https://';
|
|
|
|
return this.checkSiteWithProtocol(siteUrl, protocol).catch((secondError) => {
|
|
|
|
// Site doesn't exist.
|
|
|
|
if (secondError.error) {
|
|
|
|
return Promise.reject(secondError.error);
|
|
|
|
} else if (error.error) {
|
|
|
|
return Promise.reject(error.error);
|
|
|
|
}
|
|
|
|
return Promise.reject(this.translate.instant('mm.login.checksiteversion'));
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Helper function to check if a site is valid and if it has specifics settings for authentication.
|
|
|
|
*
|
|
|
|
* @param {string} siteUrl URL of the site to check.
|
|
|
|
* @param {string} protocol Protocol to use.
|
|
|
|
* @return {Promise<CoreSiteCheckResponse>} A promise resolved when the site is checked.
|
|
|
|
*/
|
|
|
|
checkSiteWithProtocol(siteUrl: string, protocol: string) : Promise<CoreSiteCheckResponse> {
|
|
|
|
let publicConfig;
|
|
|
|
|
|
|
|
// Now, replace the siteUrl with the protocol.
|
|
|
|
siteUrl = siteUrl.replace(/^http(s)?\:\/\//i, protocol);
|
|
|
|
|
|
|
|
return this.siteExists(siteUrl).catch((error) => {
|
|
|
|
// Do not continue checking if WS are not enabled.
|
|
|
|
if (error.errorcode && error.errorcode == 'enablewsdescription') {
|
|
|
|
return rejectWithCriticalError(error.error, error.errorcode);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Site doesn't exist. Try to add or remove 'www'.
|
|
|
|
const treatedUrl = this.urlUtils.addOrRemoveWWW(siteUrl);
|
|
|
|
return this.siteExists(treatedUrl).then(() => {
|
|
|
|
// Success, use this new URL as site url.
|
|
|
|
siteUrl = treatedUrl;
|
|
|
|
}).catch((secondError) => {
|
|
|
|
// Do not continue checking if WS are not enabled.
|
|
|
|
if (secondError.errorcode && secondError.errorcode == 'enablewsdescription') {
|
|
|
|
return rejectWithCriticalError(secondError.error, secondError.errorcode);
|
|
|
|
}
|
|
|
|
|
|
|
|
error = secondError || error;
|
|
|
|
return Promise.reject({error: typeof error == 'object' ? error.error : error});
|
|
|
|
});
|
|
|
|
}).then(() => {
|
|
|
|
// Create a temporary site to check if local_mobile is installed.
|
|
|
|
const temporarySite = this.sitesFactory.makeSite(undefined, siteUrl);
|
|
|
|
return temporarySite.checkLocalMobilePlugin().then((data) => {
|
|
|
|
data.service = data.service || CoreConfigConstants.wsservice;
|
|
|
|
this.services[siteUrl] = data.service; // No need to store it in DB.
|
|
|
|
|
|
|
|
if (data.coreSupported ||
|
|
|
|
(data.code != CoreConstants.loginSSOCode && data.code != CoreConstants.loginSSOInAppCode)) {
|
|
|
|
// SSO using local_mobile not needed, try to get the site public config.
|
|
|
|
return temporarySite.getPublicConfig().then((config) : any => {
|
|
|
|
publicConfig = config;
|
|
|
|
|
|
|
|
// Check that the user can authenticate.
|
|
|
|
if (!config.enablewebservices) {
|
|
|
|
return rejectWithCriticalError(this.translate.instant('mm.login.webservicesnotenabled'));
|
|
|
|
} else if (!config.enablemobilewebservice) {
|
|
|
|
return rejectWithCriticalError(this.translate.instant('mm.login.mobileservicesnotenabled'));
|
|
|
|
} else if (config.maintenanceenabled) {
|
|
|
|
let message = this.translate.instant('mm.core.sitemaintenance');
|
|
|
|
if (config.maintenancemessage) {
|
|
|
|
message += config.maintenancemessage;
|
|
|
|
}
|
|
|
|
return rejectWithCriticalError(message);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Everything ok.
|
|
|
|
if (data.code === 0) {
|
|
|
|
data.code = config.typeoflogin;
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}, (error) : any => {
|
|
|
|
// Error, check if not supported.
|
|
|
|
if (error.available === 1) {
|
|
|
|
// Service supported but an error happened. Return error.
|
|
|
|
return Promise.reject({error: error.error});
|
|
|
|
}
|
|
|
|
|
|
|
|
return data;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
return data;
|
|
|
|
}).then((data) => {
|
|
|
|
siteUrl = temporarySite.getURL();
|
|
|
|
return {siteUrl: siteUrl, code: data.code, warning: data.warning, service: data.service, config: publicConfig};
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
// Return a rejected promise with a "critical" error.
|
|
|
|
function rejectWithCriticalError(message: string, errorCode?: string) {
|
|
|
|
return Promise.reject({
|
|
|
|
error: message,
|
|
|
|
errorcode: errorCode,
|
|
|
|
critical: true
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if a site exists.
|
|
|
|
*
|
|
|
|
* @param {string} siteUrl URL of the site to check.
|
|
|
|
* @return {Promise} A promise to be resolved if the site exists.
|
|
|
|
*/
|
|
|
|
siteExists(siteUrl: string) : Promise<void> {
|
|
|
|
let data: any = {};
|
|
|
|
|
|
|
|
if (!this.appProvider.isMobile()) {
|
|
|
|
// Send fake parameters for CORS. This is only needed in browser.
|
|
|
|
data.username = 'a';
|
|
|
|
data.password = 'b';
|
|
|
|
data.service = 'c';
|
|
|
|
}
|
|
|
|
|
|
|
|
const observable = this.http.post(siteUrl + '/login/token.php', data).timeout(CoreConstants.wsTimeout);
|
2017-11-24 11:31:09 +00:00
|
|
|
return this.utils.observableToPromise(observable).catch((error) => {
|
|
|
|
return Promise.reject(error.message);
|
|
|
|
}).then((data: any) => {
|
2017-11-14 07:58:20 +00:00
|
|
|
if (data.errorcode && (data.errorcode == 'enablewsdescription' || data.errorcode == 'requirecorrectaccess')) {
|
|
|
|
return Promise.reject({errorcode: data.errorcode, error: data.error});
|
|
|
|
} else if (data.error && data.error == 'Web services must be enabled in Advanced features.') {
|
|
|
|
return Promise.reject({errorcode: 'enablewsdescription', error: data.error});
|
|
|
|
}
|
|
|
|
// Other errors are not being checked because invalid login will be always raised and we cannot differ them.
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets a user token from the server.
|
|
|
|
*
|
|
|
|
* @param {string} siteUrl The site url.
|
|
|
|
* @param {string} username User name.
|
|
|
|
* @param {string} password Password.
|
|
|
|
* @param {string} [service] Service to use. If not defined, it will be searched in memory.
|
|
|
|
* @param {boolean} [retry] Whether we are retrying with a prefixed URL.
|
|
|
|
* @return {Promise<CoreSiteUserTokenResponse>} A promise resolved when the token is retrieved.
|
|
|
|
*/
|
|
|
|
getUserToken(siteUrl: string, username: string, password: string, service?: string, retry?: boolean)
|
|
|
|
: Promise<CoreSiteUserTokenResponse> {
|
|
|
|
if (!this.appProvider.isOnline()) {
|
|
|
|
return Promise.reject(this.translate.instant('mm.core.networkerrormsg'));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!service) {
|
|
|
|
service = this.determineService(siteUrl);
|
|
|
|
}
|
|
|
|
|
|
|
|
const params = {
|
|
|
|
username: username,
|
|
|
|
password: password,
|
|
|
|
service: service
|
|
|
|
},
|
|
|
|
observable = this.http.post(siteUrl + '/login/token.php', params).timeout(CoreConstants.wsTimeout);
|
|
|
|
|
|
|
|
return this.utils.observableToPromise(observable).then((data: any) : any => {
|
|
|
|
if (typeof data == 'undefined') {
|
|
|
|
return Promise.reject(this.translate.instant('mm.core.cannotconnect'));
|
|
|
|
} else {
|
|
|
|
if (typeof data.token != 'undefined') {
|
|
|
|
return {token: data.token, siteUrl: siteUrl, privateToken: data.privatetoken};
|
|
|
|
} else {
|
|
|
|
if (typeof data.error != 'undefined') {
|
|
|
|
// We only allow one retry (to avoid loops).
|
|
|
|
if (!retry && data.errorcode == "requirecorrectaccess") {
|
|
|
|
siteUrl = this.urlUtils.addOrRemoveWWW(siteUrl);
|
|
|
|
return this.getUserToken(siteUrl, username, password, service, true);
|
|
|
|
} else if (typeof data.errorcode != 'undefined') {
|
|
|
|
return Promise.reject({error: data.error, errorcode: data.errorcode});
|
|
|
|
} else {
|
|
|
|
return Promise.reject(data.error);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return Promise.reject(this.translate.instant('mm.login.invalidaccount'));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}, () => {
|
|
|
|
return Promise.reject(this.translate.instant('mm.core.cannotconnect'));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add a new site to the site list and authenticate the user in this site.
|
|
|
|
*
|
|
|
|
* @param {string} siteUrl The site url.
|
|
|
|
* @param {string} token User's token.
|
|
|
|
* @param {string} [privateToken=''] User's private token.
|
|
|
|
* @return {Promise<any>} A promise resolved when the site is added and the user is authenticated.
|
|
|
|
*/
|
|
|
|
newSite(siteUrl: string, token: string, privateToken = '') : Promise<any> {
|
|
|
|
// Create a "candidate" site to fetch the site info.
|
|
|
|
const candidateSite = this.sitesFactory.makeSite(undefined, siteUrl, token, undefined, privateToken);
|
|
|
|
|
|
|
|
return candidateSite.fetchSiteInfo().then((info) => {
|
|
|
|
let result = this.isValidMoodleVersion(info);
|
|
|
|
if (result == this.VALID_VERSION) {
|
|
|
|
// Set site ID and info.
|
|
|
|
const siteId = this.createSiteID(info.siteurl, info.username);
|
|
|
|
candidateSite.setId(siteId);
|
|
|
|
candidateSite.setInfo(info);
|
|
|
|
|
|
|
|
// Try to get the site config.
|
|
|
|
return this.getSiteConfig(candidateSite).then((config) => {
|
|
|
|
candidateSite.setConfig(config);
|
|
|
|
// Add site to sites list.
|
|
|
|
this.addSite(siteId, siteUrl, token, info, privateToken, config);
|
|
|
|
// Turn candidate site into current site.
|
|
|
|
this.currentSite = candidateSite;
|
2017-11-20 15:19:21 +00:00
|
|
|
this.sites[siteId] = candidateSite;
|
2017-11-14 07:58:20 +00:00
|
|
|
// Store session.
|
|
|
|
this.login(siteId);
|
|
|
|
this.eventsProvider.trigger(CoreEventsProvider.SITE_ADDED, siteId);
|
|
|
|
|
2017-11-20 15:19:21 +00:00
|
|
|
if (this.siteTablesSchemas.length) {
|
|
|
|
// Create tables in the site's database.
|
|
|
|
candidateSite.getDb().createTablesFromSchema(this.siteTablesSchemas);
|
|
|
|
}
|
|
|
|
|
2017-11-14 07:58:20 +00:00
|
|
|
return siteId;
|
|
|
|
});
|
|
|
|
} else if (result == this.LEGACY_APP_VERSION) {
|
|
|
|
return Promise.reject(this.translate.instant('mm.login.legacymoodleversion'));
|
|
|
|
} else {
|
|
|
|
return Promise.reject(this.translate.instant('mm.login.invalidmoodleversion'));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a site ID based on site URL and username.
|
|
|
|
*
|
|
|
|
* @param {string} siteUrl The site url.
|
|
|
|
* @param {string} username Username.
|
|
|
|
* @return {string} Site ID.
|
|
|
|
*/
|
|
|
|
createSiteID(siteUrl: string, username: string) : string {
|
|
|
|
return <string> Md5.hashAsciiStr(siteUrl + username);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Function for determine which service we should use (default or extended plugin).
|
|
|
|
*
|
|
|
|
* @param {string} siteUrl The site URL.
|
|
|
|
* @return {string} The service shortname.
|
|
|
|
*/
|
|
|
|
determineService(siteUrl: string) : string {
|
|
|
|
// We need to try siteUrl in both https or http (due to loginhttps setting).
|
|
|
|
|
|
|
|
// First http://
|
|
|
|
siteUrl = siteUrl.replace('https://', 'http://');
|
|
|
|
if (this.services[siteUrl]) {
|
|
|
|
return this.services[siteUrl];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now https://
|
|
|
|
siteUrl = siteUrl.replace('http://', 'https://');
|
|
|
|
if (this.services[siteUrl]) {
|
|
|
|
return this.services[siteUrl];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return default service.
|
|
|
|
return CoreConfigConstants.wsservice;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check for the minimum required version.
|
|
|
|
*
|
|
|
|
* @param {any} info Site info.
|
|
|
|
* @return {number} Either VALID_VERSION, LEGACY_APP_VERSION or INVALID_VERSION.
|
|
|
|
*/
|
|
|
|
protected isValidMoodleVersion(info: any) : number {
|
|
|
|
if (!info) {
|
|
|
|
return this.INVALID_VERSION;
|
|
|
|
}
|
|
|
|
|
|
|
|
const version24 = 2012120300, // Moodle 2.4 version.
|
|
|
|
release24 = '2.4',
|
|
|
|
version31 = 2016052300,
|
|
|
|
release31 = '3.1';
|
|
|
|
|
|
|
|
// Try to validate by version.
|
|
|
|
if (info.version) {
|
|
|
|
const version = parseInt(info.version, 10);
|
|
|
|
if (!isNaN(version)) {
|
|
|
|
if (version >= version31) {
|
|
|
|
return this.VALID_VERSION;
|
|
|
|
} else if (version >= version24) {
|
|
|
|
return this.LEGACY_APP_VERSION;
|
|
|
|
} else {
|
|
|
|
return this.INVALID_VERSION;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// We couldn't validate by version number. Let's try to validate by release number.
|
|
|
|
if (info.release) {
|
|
|
|
const matches = info.release.match(/^([\d|\.]*)/);
|
|
|
|
if (matches && matches.length > 1) {
|
|
|
|
if (matches[1] >= release31) {
|
|
|
|
return this.VALID_VERSION;
|
|
|
|
} else if (matches[1] >= release24) {
|
|
|
|
return this.LEGACY_APP_VERSION;
|
|
|
|
} else {
|
|
|
|
return this.INVALID_VERSION;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Couldn't validate it.
|
|
|
|
return this.INVALID_VERSION;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if site info is valid. If it's not, return error message.
|
|
|
|
*
|
|
|
|
* @param {any} info Site info.
|
|
|
|
* @return {any} True if valid, object with error message to show and its params if not valid.
|
|
|
|
*/
|
|
|
|
protected validateSiteInfo(info: any) : any {
|
|
|
|
if (!info.firstname || !info.lastname) {
|
|
|
|
const moodleLink = `<a mm-link href="${info.siteurl}">${info.siteurl}</a>`;
|
|
|
|
return {error: 'mm.core.requireduserdatamissing', params: {'$a': moodleLink}};
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Saves a site in local DB.
|
|
|
|
*
|
|
|
|
* @param {string} id Site ID.
|
|
|
|
* @param {string} siteUrl Site URL.
|
|
|
|
* @param {string} token User's token in the site.
|
|
|
|
* @param {any} info Site's info.
|
|
|
|
* @param {string} [privateToken=''] User's private token.
|
|
|
|
* @param {any} [config] Site config (from tool_mobile_get_config).
|
|
|
|
* @return {Promise<any>} Promise resolved when done.
|
|
|
|
*/
|
|
|
|
addSite(id: string, siteUrl: string, token: string, info: any, privateToken = '', config?: any) : Promise<any> {
|
|
|
|
const entry = {
|
|
|
|
id: id,
|
|
|
|
siteUrl: siteUrl,
|
|
|
|
token: token,
|
|
|
|
info: info ? JSON.stringify(info) : info,
|
|
|
|
privateToken: privateToken,
|
|
|
|
config: config ? JSON.stringify(config) : config,
|
|
|
|
loggedOut: 0
|
|
|
|
};
|
|
|
|
return this.appDB.insertOrUpdateRecord(this.SITES_TABLE, entry, {id: id});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Login a user to a site from the list of sites.
|
|
|
|
*
|
|
|
|
* @param {string} siteId ID of the site to load.
|
|
|
|
* @return {Promise} Promise to be resolved when the site is loaded.
|
|
|
|
*/
|
|
|
|
loadSite(siteId: string) : Promise<any> {
|
|
|
|
this.logger.debug(`Load site ${siteId}`);
|
|
|
|
|
|
|
|
return this.getSite(siteId).then((site) => {
|
|
|
|
this.currentSite = site;
|
|
|
|
this.login(siteId);
|
|
|
|
|
|
|
|
if (site.isLoggedOut()) {
|
|
|
|
// Logged out, nothing else to do.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if local_mobile was installed to Moodle.
|
|
|
|
return site.checkIfLocalMobileInstalledAndNotUsed().then(() => {
|
|
|
|
// Local mobile was added. Throw invalid session to force reconnect and create a new token.
|
|
|
|
this.eventsProvider.trigger(CoreEventsProvider.SESSION_EXPIRED, {siteId: siteId});
|
|
|
|
}, () => {
|
|
|
|
// Update site info. We don't block the UI.
|
|
|
|
this.updateSiteInfo(siteId);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get current site.
|
|
|
|
*
|
|
|
|
* @return {CoreSite} Current site.
|
|
|
|
*/
|
|
|
|
getCurrentSite() : CoreSite {
|
|
|
|
return this.currentSite;
|
|
|
|
}
|
|
|
|
|
2017-11-15 07:47:28 +00:00
|
|
|
/**
|
|
|
|
* Get current site ID.
|
|
|
|
*
|
|
|
|
* @return {string} Current site ID.
|
|
|
|
*/
|
|
|
|
getCurrentSiteId() : string {
|
|
|
|
if (this.currentSite) {
|
|
|
|
return this.currentSite.getId();
|
|
|
|
} else {
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
}
|
2017-11-14 07:58:20 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if the user is logged in a site.
|
|
|
|
*
|
|
|
|
* @return {boolean} Whether the user is logged in a site.
|
|
|
|
*/
|
|
|
|
isLoggedIn() : boolean {
|
|
|
|
return typeof this.currentSite != 'undefined' && typeof this.currentSite.token != 'undefined' &&
|
|
|
|
this.currentSite.token != '';
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Delete a site from the sites list.
|
|
|
|
*
|
|
|
|
* @param {string} siteId ID of the site to delete.
|
|
|
|
* @return {Promise<any>} Promise to be resolved when the site is deleted.
|
|
|
|
*/
|
|
|
|
deleteSite(siteId: string) : Promise<any> {
|
|
|
|
this.logger.debug(`Delete site ${siteId}`);
|
|
|
|
|
|
|
|
if (typeof this.currentSite != 'undefined' && this.currentSite.id == siteId) {
|
|
|
|
this.logout();
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.getSite(siteId).then((site: CoreSite) => {
|
|
|
|
return site.deleteDB().then(() => {
|
|
|
|
// Site DB deleted, now delete the app from the list of sites.
|
|
|
|
delete this.sites[siteId];
|
|
|
|
return this.appDB.deleteRecords(this.SITES_TABLE, {id: siteId}).then(() => {
|
|
|
|
// Site deleted from sites list, now delete the folder.
|
|
|
|
return site.deleteFolder();
|
|
|
|
}, () => {
|
|
|
|
// DB remove shouldn't fail, but we'll go ahead even if it does.
|
|
|
|
return site.deleteFolder();
|
|
|
|
}).then(() => {
|
|
|
|
this.eventsProvider.trigger(CoreEventsProvider.SITE_DELETED, site);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if there are no sites stored.
|
|
|
|
*
|
|
|
|
* @return {Promise<void>} Promise resolved if there are no sites, and rejected if there is at least one.
|
|
|
|
*/
|
|
|
|
hasNoSites() : Promise<void> {
|
|
|
|
return this.appDB.countRecords(this.SITES_TABLE).then((count) => {
|
|
|
|
if (count > 0) {
|
|
|
|
return Promise.reject(null);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if there are sites stored.
|
|
|
|
*
|
|
|
|
* @return {Promise<void>} Promise resolved if there is at least one site, and rejected if there aren't.
|
|
|
|
*/
|
|
|
|
hasSites() : Promise<void> {
|
|
|
|
return this.appDB.countRecords(this.SITES_TABLE).then((count) => {
|
|
|
|
if (count == 0) {
|
|
|
|
return Promise.reject(null);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a site object.
|
|
|
|
*
|
|
|
|
* @param {string} [siteId] The site ID. If not defined, current site (if available).
|
|
|
|
* @return {Promise<CoreSite>} Promise resolved with the site.
|
|
|
|
*/
|
|
|
|
getSite(siteId?: string) : Promise<CoreSite> {
|
|
|
|
if (!siteId) {
|
|
|
|
return this.currentSite ? Promise.resolve(this.currentSite) : Promise.reject(null);
|
|
|
|
} else if (this.currentSite && this.currentSite.getId() == siteId) {
|
|
|
|
return Promise.resolve(this.currentSite);
|
|
|
|
} else if (typeof this.sites[siteId] != 'undefined') {
|
|
|
|
return Promise.resolve(this.sites[siteId]);
|
|
|
|
} else {
|
|
|
|
// retrieve and create the site.
|
|
|
|
return this.appDB.getRecord(this.SITES_TABLE, {id: siteId}).then((data) => {
|
|
|
|
return this.makeSiteFromSiteListEntry(data);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a site from an entry of the sites list DB. The new site is added to the list of "cached" sites: this.sites.
|
|
|
|
*
|
|
|
|
* @param {any} entry Site list entry.
|
|
|
|
* @return {CoreSite} Created site.
|
|
|
|
*/
|
|
|
|
makeSiteFromSiteListEntry(entry) : CoreSite {
|
|
|
|
let site,
|
|
|
|
info = entry.info,
|
|
|
|
config = entry.config;
|
|
|
|
|
|
|
|
// Try to parse info and config.
|
|
|
|
try {
|
|
|
|
info = info ? JSON.parse(info) : info;
|
|
|
|
} catch(ex) {}
|
|
|
|
|
|
|
|
try {
|
|
|
|
config = config ? JSON.parse(config) : config;
|
|
|
|
} catch(ex) {}
|
|
|
|
|
|
|
|
site = this.sitesFactory.makeSite(entry.id, entry.siteUrl, entry.token,
|
|
|
|
info, entry.privateToken, config, entry.loggedOut == 1);
|
|
|
|
this.sites[entry.id] = site;
|
2017-11-20 15:19:21 +00:00
|
|
|
if (this.siteTablesSchemas.length) {
|
|
|
|
// Create tables in the site's database.
|
|
|
|
site.getDb().createTablesFromSchema(this.siteTablesSchemas);
|
|
|
|
}
|
|
|
|
|
2017-11-14 07:58:20 +00:00
|
|
|
return site;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns if the site is the current one.
|
|
|
|
*
|
|
|
|
* @param {string|CoreSite} [site] Site object or siteId to be compared. If not defined, use current site.
|
|
|
|
* @return {boolean} Whether site or siteId is the current one.
|
|
|
|
*/
|
|
|
|
isCurrentSite(site: string|CoreSite) : boolean {
|
|
|
|
if (!site || !this.currentSite) {
|
|
|
|
return !!this.currentSite;
|
|
|
|
}
|
|
|
|
|
|
|
|
const siteId = typeof site == 'object' ? site.getId() : site;
|
|
|
|
return this.currentSite.getId() === siteId;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the database object of a site.
|
|
|
|
*
|
|
|
|
* @param {string} [siteId] The site ID. If not defined, current site (if available).
|
|
|
|
* @return {Promise<SQLiteDB>} Promise resolved with the database.
|
|
|
|
*/
|
|
|
|
getSiteDb(siteId: string) : Promise<SQLiteDB> {
|
|
|
|
return this.getSite(siteId).then((site) => {
|
|
|
|
return site.getDb();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the site home ID of a site.
|
|
|
|
*
|
|
|
|
* @param {number} [siteId] The site ID. If not defined, current site (if available).
|
|
|
|
* @return {Promise} Promise resolved with site home ID.
|
|
|
|
*/
|
|
|
|
getSiteHomeId(siteId: string) : Promise<number> {
|
|
|
|
return this.getSite(siteId).then((site) => {
|
|
|
|
return site.getSiteHomeId();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the list of sites stored.
|
|
|
|
*
|
|
|
|
* @param {String[]} [ids] IDs of the sites to get. If not defined, return all sites.
|
|
|
|
* @return {Promise<CoreSiteBasicInfo[]>} Promise resolved when the sites are retrieved.
|
|
|
|
*/
|
|
|
|
getSites(ids: string[]) : Promise<CoreSiteBasicInfo[]> {
|
|
|
|
return this.appDB.getAllRecords(this.SITES_TABLE).then((sites) => {
|
|
|
|
let formattedSites = [];
|
|
|
|
sites.forEach((site) => {
|
|
|
|
if (!ids || ids.indexOf(site.id) > -1) {
|
|
|
|
const basicInfo: CoreSiteBasicInfo = {
|
|
|
|
id: site.id,
|
|
|
|
siteUrl: site.siteUrl,
|
|
|
|
fullName: site.info.fullname,
|
|
|
|
siteName: site.info.sitename,
|
|
|
|
avatar: site.info.userpictureurl
|
|
|
|
};
|
|
|
|
formattedSites.push(basicInfo);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return formattedSites;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the list of IDs of sites stored.
|
|
|
|
*
|
|
|
|
* @return {Promise<string[]>} Promise resolved when the sites IDs are retrieved.
|
|
|
|
*/
|
|
|
|
getSitesIds(): Promise<string[]> {
|
|
|
|
return this.appDB.getAllRecords(this.SITES_TABLE).then((sites) => {
|
|
|
|
return sites.map((site) => {
|
|
|
|
return site.id;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Login the user in a site.
|
|
|
|
*
|
|
|
|
* @param {string} siteid ID of the site the user is accessing.
|
|
|
|
* @return {Promise<void>} Promise resolved when current site is stored.
|
|
|
|
*/
|
|
|
|
login(siteId: string) : Promise<void> {
|
|
|
|
const entry = {
|
|
|
|
id: 1,
|
|
|
|
siteId: siteId
|
|
|
|
};
|
|
|
|
return this.appDB.insertOrUpdateRecord(this.CURRENT_SITE_TABLE, entry, {id: 1}).then(() => {
|
|
|
|
this.eventsProvider.trigger(CoreEventsProvider.LOGIN, {siteId: siteId});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Logout the user.
|
|
|
|
*
|
|
|
|
* @return {Promise<any>} Promise resolved when the user is logged out.
|
|
|
|
*/
|
|
|
|
logout() : Promise<any> {
|
|
|
|
if (!this.currentSite) {
|
|
|
|
// Already logged out.
|
|
|
|
return Promise.resolve();
|
|
|
|
}
|
|
|
|
|
|
|
|
const siteId = this.currentSite.getId(),
|
|
|
|
siteConfig = this.currentSite.getStoredConfig(),
|
|
|
|
promises = [];
|
|
|
|
|
|
|
|
this.currentSite = undefined;
|
|
|
|
|
|
|
|
if (siteConfig && siteConfig.tool_mobile_forcelogout == '1') {
|
|
|
|
promises.push(this.setSiteLoggedOut(siteId, true));
|
|
|
|
}
|
|
|
|
|
|
|
|
promises.push(this.appDB.deleteRecords(this.CURRENT_SITE_TABLE, {id: 1}));
|
|
|
|
|
|
|
|
return Promise.all(promises).finally(() => {
|
|
|
|
this.eventsProvider.trigger(CoreEventsProvider.LOGOUT, {siteId: siteId});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Restores the session to the previous one so the user doesn't has to login everytime the app is started.
|
|
|
|
*
|
|
|
|
* @return {Promise<any>} Promise resolved if a session is restored.
|
|
|
|
*/
|
|
|
|
restoreSession() : Promise<any> {
|
|
|
|
if (this.sessionRestored) {
|
|
|
|
return Promise.reject(null);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.sessionRestored = true;
|
|
|
|
|
|
|
|
return this.appDB.getRecord(this.CURRENT_SITE_TABLE, {id: 1}).then((currentSite) => {
|
|
|
|
const siteId = currentSite.siteId;
|
|
|
|
this.logger.debug(`Restore session in site ${siteId}`);
|
|
|
|
return this.loadSite(siteId);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Mark or unmark a site as logged out so the user needs to authenticate again.
|
|
|
|
*
|
|
|
|
* @param {string} siteId ID of the site.
|
|
|
|
* @param {boolean} loggedOut True to set the site as logged out, false otherwise.
|
|
|
|
* @return {Promise<any>} Promise resolved when done.
|
|
|
|
*/
|
|
|
|
setSiteLoggedOut(siteId: string, loggedOut: boolean) : Promise<any> {
|
|
|
|
return this.getSite(siteId).then((site) => {
|
|
|
|
const newValues = {
|
|
|
|
token: '', // Erase the token for security.
|
|
|
|
loggedOut: loggedOut ? 1 : 0
|
|
|
|
};
|
|
|
|
|
|
|
|
site.setLoggedOut(loggedOut);
|
|
|
|
|
|
|
|
return this.appDB.updateRecords(this.SITES_TABLE, newValues, {id: siteId});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Updates a site's token.
|
|
|
|
*
|
|
|
|
* @param {string} siteUrl Site's URL.
|
|
|
|
* @param {string} username Username.
|
|
|
|
* @param {string} token User's new token.
|
|
|
|
* @param {string} [privateToken=''] User's private token.
|
|
|
|
* @return {Promise<any>} A promise resolved when the site is updated.
|
|
|
|
*/
|
|
|
|
updateSiteToken(siteUrl: string, username: string, token: string, privateToken = '') : Promise<any> {
|
|
|
|
const siteId = this.createSiteID(siteUrl, username);
|
|
|
|
return this.updateSiteTokenBySiteId(siteId, token, privateToken);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Updates a site's token using siteId.
|
|
|
|
*
|
|
|
|
* @param {string} siteId Site Id.
|
|
|
|
* @param {string} token User's new token.
|
|
|
|
* @param {string} [privateToken=''] User's private token.
|
|
|
|
* @return {Promise<any>} A promise resolved when the site is updated.
|
|
|
|
*/
|
|
|
|
updateSiteTokenBySiteId(siteId: string, token: string, privateToken = '') : Promise<any> {
|
|
|
|
return this.getSite(siteId).then((site) => {
|
|
|
|
const newValues = {
|
|
|
|
token: token,
|
|
|
|
privateToken: privateToken,
|
|
|
|
loggedOut: 0
|
|
|
|
};
|
|
|
|
|
|
|
|
site.token = token;
|
|
|
|
site.privateToken = privateToken;
|
|
|
|
site.setLoggedOut(false); // Token updated means the user authenticated again, not logged out anymore.
|
|
|
|
|
|
|
|
return this.appDB.updateRecords(this.SITES_TABLE, newValues, {id: siteId});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Updates a site's info.
|
|
|
|
*
|
|
|
|
* @param {string} siteid Site's ID.
|
|
|
|
* @return {Promise<any>} A promise resolved when the site is updated.
|
|
|
|
*/
|
|
|
|
updateSiteInfo(siteId: string) : Promise<any> {
|
|
|
|
return this.getSite(siteId).then((site) => {
|
|
|
|
return site.fetchSiteInfo().then((info) => {
|
|
|
|
site.setInfo(info);
|
|
|
|
|
|
|
|
// Try to get the site config.
|
|
|
|
return this.getSiteConfig(site).catch(() => {
|
|
|
|
// Error getting config, keep the current one.
|
|
|
|
}).then((config) => {
|
|
|
|
let newValues: any = {
|
|
|
|
info: JSON.stringify(info),
|
|
|
|
loggedOut: site.isLoggedOut() ? 1 : 0
|
|
|
|
};
|
|
|
|
|
|
|
|
if (typeof config != 'undefined') {
|
|
|
|
site.setConfig(config);
|
|
|
|
newValues.config = JSON.stringify(config);
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.appDB.updateRecords(this.SITES_TABLE, newValues, {id: siteId}).finally(() => {
|
|
|
|
this.eventsProvider.trigger(CoreEventsProvider.SITE_UPDATED, {siteId: siteId});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Updates a site's info.
|
|
|
|
*
|
|
|
|
* @param {string} siteUrl Site's URL.
|
|
|
|
* @param {string} username Username.
|
|
|
|
* @return {Promise<any>} A promise to be resolved when the site is updated.
|
|
|
|
*/
|
|
|
|
updateSiteInfoByUrl(siteUrl: string, username: string) : Promise<any> {
|
|
|
|
const siteId = this.createSiteID(siteUrl, username);
|
|
|
|
return this.updateSiteInfo(siteId);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the site IDs a URL belongs to.
|
|
|
|
* Someone can have more than one account in the same site, that's why this function returns an array of IDs.
|
|
|
|
*
|
|
|
|
* @param {string} url URL to check.
|
|
|
|
* @param {boolean} [prioritize] True if it should prioritize current site. If the URL belongs to current site then it won't
|
|
|
|
* check any other site, it will only return current site.
|
|
|
|
* @param {string} [username] If set, it will return only the sites where the current user has this username.
|
|
|
|
* @return {Promise<string[]>} Promise resolved with the site IDs (array).
|
|
|
|
*/
|
|
|
|
getSiteIdsFromUrl(url: string, prioritize?: boolean, username?: string) : Promise<string[]> {
|
|
|
|
// If prioritize is true, check current site first.
|
|
|
|
if (prioritize && this.currentSite && this.currentSite.containsUrl(url)) {
|
|
|
|
if (!username || this.currentSite.getInfo().username == username) {
|
|
|
|
return Promise.resolve([this.currentSite.getId()]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if URL has http(s) protocol.
|
|
|
|
if (!url.match(/^https?:\/\//i)) {
|
|
|
|
// URL doesn't have http(s) protocol. Check if it has any protocol.
|
|
|
|
if (this.urlUtils.isAbsoluteURL(url)) {
|
|
|
|
// It has some protocol. Return empty array.
|
|
|
|
return Promise.resolve([]);
|
|
|
|
} else {
|
|
|
|
// No protocol, probably a relative URL. Return current site.
|
|
|
|
if (this.currentSite) {
|
|
|
|
return Promise.resolve([this.currentSite.getId()]);
|
|
|
|
} else {
|
|
|
|
return Promise.resolve([]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.appDB.getAllRecords(this.SITES_TABLE).then((siteEntries) => {
|
|
|
|
let ids = [];
|
|
|
|
siteEntries.forEach((site) => {
|
|
|
|
if (!this.sites[site.id]) {
|
|
|
|
this.makeSiteFromSiteListEntry(site);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.sites[site.id].containsUrl(url)) {
|
|
|
|
if (!username || this.sites[site.id].getInfo().username == username) {
|
|
|
|
ids.push(site.id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return ids;
|
|
|
|
}).catch(() => {
|
|
|
|
// Shouldn't happen.
|
|
|
|
return [];
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the site ID stored in DB as current site.
|
|
|
|
*
|
|
|
|
* @return {Promise<string>} Promise resolved with the site ID.
|
|
|
|
*/
|
|
|
|
getStoredCurrentSiteId() : Promise<string> {
|
|
|
|
return this.appDB.getRecord(this.CURRENT_SITE_TABLE, {id: 1}).then((currentSite) => {
|
|
|
|
return currentSite.siteId;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the public config of a certain site.
|
|
|
|
*
|
|
|
|
* @param {string} siteUrl URL of the site.
|
|
|
|
* @return {Promise<any>} Promise resolved with the public config.
|
|
|
|
*/
|
|
|
|
getSitePublicConfig(siteUrl: string) : Promise<any> {
|
|
|
|
const temporarySite = this.sitesFactory.makeSite(undefined, siteUrl);
|
|
|
|
return temporarySite.getPublicConfig();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get site config.
|
|
|
|
*
|
|
|
|
* @param {any} site The site to get the config.
|
|
|
|
* @return {Promise<any>} Promise resolved with config if available.
|
|
|
|
*/
|
|
|
|
protected getSiteConfig(site: CoreSite) : Promise<any> {
|
|
|
|
if (!site.wsAvailable('tool_mobile_get_config')) {
|
|
|
|
// WS not available, cannot get config.
|
|
|
|
return Promise.resolve();
|
|
|
|
}
|
|
|
|
|
|
|
|
return site.getConfig(null, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if a certain feature is disabled in a site.
|
|
|
|
*
|
|
|
|
* @param {string} name Name of the feature to check.
|
|
|
|
* @param {string} [siteId] The site ID. If not defined, current site (if available).
|
|
|
|
* @return {Promise<boolean>} Promise resolved with true if disabled.
|
|
|
|
*/
|
|
|
|
isFeatureDisabled(name: string, siteId?: string) : Promise<boolean> {
|
|
|
|
return this.getSite(siteId).then((site) => {
|
|
|
|
return site.isFeatureDisabled(name);
|
|
|
|
});
|
|
|
|
}
|
2017-11-20 15:19:21 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a table in all the sites databases.
|
|
|
|
*
|
|
|
|
* @param {any} table Table schema.
|
|
|
|
*/
|
|
|
|
createTableFromSchema(table: any) : void {
|
|
|
|
this.createTablesFromSchema([table]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create several tables in all the sites databases.
|
|
|
|
*
|
|
|
|
* @param {any[]} tables List of tables schema.
|
|
|
|
*/
|
|
|
|
createTablesFromSchema(tables: any[]) : void {
|
|
|
|
// Add the tables to the list of schemas. This list is to create all the tables in new sites.
|
|
|
|
this.siteTablesSchemas = this.siteTablesSchemas.concat(tables);
|
|
|
|
|
|
|
|
// Now create these tables in current sites.
|
|
|
|
for (let id in this.sites) {
|
|
|
|
this.sites[id].getDb().createTablesFromSchema(tables);
|
|
|
|
}
|
|
|
|
}
|
2017-11-14 07:58:20 +00:00
|
|
|
}
|