MOBILE-2261 site: Implement sites factory and events provider
parent
4089a85f94
commit
c8935be6fe
|
@ -26,6 +26,8 @@ import { CoreMimetypeUtilsProvider } from '../providers/utils/mimetype';
|
||||||
import { CoreInitDelegate } from '../providers/init';
|
import { CoreInitDelegate } from '../providers/init';
|
||||||
import { CoreFileProvider } from '../providers/file';
|
import { CoreFileProvider } from '../providers/file';
|
||||||
import { CoreWSProvider } from '../providers/ws';
|
import { CoreWSProvider } from '../providers/ws';
|
||||||
|
import { CoreEventsProvider } from '../providers/events';
|
||||||
|
import { CoreSitesFactoryProvider } from '../providers/sites-factory';
|
||||||
|
|
||||||
// For translate loader. AoT requires an exported function for factories.
|
// For translate loader. AoT requires an exported function for factories.
|
||||||
export function createTranslateLoader(http: HttpClient) {
|
export function createTranslateLoader(http: HttpClient) {
|
||||||
|
@ -72,7 +74,9 @@ export function createTranslateLoader(http: HttpClient) {
|
||||||
CoreMimetypeUtilsProvider,
|
CoreMimetypeUtilsProvider,
|
||||||
CoreInitDelegate,
|
CoreInitDelegate,
|
||||||
CoreFileProvider,
|
CoreFileProvider,
|
||||||
CoreWSProvider
|
CoreWSProvider,
|
||||||
|
CoreEventsProvider,
|
||||||
|
CoreSitesFactoryProvider,
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class AppModule {
|
export class AppModule {
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -303,12 +303,14 @@ export class SQLiteDB {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute a SQL query.
|
* Execute a SQL query.
|
||||||
|
* IMPORTANT: Use this function only if you cannot use any of the other functions in this API. Please take into account that
|
||||||
|
* these query will be run in SQLite (Mobile) and Web SQL (desktop), so your query should work in both environments.
|
||||||
*
|
*
|
||||||
* @param {string} sql SQL query to execute.
|
* @param {string} sql SQL query to execute.
|
||||||
* @param {any[]} params Query parameters.
|
* @param {any[]} params Query parameters.
|
||||||
* @return {Promise<any>} Promise resolved with the result.
|
* @return {Promise<any>} Promise resolved with the result.
|
||||||
*/
|
*/
|
||||||
protected execute(sql: string, params?: any[]) : Promise<any> {
|
execute(sql: string, params?: any[]) : Promise<any> {
|
||||||
return this.ready().then(() => {
|
return this.ready().then(() => {
|
||||||
return this.db.executeSql(sql, params);
|
return this.db.executeSql(sql, params);
|
||||||
});
|
});
|
||||||
|
@ -316,16 +318,28 @@ export class SQLiteDB {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute a set of SQL queries. This operation is atomic.
|
* Execute a set of SQL queries. This operation is atomic.
|
||||||
|
* IMPORTANT: Use this function only if you cannot use any of the other functions in this API. Please take into account that
|
||||||
|
* these query will be run in SQLite (Mobile) and Web SQL (desktop), so your query should work in both environments.
|
||||||
*
|
*
|
||||||
* @param {any[]} sqlStatements SQL statements to execute.
|
* @param {any[]} sqlStatements SQL statements to execute.
|
||||||
* @return {Promise<any>} Promise resolved with the result.
|
* @return {Promise<any>} Promise resolved with the result.
|
||||||
*/
|
*/
|
||||||
protected executeBatch(sqlStatements: any[]) : Promise<any> {
|
executeBatch(sqlStatements: any[]) : Promise<any> {
|
||||||
return this.ready().then(() => {
|
return this.ready().then(() => {
|
||||||
return this.db.sqlBatch(sqlStatements);
|
return this.db.sqlBatch(sqlStatements);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all the records from a table.
|
||||||
|
*
|
||||||
|
* @param {string} table The table to query.
|
||||||
|
* @return {Promise<any>} Promise resolved with the records.
|
||||||
|
*/
|
||||||
|
getAllRecords(table: string) : Promise<any> {
|
||||||
|
return this.getRecords(table);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a single field value from a table record where all the given conditions met.
|
* Get a single field value from a table record where all the given conditions met.
|
||||||
*
|
*
|
||||||
|
|
|
@ -25,4 +25,5 @@ export class CoreConstants {
|
||||||
public static dontShowError = 'CoreDontShowError';
|
public static dontShowError = 'CoreDontShowError';
|
||||||
public static settingsRichTextEditor = 'CoreSettingsRichTextEditor';
|
public static settingsRichTextEditor = 'CoreSettingsRichTextEditor';
|
||||||
public static wsTimeout = 30000;
|
public static wsTimeout = 30000;
|
||||||
|
public static wsPrefix = 'local_mobile_';
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,12 +78,14 @@ export class SQLiteDBMock extends SQLiteDB {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute a SQL query.
|
* Execute a SQL query.
|
||||||
|
* IMPORTANT: Use this function only if you cannot use any of the other functions in this API. Please take into account that
|
||||||
|
* these query will be run in SQLite (Mobile) and Web SQL (desktop), so your query should work in both environments.
|
||||||
*
|
*
|
||||||
* @param {string} sql SQL query to execute.
|
* @param {string} sql SQL query to execute.
|
||||||
* @param {any[]} params Query parameters.
|
* @param {any[]} params Query parameters.
|
||||||
* @return {Promise<any>} Promise resolved with the result.
|
* @return {Promise<any>} Promise resolved with the result.
|
||||||
*/
|
*/
|
||||||
protected execute(sql: string, params?: any[]) : Promise<any> {
|
execute(sql: string, params?: any[]) : Promise<any> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
// With WebSQL, all queries must be run in a transaction.
|
// With WebSQL, all queries must be run in a transaction.
|
||||||
this.db.transaction((tx) => {
|
this.db.transaction((tx) => {
|
||||||
|
@ -96,11 +98,13 @@ export class SQLiteDBMock extends SQLiteDB {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute a set of SQL queries. This operation is atomic.
|
* Execute a set of SQL queries. This operation is atomic.
|
||||||
|
* IMPORTANT: Use this function only if you cannot use any of the other functions in this API. Please take into account that
|
||||||
|
* these query will be run in SQLite (Mobile) and Web SQL (desktop), so your query should work in both environments.
|
||||||
*
|
*
|
||||||
* @param {any[]} sqlStatements SQL statements to execute.
|
* @param {any[]} sqlStatements SQL statements to execute.
|
||||||
* @return {Promise<any>} Promise resolved with the result.
|
* @return {Promise<any>} Promise resolved with the result.
|
||||||
*/
|
*/
|
||||||
protected executeBatch(sqlStatements: any[]) : Promise<any> {
|
executeBatch(sqlStatements: any[]) : Promise<any> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
// Create a transaction to execute the queries.
|
// Create a transaction to execute the queries.
|
||||||
this.db.transaction((tx) => {
|
this.db.transaction((tx) => {
|
||||||
|
|
|
@ -0,0 +1,127 @@
|
||||||
|
// (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 { Subject } from 'rxjs';
|
||||||
|
import { CoreLoggerProvider } from '../providers/logger';
|
||||||
|
|
||||||
|
export interface CoreEventObserver {
|
||||||
|
off: () => void; // Unsubscribe.
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Service to send and listen to events.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class CoreEventsProvider {
|
||||||
|
public static SESSION_EXPIRED = 'session_expired';
|
||||||
|
public static PASSWORD_CHANGE_FORCED = 'password_change_forced';
|
||||||
|
public static USER_NOT_FULLY_SETUP = 'user_not_fully_setup';
|
||||||
|
public static SITE_POLICY_NOT_AGREED = 'site_policy_not_agreed';
|
||||||
|
public static LOGIN = 'login';
|
||||||
|
public static LOGOUT = 'logout';
|
||||||
|
public static LANGUAGE_CHANGED = 'language_changed';
|
||||||
|
public static NOTIFICATION_SOUND_CHANGED = 'notification_sound_changed';
|
||||||
|
public static SITE_ADDED = 'site_added';
|
||||||
|
public static SITE_UPDATED = 'site_updated';
|
||||||
|
public static SITE_DELETED = 'site_deleted';
|
||||||
|
public static COMPLETION_MODULE_VIEWED = 'completion_module_viewed';
|
||||||
|
public static USER_DELETED = 'user_deleted';
|
||||||
|
public static PACKAGE_STATUS_CHANGED = 'package_status_changed';
|
||||||
|
public static SECTION_STATUS_CHANGED = 'section_status_changed';
|
||||||
|
public static REMOTE_ADDONS_LOADED = 'remote_addons_loaded';
|
||||||
|
|
||||||
|
logger;
|
||||||
|
observables = {};
|
||||||
|
uniqueEvents = {};
|
||||||
|
|
||||||
|
constructor(logger: CoreLoggerProvider) {
|
||||||
|
this.logger = logger.getInstance('CoreEventsProvider');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listen for a certain event. To stop listening to the event:
|
||||||
|
* let observer = eventsProvider.on('something', myCallBack);
|
||||||
|
* ...
|
||||||
|
* observer.off();
|
||||||
|
*
|
||||||
|
* @param {string} eventName Name of the event to listen to.
|
||||||
|
* @param {Function} callBack Function to call when the event is triggered.
|
||||||
|
* @return {CoreEventObserver} Observer to stop listening.
|
||||||
|
*/
|
||||||
|
on(eventName: string, callBack: Function) : CoreEventObserver {
|
||||||
|
// If it's a unique event and has been triggered already, call the callBack.
|
||||||
|
// We don't need to create an observer because the event won't be triggered again.
|
||||||
|
if (this.uniqueEvents[eventName]) {
|
||||||
|
callBack(this.uniqueEvents[eventName].data);
|
||||||
|
// Return a fake observer to prevent errors.
|
||||||
|
return {
|
||||||
|
off: () => {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.debug(`New observer listening to event '${eventName}'`);
|
||||||
|
|
||||||
|
if (typeof this.observables[eventName] == 'undefined') {
|
||||||
|
// No observable for this event, create a new one.
|
||||||
|
this.observables[eventName] = new Subject<any>();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.observables[eventName].subscribe(callBack);
|
||||||
|
|
||||||
|
// Create and return a CoreEventObserver.
|
||||||
|
return {
|
||||||
|
off: () => {
|
||||||
|
this.logger.debug(`Stop listening to event '${eventName}'`);
|
||||||
|
this.observables[eventName].unsubscribe(callBack);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggers an event, notifying all the observers.
|
||||||
|
*
|
||||||
|
* @param {string} event Name of the event to trigger.
|
||||||
|
* @param {any} data Data to pass to the observers.
|
||||||
|
*/
|
||||||
|
trigger(eventName: string, data: any) : void {
|
||||||
|
this.logger.debug(`Event '${eventName}' triggered.`);
|
||||||
|
if (this.observables[eventName]) {
|
||||||
|
this.observables[eventName].next(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggers a unique event, notifying all the observers. If the event has already been triggered, don't do anything.
|
||||||
|
*
|
||||||
|
* @param {string} event Name of the event to trigger.
|
||||||
|
* @param {any} data Data to pass to the observers.
|
||||||
|
*/
|
||||||
|
triggerUnique(eventName: string, data: any) : void {
|
||||||
|
if (this.uniqueEvents[eventName]) {
|
||||||
|
this.logger.debug(`Unique event '${eventName}' ignored because it was already triggered.`);
|
||||||
|
} else {
|
||||||
|
this.logger.debug(`Unique event '${eventName}' triggered.`);
|
||||||
|
// Store the data so it can be passed to observers that register from now on.
|
||||||
|
this.uniqueEvents[eventName] = {
|
||||||
|
data: data
|
||||||
|
};
|
||||||
|
|
||||||
|
// Now pass the data to observers.
|
||||||
|
if (this.observables[eventName]) {
|
||||||
|
this.observables[eventName].next(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
// (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, Injector } from '@angular/core';
|
||||||
|
import { CoreSite } from '../classes/site';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Provider to create sites instances.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class CoreSitesFactoryProvider {
|
||||||
|
|
||||||
|
constructor(private injector: Injector) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a site object.
|
||||||
|
*
|
||||||
|
* @param {string} id Site ID.
|
||||||
|
* @param {string} siteUrl Site URL.
|
||||||
|
* @param {string} token Site's WS token.
|
||||||
|
* @param {any} info Site info.
|
||||||
|
* @param {string} [privateToken] Private token.
|
||||||
|
* @param {any} [config] Site public config.
|
||||||
|
* @param {boolean} [loggedOut] Whether user is logged out.
|
||||||
|
* @return {CoreSite} Site instance.
|
||||||
|
* @description
|
||||||
|
* This returns a site object.
|
||||||
|
*/
|
||||||
|
makeSite = function(id: string, siteUrl: string, token: string, info: any, privateToken?: string,
|
||||||
|
config?: any, loggedOut?: boolean) : CoreSite {
|
||||||
|
return new CoreSite(this.injector, id, siteUrl, token, info, privateToken, config, loggedOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the list of Site methods.
|
||||||
|
*
|
||||||
|
* @return {string[]} List of methods.
|
||||||
|
*/
|
||||||
|
getSiteMethods(): string[] {
|
||||||
|
let methods = [];
|
||||||
|
for (let name in CoreSite.prototype) {
|
||||||
|
methods.push(name);
|
||||||
|
}
|
||||||
|
return methods;
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,25 +25,6 @@ export class CoreTextUtilsProvider {
|
||||||
|
|
||||||
constructor(private translate: TranslateService, private langProvider: CoreLangProvider) {}
|
constructor(private translate: TranslateService, private langProvider: CoreLangProvider) {}
|
||||||
|
|
||||||
/**
|
|
||||||
* Add or remove 'www' from a URL. The url needs to have http or https protocol.
|
|
||||||
*
|
|
||||||
* @param {string} url URL to modify.
|
|
||||||
* @return {string} Modified URL.
|
|
||||||
*/
|
|
||||||
addOrRemoveWWW(url: string) : string {
|
|
||||||
if (url) {
|
|
||||||
if (url.match(/http(s)?:\/\/www\./)) {
|
|
||||||
// Already has www. Remove it.
|
|
||||||
url = url.replace('www.', '');
|
|
||||||
} else {
|
|
||||||
url = url.replace('https://', 'https://www.');
|
|
||||||
url = url.replace('http://', 'http://www.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a list of sentences, build a message with all of them wrapped in <p>.
|
* Given a list of sentences, build a message with all of them wrapped in <p>.
|
||||||
*
|
*
|
||||||
|
@ -326,47 +307,6 @@ export class CoreTextUtilsProvider {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Formats a URL, trim, lowercase, etc...
|
|
||||||
*
|
|
||||||
* @param {string} url The url to be formatted.
|
|
||||||
* @return {string} Fromatted url.
|
|
||||||
*/
|
|
||||||
formatURL(url: string) : string {
|
|
||||||
url = url.trim();
|
|
||||||
|
|
||||||
// Check if the URL starts by http or https.
|
|
||||||
if (! /^http(s)?\:\/\/.*/i.test(url)) {
|
|
||||||
// Test first allways https.
|
|
||||||
url = 'https://' + url;
|
|
||||||
}
|
|
||||||
|
|
||||||
// http allways in lowercase.
|
|
||||||
url = url.replace(/^http/i, 'http');
|
|
||||||
url = url.replace(/^https/i, 'https');
|
|
||||||
|
|
||||||
// Replace last slash.
|
|
||||||
url = url.replace(/\/$/, "");
|
|
||||||
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Given a URL, returns what's after the last '/' without params.
|
|
||||||
* Example:
|
|
||||||
* http://mysite.com/a/course.html?id=1 -> course.html
|
|
||||||
*
|
|
||||||
* @param {string} url URL to treat.
|
|
||||||
* @return {string} Last file without params.
|
|
||||||
*/
|
|
||||||
getLastFileWithoutParams(url: string) : string {
|
|
||||||
let filename = url.substr(url.lastIndexOf('/') + 1);
|
|
||||||
if (filename.indexOf('?') != -1) {
|
|
||||||
filename = filename.substr(0, filename.indexOf('?'));
|
|
||||||
}
|
|
||||||
return filename;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the pluginfile URL to replace @@PLUGINFILE@@ wildcards.
|
* Get the pluginfile URL to replace @@PLUGINFILE@@ wildcards.
|
||||||
*
|
*
|
||||||
|
@ -383,61 +323,6 @@ export class CoreTextUtilsProvider {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the protocol from a URL.
|
|
||||||
* E.g. http://www.google.com returns 'http'.
|
|
||||||
*
|
|
||||||
* @param {string} url URL to treat.
|
|
||||||
* @return {string} Protocol, undefined if no protocol found.
|
|
||||||
*/
|
|
||||||
getUrlProtocol(url: string) : string {
|
|
||||||
if (!url) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let matches = url.match(/^([^\/:\.\?]*):\/\//);
|
|
||||||
if (matches && matches[1]) {
|
|
||||||
return matches[1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the scheme from a URL. Please notice that, if a URL has protocol, it will return the protocol.
|
|
||||||
* E.g. javascript:doSomething() returns 'javascript'.
|
|
||||||
*
|
|
||||||
* @param {string} url URL to treat.
|
|
||||||
* @return {string} Scheme, undefined if no scheme found.
|
|
||||||
*/
|
|
||||||
getUrlScheme(url: string) : string {
|
|
||||||
if (!url) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let matches = url.match(/^([a-z][a-z0-9+\-.]*):/);
|
|
||||||
if (matches && matches[1]) {
|
|
||||||
return matches[1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Gets a username from a URL like: user@mysite.com.
|
|
||||||
*
|
|
||||||
* @param {string} url URL to treat.
|
|
||||||
* @return {string} Username. Undefined if no username found.
|
|
||||||
*/
|
|
||||||
getUsernameFromUrl(url: string) : string {
|
|
||||||
if (url.indexOf('@') > -1) {
|
|
||||||
// Get URL without protocol.
|
|
||||||
let withoutProtocol = url.replace(/.*?:\/\//, ''),
|
|
||||||
matches = withoutProtocol.match(/[^@]*/);
|
|
||||||
|
|
||||||
// Make sure that @ is at the start of the URL, not in a param at the end.
|
|
||||||
if (matches && matches.length && !matches[0].match(/[\/|?]/)) {
|
|
||||||
return matches[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a text contains HTML tags.
|
* Check if a text contains HTML tags.
|
||||||
*
|
*
|
||||||
|
@ -498,20 +383,6 @@ export class CoreTextUtilsProvider {
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove protocol and www from a URL.
|
|
||||||
*
|
|
||||||
* @param {string} url URL to treat.
|
|
||||||
* @return {string} Treated URL.
|
|
||||||
*/
|
|
||||||
removeProtocolAndWWW(url: string) : string {
|
|
||||||
// Remove protocol.
|
|
||||||
url = url.replace(/.*?:\/\//g, '');
|
|
||||||
// Remove www.
|
|
||||||
url = url.replace(/^www./, '');
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replace all characters that cause problems with files in Android and iOS.
|
* Replace all characters that cause problems with files in Android and iOS.
|
||||||
*
|
*
|
||||||
|
|
|
@ -23,6 +23,25 @@ export class CoreUrlUtilsProvider {
|
||||||
|
|
||||||
constructor(private langProvider: CoreLangProvider) {}
|
constructor(private langProvider: CoreLangProvider) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add or remove 'www' from a URL. The url needs to have http or https protocol.
|
||||||
|
*
|
||||||
|
* @param {string} url URL to modify.
|
||||||
|
* @return {string} Modified URL.
|
||||||
|
*/
|
||||||
|
addOrRemoveWWW(url: string) : string {
|
||||||
|
if (url) {
|
||||||
|
if (url.match(/http(s)?:\/\/www\./)) {
|
||||||
|
// Already has www. Remove it.
|
||||||
|
url = url.replace('www.', '');
|
||||||
|
} else {
|
||||||
|
url = url.replace('https://', 'https://www.');
|
||||||
|
url = url.replace('http://', 'http://www.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts the parameters from a URL and stores them in an object.
|
* Extracts the parameters from a URL and stores them in an object.
|
||||||
*
|
*
|
||||||
|
@ -41,6 +60,71 @@ export class CoreUrlUtilsProvider {
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic function for adding the wstoken to Moodle urls and for pointing to the correct script.
|
||||||
|
* For download remote files from Moodle we need to use the special /webservice/pluginfile passing
|
||||||
|
* the ws token as a get parameter.
|
||||||
|
*
|
||||||
|
* @param {string} url The url to be fixed.
|
||||||
|
* @param {string} token Token to use.
|
||||||
|
* @return {string} Fixed URL.
|
||||||
|
*/
|
||||||
|
fixPluginfileURL(url: string, token: string) : string {
|
||||||
|
if (!url || !token) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// First check if we need to fix this url or is already fixed.
|
||||||
|
if (url.indexOf('token=') != -1) {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if is a valid URL (contains the pluginfile endpoint).
|
||||||
|
if (!this.isPluginFileUrl(url)) {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
// In which way the server is serving the files? Are we using slash parameters?
|
||||||
|
if (url.indexOf('?file=') != -1 || url.indexOf('?forcedownload=') != -1 || url.indexOf('?rev=') != -1) {
|
||||||
|
url += '&';
|
||||||
|
} else {
|
||||||
|
url += '?';
|
||||||
|
}
|
||||||
|
// Always send offline=1 (for external repositories). It shouldn't cause problems for local files or old Moodles.
|
||||||
|
url += 'token=' + token + '&offline=1';
|
||||||
|
|
||||||
|
// Some webservices returns directly the correct download url, others not.
|
||||||
|
if (url.indexOf('/webservice/pluginfile') == -1) {
|
||||||
|
url = url.replace('/pluginfile', '/webservice/pluginfile');
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats a URL, trim, lowercase, etc...
|
||||||
|
*
|
||||||
|
* @param {string} url The url to be formatted.
|
||||||
|
* @return {string} Fromatted url.
|
||||||
|
*/
|
||||||
|
formatURL(url: string) : string {
|
||||||
|
url = url.trim();
|
||||||
|
|
||||||
|
// Check if the URL starts by http or https.
|
||||||
|
if (! /^http(s)?\:\/\/.*/i.test(url)) {
|
||||||
|
// Test first allways https.
|
||||||
|
url = 'https://' + url;
|
||||||
|
}
|
||||||
|
|
||||||
|
// http allways in lowercase.
|
||||||
|
url = url.replace(/^http/i, 'http');
|
||||||
|
url = url.replace(/^https/i, 'https');
|
||||||
|
|
||||||
|
// Replace last slash.
|
||||||
|
url = url.replace(/\/$/, "");
|
||||||
|
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the URL to the documentation of the app, based on Moodle version and current language.
|
* Returns the URL to the documentation of the app, based on Moodle version and current language.
|
||||||
*
|
*
|
||||||
|
@ -67,6 +151,77 @@ export class CoreUrlUtilsProvider {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a URL, returns what's after the last '/' without params.
|
||||||
|
* Example:
|
||||||
|
* http://mysite.com/a/course.html?id=1 -> course.html
|
||||||
|
*
|
||||||
|
* @param {string} url URL to treat.
|
||||||
|
* @return {string} Last file without params.
|
||||||
|
*/
|
||||||
|
getLastFileWithoutParams(url: string) : string {
|
||||||
|
let filename = url.substr(url.lastIndexOf('/') + 1);
|
||||||
|
if (filename.indexOf('?') != -1) {
|
||||||
|
filename = filename.substr(0, filename.indexOf('?'));
|
||||||
|
}
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the protocol from a URL.
|
||||||
|
* E.g. http://www.google.com returns 'http'.
|
||||||
|
*
|
||||||
|
* @param {string} url URL to treat.
|
||||||
|
* @return {string} Protocol, undefined if no protocol found.
|
||||||
|
*/
|
||||||
|
getUrlProtocol(url: string) : string {
|
||||||
|
if (!url) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let matches = url.match(/^([^\/:\.\?]*):\/\//);
|
||||||
|
if (matches && matches[1]) {
|
||||||
|
return matches[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the scheme from a URL. Please notice that, if a URL has protocol, it will return the protocol.
|
||||||
|
* E.g. javascript:doSomething() returns 'javascript'.
|
||||||
|
*
|
||||||
|
* @param {string} url URL to treat.
|
||||||
|
* @return {string} Scheme, undefined if no scheme found.
|
||||||
|
*/
|
||||||
|
getUrlScheme(url: string) : string {
|
||||||
|
if (!url) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let matches = url.match(/^([a-z][a-z0-9+\-.]*):/);
|
||||||
|
if (matches && matches[1]) {
|
||||||
|
return matches[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Gets a username from a URL like: user@mysite.com.
|
||||||
|
*
|
||||||
|
* @param {string} url URL to treat.
|
||||||
|
* @return {string} Username. Undefined if no username found.
|
||||||
|
*/
|
||||||
|
getUsernameFromUrl(url: string) : string {
|
||||||
|
if (url.indexOf('@') > -1) {
|
||||||
|
// Get URL without protocol.
|
||||||
|
let withoutProtocol = url.replace(/.*?:\/\//, ''),
|
||||||
|
matches = withoutProtocol.match(/[^@]*/);
|
||||||
|
|
||||||
|
// Make sure that @ is at the start of the URL, not in a param at the end.
|
||||||
|
if (matches && matches.length && !matches[0].match(/[\/|?]/)) {
|
||||||
|
return matches[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns if a URL has any protocol (not a relative URL).
|
* Returns if a URL has any protocol (not a relative URL).
|
||||||
*
|
*
|
||||||
|
@ -128,42 +283,16 @@ export class CoreUrlUtilsProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generic function for adding the wstoken to Moodle urls and for pointing to the correct script.
|
* Remove protocol and www from a URL.
|
||||||
* For download remote files from Moodle we need to use the special /webservice/pluginfile passing
|
|
||||||
* the ws token as a get parameter.
|
|
||||||
*
|
*
|
||||||
* @param {string} url The url to be fixed.
|
* @param {string} url URL to treat.
|
||||||
* @param {string} token Token to use.
|
* @return {string} Treated URL.
|
||||||
* @return {string} Fixed URL.
|
|
||||||
*/
|
*/
|
||||||
fixPluginfileURL(url: string, token: string) : string {
|
removeProtocolAndWWW(url: string) : string {
|
||||||
if (!url || !token) {
|
// Remove protocol.
|
||||||
return '';
|
url = url.replace(/.*?:\/\//g, '');
|
||||||
}
|
// Remove www.
|
||||||
|
url = url.replace(/^www./, '');
|
||||||
// First check if we need to fix this url or is already fixed.
|
|
||||||
if (url.indexOf('token=') != -1) {
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if is a valid URL (contains the pluginfile endpoint).
|
|
||||||
if (!this.isPluginFileUrl(url)) {
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
|
|
||||||
// In which way the server is serving the files? Are we using slash parameters?
|
|
||||||
if (url.indexOf('?file=') != -1 || url.indexOf('?forcedownload=') != -1 || url.indexOf('?rev=') != -1) {
|
|
||||||
url += '&';
|
|
||||||
} else {
|
|
||||||
url += '?';
|
|
||||||
}
|
|
||||||
// Always send offline=1 (for external repositories). It shouldn't cause problems for local files or old Moodles.
|
|
||||||
url += 'token=' + token + '&offline=1';
|
|
||||||
|
|
||||||
// Some webservices returns directly the correct download url, others not.
|
|
||||||
if (url.indexOf('/webservice/pluginfile') == -1) {
|
|
||||||
url = url.replace('/pluginfile', '/webservice/pluginfile');
|
|
||||||
}
|
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Platform } from 'ionic-angular';
|
import { Platform } from 'ionic-angular';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
import { InAppBrowser, InAppBrowserObject } from '@ionic-native/in-app-browser';
|
import { InAppBrowser, InAppBrowserObject } from '@ionic-native/in-app-browser';
|
||||||
import { Clipboard } from '@ionic-native/clipboard';
|
import { Clipboard } from '@ionic-native/clipboard';
|
||||||
import { CoreAppProvider } from '../app';
|
import { CoreAppProvider } from '../app';
|
||||||
|
@ -349,6 +350,34 @@ export class CoreUtilsProvider {
|
||||||
return this.allPromises(promises);
|
return this.allPromises(promises);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flatten an object, moving subobjects' properties to the first level using dot notation. E.g.:
|
||||||
|
* {a: {b: 1, c: 2}, d: 3} -> {'a.b': 1, 'a.c': 2, d: 3}
|
||||||
|
*
|
||||||
|
* @param {object} obj Object to flatten.
|
||||||
|
* @return {object} Flatten object.
|
||||||
|
*/
|
||||||
|
flattenObject(obj: object) : object {
|
||||||
|
let toReturn = {};
|
||||||
|
|
||||||
|
for (let name in obj) {
|
||||||
|
if (!obj.hasOwnProperty(name)) continue;
|
||||||
|
|
||||||
|
if (typeof obj[name] == 'object') {
|
||||||
|
let flatObject = this.flattenObject(obj[name]);
|
||||||
|
for (let subName in flatObject) {
|
||||||
|
if (!flatObject.hasOwnProperty(subName)) continue;
|
||||||
|
|
||||||
|
toReturn[name + '.' + subName] = flatObject[subName];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
toReturn[name] = obj[name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return toReturn;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given an array of strings, return only the ones that match a regular expression.
|
* Given an array of strings, return only the ones that match a regular expression.
|
||||||
*
|
*
|
||||||
|
@ -916,6 +945,18 @@ export class CoreUtilsProvider {
|
||||||
return mapped;
|
return mapped;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given an observable, convert it to a Promise that will resolve with the first received value.
|
||||||
|
*
|
||||||
|
* @param {Observable<any>} obs The observable to convert.
|
||||||
|
* @return {Promise<any>} Promise.
|
||||||
|
*/
|
||||||
|
observableToPromise(obs: Observable<any>) : Promise<any> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
obs.subscribe(resolve, reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Similar to AngularJS $q.defer(). It will return an object containing the promise, and the resolve and reject functions.
|
* Similar to AngularJS $q.defer(). It will return an object containing the promise, and the resolve and reject functions.
|
||||||
*
|
*
|
||||||
|
@ -1021,6 +1062,17 @@ export class CoreUtilsProvider {
|
||||||
return query.length ? query.substr(0, query.length - 1) : query;
|
return query.length ? query.substr(0, query.length - 1) : query;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stringify an object, sorting the properties. It doesn't sort arrays, only object properties. E.g.:
|
||||||
|
* {b: 2, a: 1} -> '{"a":1,"b":2}'
|
||||||
|
*
|
||||||
|
* @param {object} obj Object to stringify.
|
||||||
|
* @return {string} Stringified object.
|
||||||
|
*/
|
||||||
|
sortAndStringify(obj: object) : string {
|
||||||
|
return JSON.stringify(obj, Object.keys(this.flattenObject(obj)).sort());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sum the filesizes from a list of files checking if the size will be partial or totally calculated.
|
* Sum the filesizes from a list of files checking if the size will be partial or totally calculated.
|
||||||
*
|
*
|
||||||
|
|
|
@ -104,7 +104,7 @@ export class CoreWSProvider {
|
||||||
* A wrapper function for a moodle WebService call.
|
* A wrapper function for a moodle WebService call.
|
||||||
*
|
*
|
||||||
* @param {string} method The WebService method to be called.
|
* @param {string} method The WebService method to be called.
|
||||||
* @param {any} data Arguments to pass to the method.
|
* @param {any} data Arguments to pass to the method. It's recommended to call convertValuesToString before passing the data.
|
||||||
* @param {CoreWSPreSets} preSets Extra settings and information.
|
* @param {CoreWSPreSets} preSets Extra settings and information.
|
||||||
* @return {Promise} Promise resolved with the response data in success and rejected with the error message if it fails.
|
* @return {Promise} Promise resolved with the response data in success and rejected with the error message if it fails.
|
||||||
*/
|
*/
|
||||||
|
@ -123,13 +123,6 @@ export class CoreWSProvider {
|
||||||
preSets.responseExpected = true;
|
preSets.responseExpected = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
data = this.convertValuesToString(data, preSets.cleanUnicode);
|
|
||||||
} catch (e) {
|
|
||||||
// Empty cleaned text found.
|
|
||||||
return Promise.reject(this.createFakeWSError('mm.core.unicodenotsupportedcleanerror', true));
|
|
||||||
}
|
|
||||||
|
|
||||||
data.wsfunction = method;
|
data.wsfunction = method;
|
||||||
data.wstoken = preSets.wsToken;
|
data.wstoken = preSets.wsToken;
|
||||||
siteUrl = preSets.siteUrl + '/webservice/rest/server.php?moodlewsrestformat=json';
|
siteUrl = preSets.siteUrl + '/webservice/rest/server.php?moodlewsrestformat=json';
|
||||||
|
@ -182,33 +175,32 @@ export class CoreWSProvider {
|
||||||
|
|
||||||
siteUrl = preSets.siteUrl + '/lib/ajax/service.php';
|
siteUrl = preSets.siteUrl + '/lib/ajax/service.php';
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
let observable = this.http.post(siteUrl, JSON.stringify(ajaxData)).timeout(CoreConstants.wsTimeout);
|
||||||
this.http.post(siteUrl, JSON.stringify(ajaxData)).timeout(CoreConstants.wsTimeout).subscribe((data: any) => {
|
return this.utils.observableToPromise(observable).then((data: any) => {
|
||||||
// Some moodle web services return null. If the responseExpected value is set then so long as no data
|
// Some moodle web services return null. If the responseExpected value is set then so long as no data
|
||||||
// is returned, we create a blank object.
|
// is returned, we create a blank object.
|
||||||
if (!data && !preSets.responseExpected) {
|
if (!data && !preSets.responseExpected) {
|
||||||
data = [{}];
|
data = [{}];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if error. Ajax layer should always return an object (if error) or an array (if success).
|
// Check if error. Ajax layer should always return an object (if error) or an array (if success).
|
||||||
if (!data || typeof data != 'object') {
|
if (!data || typeof data != 'object') {
|
||||||
return rejectWithError(this.translate.instant('mm.core.serverconnection'));
|
return rejectWithError(this.translate.instant('mm.core.serverconnection'));
|
||||||
} else if (data.error) {
|
} else if (data.error) {
|
||||||
return rejectWithError(data.error, data.errorcode);
|
return rejectWithError(data.error, data.errorcode);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the first response since only one request was done.
|
// Get the first response since only one request was done.
|
||||||
data = data[0];
|
data = data[0];
|
||||||
|
|
||||||
if (data.error) {
|
if (data.error) {
|
||||||
return rejectWithError(data.exception.message, data.exception.errorcode);
|
return rejectWithError(data.exception.message, data.exception.errorcode);
|
||||||
}
|
}
|
||||||
|
|
||||||
return data.data;
|
return data.data;
|
||||||
}, (data) => {
|
}, (data) => {
|
||||||
let available = data.status == 404 ? -1 : 0;
|
let available = data.status == 404 ? -1 : 0;
|
||||||
return rejectWithError(this.translate.instant('mm.core.serverconnection'), '', available);
|
return rejectWithError(this.translate.instant('mm.core.serverconnection'), '', available);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Convenience function to return an error.
|
// Convenience function to return an error.
|
||||||
|
@ -237,7 +229,7 @@ export class CoreWSProvider {
|
||||||
* @param {boolean} [stripUnicode] If Unicode long chars need to be stripped.
|
* @param {boolean} [stripUnicode] If Unicode long chars need to be stripped.
|
||||||
* @return {object} The cleaned object, with multilevel array and objects preserved.
|
* @return {object} The cleaned object, with multilevel array and objects preserved.
|
||||||
*/
|
*/
|
||||||
protected convertValuesToString(data: object, stripUnicode?: boolean) : object {
|
convertValuesToString(data: object, stripUnicode?: boolean) : object {
|
||||||
let result;
|
let result;
|
||||||
if (!Array.isArray(data) && typeof data == 'object') {
|
if (!Array.isArray(data) && typeof data == 'object') {
|
||||||
result = {};
|
result = {};
|
||||||
|
@ -435,10 +427,7 @@ export class CoreWSProvider {
|
||||||
let promise = this.getPromiseHttp('head', url);
|
let promise = this.getPromiseHttp('head', url);
|
||||||
|
|
||||||
if (!promise) {
|
if (!promise) {
|
||||||
promise = new Promise((resolve, reject) => {
|
promise = this.utils.observableToPromise(this.http.head(url).timeout(CoreConstants.wsTimeout));
|
||||||
this.http.head(url).timeout(CoreConstants.wsTimeout).subscribe(resolve, reject);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.setPromiseHttp(promise, 'head', url);
|
this.setPromiseHttp(promise, 'head', url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -455,58 +444,58 @@ export class CoreWSProvider {
|
||||||
* @return {Promise<any>} Promise resolved with the response data in success and rejected with CoreWSError if it fails.
|
* @return {Promise<any>} Promise resolved with the response data in success and rejected with CoreWSError if it fails.
|
||||||
*/
|
*/
|
||||||
performPost(method: string, siteUrl: string, ajaxData: any, preSets: CoreWSPreSets) : Promise<any> {
|
performPost(method: string, siteUrl: string, ajaxData: any, preSets: CoreWSPreSets) : Promise<any> {
|
||||||
// Create the promise for the request.
|
// Perform the post request.
|
||||||
let promise = new Promise((resolve, reject) => {
|
let observable = this.http.post(siteUrl, ajaxData).timeout(CoreConstants.wsTimeout),
|
||||||
|
promise;
|
||||||
|
|
||||||
this.http.post(siteUrl, ajaxData).timeout(CoreConstants.wsTimeout).subscribe((data: any) => {
|
promise = this.utils.observableToPromise(observable).then((data: any) => {
|
||||||
// Some moodle web services return null.
|
// Some moodle web services return null.
|
||||||
// If the responseExpected value is set to false, we create a blank object if the response is null.
|
// If the responseExpected value is set to false, we create a blank object if the response is null.
|
||||||
if (!data && !preSets.responseExpected) {
|
if (!data && !preSets.responseExpected) {
|
||||||
data = {};
|
data = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!data) {
|
|
||||||
return Promise.reject(this.createFakeWSError('mm.core.serverconnection', true));
|
|
||||||
} else if (typeof data != preSets.typeExpected) {
|
|
||||||
this.logger.warn('Response of type "' + typeof data + `" received, expecting "${preSets.typeExpected}"`);
|
|
||||||
return Promise.reject(this.createFakeWSError('mm.core.errorinvalidresponse', true));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof data.exception !== 'undefined') {
|
|
||||||
return Promise.reject(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof data.debuginfo != 'undefined') {
|
|
||||||
return Promise.reject(this.createFakeWSError('Error. ' + data.message));
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}, (error) => {
|
|
||||||
// If server has heavy load, retry after some seconds.
|
|
||||||
if (error.status == 429) {
|
|
||||||
let retryPromise = this.addToRetryQueue(method, siteUrl, ajaxData, preSets);
|
|
||||||
|
|
||||||
// Only process the queue one time.
|
|
||||||
if (this.retryTimeout == 0) {
|
|
||||||
this.retryTimeout = parseInt(error.headers('Retry-After'), 10) || 5;
|
|
||||||
this.logger.warn(`${error.statusText}. Retrying in ${this.retryTimeout} seconds. ` +
|
|
||||||
`${this.retryCalls.length} calls left.`);
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
this.logger.warn(`Retrying now with ${this.retryCalls.length} calls to process.`);
|
|
||||||
// Finish timeout.
|
|
||||||
this.retryTimeout = 0;
|
|
||||||
this.processRetryQueue();
|
|
||||||
}, this.retryTimeout * 1000);
|
|
||||||
} else {
|
|
||||||
this.logger.warn('Calls locked, trying later...');
|
|
||||||
}
|
|
||||||
|
|
||||||
return retryPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
return Promise.reject(this.createFakeWSError('mm.core.serverconnection', true));
|
return Promise.reject(this.createFakeWSError('mm.core.serverconnection', true));
|
||||||
});
|
} else if (typeof data != preSets.typeExpected) {
|
||||||
|
this.logger.warn('Response of type "' + typeof data + `" received, expecting "${preSets.typeExpected}"`);
|
||||||
|
return Promise.reject(this.createFakeWSError('mm.core.errorinvalidresponse', true));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof data.exception !== 'undefined') {
|
||||||
|
return Promise.reject(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof data.debuginfo != 'undefined') {
|
||||||
|
return Promise.reject(this.createFakeWSError('Error. ' + data.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}, (error) => {
|
||||||
|
// If server has heavy load, retry after some seconds.
|
||||||
|
if (error.status == 429) {
|
||||||
|
let retryPromise = this.addToRetryQueue(method, siteUrl, ajaxData, preSets);
|
||||||
|
|
||||||
|
// Only process the queue one time.
|
||||||
|
if (this.retryTimeout == 0) {
|
||||||
|
this.retryTimeout = parseInt(error.headers('Retry-After'), 10) || 5;
|
||||||
|
this.logger.warn(`${error.statusText}. Retrying in ${this.retryTimeout} seconds. ` +
|
||||||
|
`${this.retryCalls.length} calls left.`);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.logger.warn(`Retrying now with ${this.retryCalls.length} calls to process.`);
|
||||||
|
// Finish timeout.
|
||||||
|
this.retryTimeout = 0;
|
||||||
|
this.processRetryQueue();
|
||||||
|
}, this.retryTimeout * 1000);
|
||||||
|
} else {
|
||||||
|
this.logger.warn('Calls locked, trying later...');
|
||||||
|
}
|
||||||
|
|
||||||
|
return retryPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject(this.createFakeWSError('mm.core.serverconnection', true));
|
||||||
});
|
});
|
||||||
|
|
||||||
this.setPromiseHttp(promise, 'post', preSets.siteUrl, ajaxData);
|
this.setPromiseHttp(promise, 'post', preSets.siteUrl, ajaxData);
|
||||||
|
|
Loading…
Reference in New Issue