commit
afae593ed8
|
@ -8,6 +8,7 @@
|
|||
<access launch-external="yes" origin="tel:*" />
|
||||
<access launch-external="yes" origin="mailto:*" />
|
||||
<access launch-external="yes" origin="geo:*" />
|
||||
<allow-navigation href="moodleappfs:*" />
|
||||
<allow-navigation href="cdvfile:*" />
|
||||
<allow-navigation href="content:*" />
|
||||
<allow-navigation href="data:*" />
|
||||
|
@ -38,6 +39,8 @@
|
|||
<preference name="SplashShowOnlyFirstTime" value="false" />
|
||||
<preference name="LoadUrlTimeoutValue" value="60000" />
|
||||
<preference name="CustomURLSchemePluginClearsAndroidIntent" value="true" />
|
||||
<preference name="Scheme" value="moodleappfs" />
|
||||
<preference name="iosScheme" value="moodleappfs" />
|
||||
<feature name="StatusBar">
|
||||
<param name="ios-package" onload="true" value="CDVStatusBar" />
|
||||
</feature>
|
||||
|
@ -171,6 +174,7 @@
|
|||
<plugin name="cordova-plugin-splashscreen" spec="5.0.3" />
|
||||
<plugin name="cordova-plugin-statusbar" spec="2.4.3" />
|
||||
<plugin name="cordova-plugin-whitelist" spec="1.3.4" />
|
||||
<plugin name="cordova-plugin-wkwebview-cookies" spec="https://github.com/moodlemobile/cordova-plugin-wkwebview-cookies.git" />
|
||||
<plugin name="cordova-plugin-zip" spec="3.1.0" />
|
||||
<plugin name="cordova-sqlite-storage" spec="3.4.0" />
|
||||
<plugin name="nl.kingsquare.cordova.background-audio" spec="1.0.1" />
|
||||
|
@ -178,6 +182,8 @@
|
|||
<variable name="ANDROID_SUPPORT_V13_VERSION" value="27.+" />
|
||||
<variable name="FCM_VERSION" value="17.5.+" />
|
||||
</plugin>
|
||||
<plugin name="cordova-plugin-ionic-webview" spec="4.1.3" />
|
||||
<plugin name="cordova-plugin-advanced-http" spec="2.4.1" />
|
||||
<edit-config file="AndroidManifest.xml" mode="merge" target="/manifest/application/activity[@android:name='MainActivity']">
|
||||
<activity android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|screenLayout|smallestScreenSize" android:debuggable="true" />
|
||||
</edit-config>
|
||||
|
|
|
@ -12,7 +12,8 @@ const customConfig = {
|
|||
'@providers': resolve('./src/providers'),
|
||||
'@components': resolve('./src/components'),
|
||||
'@directives': resolve('./src/directives'),
|
||||
'@pipes': resolve('./src/pipes')
|
||||
'@pipes': resolve('./src/pipes'),
|
||||
'@singletons': resolve('./src/singletons'),
|
||||
}
|
||||
},
|
||||
externals: [
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "moodlemobile",
|
||||
"version": "3.8.1",
|
||||
"version": "3.8.2",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
@ -147,6 +147,11 @@
|
|||
"resolved": "https://registry.npmjs.org/@ionic-native/globalization/-/globalization-4.20.0.tgz",
|
||||
"integrity": "sha512-zyxaW+vZb1OHeDgGbrZHQe3hy30K4YeKjGr8KNGcwq+k2ZHkfqo/H6XIwf2m/UlFTgacvdR9XZtfP+6N0suybg=="
|
||||
},
|
||||
"@ionic-native/http": {
|
||||
"version": "4.20.0",
|
||||
"resolved": "https://registry.npmjs.org/@ionic-native/http/-/http-4.20.0.tgz",
|
||||
"integrity": "sha512-DF+Y1oYoHTv9Y22a2jLgniOmj9Twba+9j8rzHA4xboVT2HpB6bsBSWOktdAXDVjoajXiLsA/u7fh6YD8//NVGg=="
|
||||
},
|
||||
"@ionic-native/in-app-browser": {
|
||||
"version": "4.20.0",
|
||||
"resolved": "https://registry.npmjs.org/@ionic-native/in-app-browser/-/in-app-browser-4.20.0.tgz",
|
||||
|
@ -1366,7 +1371,8 @@
|
|||
},
|
||||
"kind-of": {
|
||||
"version": "6.0.2",
|
||||
"resolved": ""
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
|
||||
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -2620,6 +2626,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"cordova-plugin-advanced-http": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/cordova-plugin-advanced-http/-/cordova-plugin-advanced-http-2.4.1.tgz",
|
||||
"integrity": "sha512-6G8MTy/d02jE6n3Y9CVyCtD5hZGiBb+/dR2AIzhKN1RGGz38g1D2C8yE4MqHRvnmry6k/KHQWT1MsHNXrjouXQ=="
|
||||
},
|
||||
"cordova-plugin-badge": {
|
||||
"version": "0.8.8",
|
||||
"resolved": "https://registry.npmjs.org/cordova-plugin-badge/-/cordova-plugin-badge-0.8.8.tgz",
|
||||
|
@ -2675,6 +2686,11 @@
|
|||
"resolved": "https://registry.npmjs.org/cordova-plugin-ionic-keyboard/-/cordova-plugin-ionic-keyboard-2.1.3.tgz",
|
||||
"integrity": "sha512-6ucQ6FdlLdBm8kJfFnzozmBTjru/0xekHP/dAhjoCZggkGRlgs8TsUJFkxa/bV+qi7Dlo50JjmpE4UMWAO+aOQ=="
|
||||
},
|
||||
"cordova-plugin-ionic-webview": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/cordova-plugin-ionic-webview/-/cordova-plugin-ionic-webview-4.1.3.tgz",
|
||||
"integrity": "sha512-hlrUF0kLjjEkZmpYlLJO0NnXmVjMmQ3MOZVXm1ytDihLPKHklYCOpCvjA5Wz3hJrPD1shFEsqi/SPnp873AsdQ=="
|
||||
},
|
||||
"cordova-plugin-local-notification": {
|
||||
"version": "git+https://github.com/moodlemobile/cordova-plugin-local-notification.git#0bb96b757fb484553ceabf35a59802f7983a2836",
|
||||
"from": "git+https://github.com/moodlemobile/cordova-plugin-local-notification.git#moodle"
|
||||
|
@ -2709,6 +2725,10 @@
|
|||
"resolved": "https://registry.npmjs.org/cordova-plugin-whitelist/-/cordova-plugin-whitelist-1.3.4.tgz",
|
||||
"integrity": "sha512-EYC5eQFVkoYXq39l7tYKE6lEjHJ04mvTmKXxGL7quHLdFPfJMNzru/UYpn92AOfpl3PQaZmou78C7EgmFOwFQQ=="
|
||||
},
|
||||
"cordova-plugin-wkwebview-cookies": {
|
||||
"version": "git+https://github.com/moodlemobile/cordova-plugin-wkwebview-cookies.git#0f20979803ffd9b13250215975692703342d759c",
|
||||
"from": "git+https://github.com/moodlemobile/cordova-plugin-wkwebview-cookies.git"
|
||||
},
|
||||
"cordova-plugin-zip": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cordova-plugin-zip/-/cordova-plugin-zip-3.1.0.tgz",
|
||||
|
@ -6672,7 +6692,8 @@
|
|||
},
|
||||
"kind-of": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
|
||||
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
|
||||
"dev": true
|
||||
},
|
||||
"micromatch": {
|
||||
|
@ -11323,7 +11344,8 @@
|
|||
},
|
||||
"kind-of": {
|
||||
"version": "6.0.2",
|
||||
"resolved": ""
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
|
||||
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -13484,7 +13506,8 @@
|
|||
},
|
||||
"kind-of": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
|
||||
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
|
||||
"dev": true
|
||||
},
|
||||
"micromatch": {
|
||||
|
|
11
package.json
11
package.json
|
@ -59,6 +59,7 @@
|
|||
"@ionic-native/file-transfer": "4.20.0",
|
||||
"@ionic-native/geolocation": "4.20.0",
|
||||
"@ionic-native/globalization": "4.20.0",
|
||||
"@ionic-native/http": "^4.20.0",
|
||||
"@ionic-native/in-app-browser": "4.20.0",
|
||||
"@ionic-native/keyboard": "4.20.0",
|
||||
"@ionic-native/local-notifications": "4.20.0",
|
||||
|
@ -81,6 +82,7 @@
|
|||
"cordova-android-support-gradle-release": "3.0.1",
|
||||
"cordova-clipboard": "1.3.0",
|
||||
"cordova-ios": "5.1.1",
|
||||
"cordova-plugin-advanced-http": "2.4.1",
|
||||
"cordova-plugin-badge": "0.8.8",
|
||||
"cordova-plugin-camera": "4.1.0",
|
||||
"cordova-plugin-customurlscheme": "5.0.0",
|
||||
|
@ -92,6 +94,7 @@
|
|||
"cordova-plugin-globalization": "1.11.0",
|
||||
"cordova-plugin-inappbrowser": "3.2.0",
|
||||
"cordova-plugin-ionic-keyboard": "2.1.3",
|
||||
"cordova-plugin-ionic-webview": "4.1.3",
|
||||
"cordova-plugin-local-notification": "git+https://github.com/moodlemobile/cordova-plugin-local-notification.git#moodle",
|
||||
"cordova-plugin-media-capture": "3.0.3",
|
||||
"cordova-plugin-network-information": "2.0.2",
|
||||
|
@ -99,6 +102,7 @@
|
|||
"cordova-plugin-splashscreen": "5.0.3",
|
||||
"cordova-plugin-statusbar": "2.4.3",
|
||||
"cordova-plugin-whitelist": "1.3.4",
|
||||
"cordova-plugin-wkwebview-cookies": "git+https://github.com/moodlemobile/cordova-plugin-wkwebview-cookies.git",
|
||||
"cordova-plugin-zip": "3.1.0",
|
||||
"cordova-sqlite-storage": "4.0.0",
|
||||
"cordova-support-google-services": "1.3.2",
|
||||
|
@ -185,7 +189,12 @@
|
|||
},
|
||||
"cordova-plugin-geolocation": {
|
||||
"GEOLOCATION_USAGE_DESCRIPTION": "To locate you"
|
||||
}
|
||||
},
|
||||
"cordova-plugin-ionic-webview": {},
|
||||
"cordova-plugin-advanced-http": {
|
||||
"OKHTTP_VERSION": "3.10.0"
|
||||
},
|
||||
"cordova-plugin-wkwebview-cookies": {}
|
||||
}
|
||||
},
|
||||
"main": "desktop/electron.js",
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { NgModule, COMPILER_OPTIONS } from '@angular/core';
|
||||
import { NgModule, COMPILER_OPTIONS, Injector } from '@angular/core';
|
||||
import { IonicApp, IonicModule, Platform, Content, ScrollEvent, Config, Refresher } from 'ionic-angular';
|
||||
import { assert } from 'ionic-angular/util/util';
|
||||
import { HttpClient, HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
|
||||
|
@ -155,6 +155,8 @@ import { AddonQtypeModule } from '@addon/qtype/qtype.module';
|
|||
import { AddonStorageManagerModule } from '@addon/storagemanager/storagemanager.module';
|
||||
import { AddonFilterModule } from '@addon/filter/filter.module';
|
||||
|
||||
import { setSingletonsInjector } from '@singletons/core.singletons';
|
||||
|
||||
// For translate loader. AoT requires an exported function for factories.
|
||||
export function createTranslateLoader(http: HttpClient): TranslateHttpLoader {
|
||||
return new TranslateHttpLoader(http, './assets/lang/', '.json');
|
||||
|
@ -357,6 +359,7 @@ export class AppModule {
|
|||
private eventsProvider: CoreEventsProvider,
|
||||
cronDelegate: CoreCronDelegate,
|
||||
siteInfoCronHandler: CoreSiteInfoCronHandler,
|
||||
injector: Injector,
|
||||
) {
|
||||
// Register a handler for platform ready.
|
||||
initDelegate.registerProcess({
|
||||
|
@ -391,6 +394,9 @@ export class AppModule {
|
|||
// Register handlers.
|
||||
cronDelegate.register(siteInfoCronHandler);
|
||||
|
||||
// Set the injector.
|
||||
setSingletonsInjector(injector);
|
||||
|
||||
// Set transition animation.
|
||||
config.setTransition('core-page-transition', CorePageTransition);
|
||||
config.setTransition('core-modal-lateral-transition', CoreModalLateralTransition);
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { HttpResponse as AngularHttpResponse, HttpHeaders } from '@angular/common/http';
|
||||
import { HTTPResponse as NativeHttpResponse } from '@ionic-native/http';
|
||||
|
||||
const HTTP_STATUS_MESSAGES = {
|
||||
100: 'Continue',
|
||||
101: 'Switching Protocol',
|
||||
102: 'Processing',
|
||||
103: 'Early Hints',
|
||||
200: 'OK',
|
||||
201: 'Created',
|
||||
202: 'Accepted',
|
||||
203: 'Non-Authoritative Information',
|
||||
204: 'No Content',
|
||||
205: 'Reset Content',
|
||||
206: 'Partial Content',
|
||||
207: 'Multi-Status',
|
||||
208: 'Already Reported',
|
||||
226: 'IM Used',
|
||||
300: 'Multiple Choice',
|
||||
301: 'Moved Permanently',
|
||||
302: 'Found',
|
||||
303: 'See Other',
|
||||
304: 'Not Modified',
|
||||
305: 'Use Proxy',
|
||||
306: 'unused',
|
||||
307: 'Temporary Redirect',
|
||||
308: 'Permanent Redirect',
|
||||
400: 'Bad Request',
|
||||
401: 'Unauthorized',
|
||||
402: 'Payment Required',
|
||||
403: 'Forbidden',
|
||||
404: 'Not Found',
|
||||
405: 'Method Not Allowed',
|
||||
406: 'Not Acceptable',
|
||||
407: 'Proxy Authentication Required',
|
||||
408: 'Request Timeout',
|
||||
409: 'Conflict',
|
||||
410: 'Gone',
|
||||
411: 'Length Required',
|
||||
412: 'Precondition Failed',
|
||||
413: 'Payload Too Large',
|
||||
414: 'URI Too Long',
|
||||
415: 'Unsupported Media Type',
|
||||
416: 'Range Not Satisfiable',
|
||||
417: 'Expectation Failed',
|
||||
418: 'I\'m a teapot',
|
||||
421: 'Misdirected Request',
|
||||
422: 'Unprocessable Entity',
|
||||
423: 'Locked',
|
||||
424: 'Failed Dependency',
|
||||
425: 'Too Early',
|
||||
426: 'Upgrade Required',
|
||||
428: 'Precondition Required',
|
||||
429: 'Too Many Requests',
|
||||
431: 'Request Header Fields Too Large',
|
||||
451: 'Unavailable For Legal Reasons',
|
||||
500: 'Internal Server Error',
|
||||
501: 'Not Implemented',
|
||||
502: 'Bad Gateway',
|
||||
503: 'Service Unavailable',
|
||||
504: 'Gateway Timeout',
|
||||
505: 'HTTP Version Not Supported',
|
||||
506: 'Variant Also Negotiates',
|
||||
507: 'Insufficient Storage',
|
||||
508: 'Loop Detected',
|
||||
510: 'Not Extended',
|
||||
511: 'Network Authentication Required',
|
||||
};
|
||||
|
||||
/**
|
||||
* Class that adapts a Cordova plugin http response to an Angular http response.
|
||||
*/
|
||||
export class CoreNativeToAngularHttpResponse<T> extends AngularHttpResponse<T> {
|
||||
|
||||
constructor(protected nativeResponse: NativeHttpResponse) {
|
||||
super({
|
||||
body: nativeResponse.data,
|
||||
headers: new HttpHeaders(nativeResponse.headers),
|
||||
status: nativeResponse.status,
|
||||
statusText: HTTP_STATUS_MESSAGES[nativeResponse.status] || '',
|
||||
url: nativeResponse.url || ''
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Injector, Type } from '@angular/core';
|
||||
|
||||
/**
|
||||
* Stub class used to type anonymous classes created in CoreSingletonsFactory#makeSingleton method.
|
||||
*/
|
||||
class CoreSingleton {}
|
||||
|
||||
/**
|
||||
* Token that can be used to resolve instances from the injector.
|
||||
*/
|
||||
export type CoreInjectionToken<Service> = Type<Service> | Type<any> | string;
|
||||
|
||||
/**
|
||||
* Singleton class created using the factory.
|
||||
*/
|
||||
export type CoreSingletonClass<Service> = typeof CoreSingleton & { instance: Service };
|
||||
|
||||
/**
|
||||
* Factory used to create CoreSingleton classes that get instances from an injector.
|
||||
*/
|
||||
export class CoreSingletonsFactory {
|
||||
|
||||
/**
|
||||
* Angular injector used to resolve singleton instances.
|
||||
*/
|
||||
private injector: Injector;
|
||||
|
||||
/**
|
||||
* Set the injector that will be used to resolve instances in the singletons created with this factory.
|
||||
*
|
||||
* @param injector Injector.
|
||||
*/
|
||||
setInjector(injector: Injector): void {
|
||||
this.injector = injector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a singleton that will hold an instance resolved from the factory injector.
|
||||
*
|
||||
* @param injectionToken Injection token used to resolve the singleton instance. This is usually the service class if the
|
||||
* provider was defined using a class or the string used in the `provide` key if it was defined using an object.
|
||||
*/
|
||||
makeSingleton<Service>(injectionToken: CoreInjectionToken<Service>): CoreSingletonClass<Service> {
|
||||
// tslint:disable: no-this-assignment
|
||||
const factory = this;
|
||||
|
||||
return class {
|
||||
|
||||
private static _instance: Service;
|
||||
|
||||
static get instance(): Service {
|
||||
// Initialize instances lazily.
|
||||
if (!this._instance) {
|
||||
this._instance = factory.injector.get(injectionToken);
|
||||
}
|
||||
|
||||
return this._instance;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
}
|
|
@ -14,7 +14,6 @@
|
|||
|
||||
import { Injector } from '@angular/core';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { SQLiteDB } from './sqlitedb';
|
||||
import { CoreAppProvider } from '@providers/app';
|
||||
import { CoreDbProvider } from '@providers/db';
|
||||
|
@ -190,7 +189,6 @@ export class CoreSite {
|
|||
protected domUtils: CoreDomUtilsProvider;
|
||||
protected eventsProvider: CoreEventsProvider;
|
||||
protected fileProvider: CoreFileProvider;
|
||||
protected http: HttpClient;
|
||||
protected textUtils: CoreTextUtilsProvider;
|
||||
protected timeUtils: CoreTimeUtilsProvider;
|
||||
protected translate: TranslateService;
|
||||
|
@ -256,7 +254,6 @@ export class CoreSite {
|
|||
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);
|
||||
|
@ -1357,55 +1354,62 @@ export class CoreSite {
|
|||
* @param retrying True if we're retrying the check.
|
||||
* @return Promise resolved when the check is done.
|
||||
*/
|
||||
checkLocalMobilePlugin(retrying?: boolean): Promise<LocalMobileResponse> {
|
||||
async checkLocalMobilePlugin(retrying?: boolean): Promise<LocalMobileResponse> {
|
||||
const checkUrl = this.siteUrl + '/local/mobile/check.php',
|
||||
service = CoreConfigConstants.wsextservice;
|
||||
|
||||
if (!service) {
|
||||
// External service not defined.
|
||||
return Promise.resolve({ code: 0 });
|
||||
return { code: 0 };
|
||||
}
|
||||
|
||||
const promise = this.http.post(checkUrl, { service: service }).timeout(this.wsProvider.getRequestTimeout()).toPromise();
|
||||
let data;
|
||||
|
||||
return promise.then((data: any) => {
|
||||
if (typeof data != 'undefined' && data.errorcode === 'requirecorrectaccess') {
|
||||
if (!retrying) {
|
||||
this.siteUrl = this.urlUtils.addOrRemoveWWW(this.siteUrl);
|
||||
try {
|
||||
const response = await this.wsProvider.sendHTTPRequest(checkUrl, {
|
||||
method: 'post',
|
||||
data: { service: service },
|
||||
});
|
||||
|
||||
return this.checkLocalMobilePlugin(true);
|
||||
} else {
|
||||
return Promise.reject(data.error);
|
||||
}
|
||||
} 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' };
|
||||
}
|
||||
|
||||
const code = parseInt(data.code, 10);
|
||||
if (data.error) {
|
||||
switch (code) {
|
||||
case 1:
|
||||
// Site in maintenance mode.
|
||||
return Promise.reject(this.translate.instant('core.login.siteinmaintenance'));
|
||||
case 2:
|
||||
// Web services not enabled.
|
||||
return Promise.reject(this.translate.instant('core.login.webservicesnotenabled'));
|
||||
case 3:
|
||||
// Extended service not enabled, but the official is enabled.
|
||||
return { code: 0 };
|
||||
case 4:
|
||||
// Neither extended or official services enabled.
|
||||
return Promise.reject(this.translate.instant('core.login.mobileservicesnotenabled'));
|
||||
default:
|
||||
return Promise.reject(this.translate.instant('core.unexpectederror'));
|
||||
}
|
||||
} else {
|
||||
return { code: code, service: service, coreSupported: !!data.coresupported };
|
||||
}
|
||||
}, () => {
|
||||
data = response.body;
|
||||
} catch (ex) {
|
||||
return { code: 0 };
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof data != 'undefined' && data.errorcode === 'requirecorrectaccess') {
|
||||
if (!retrying) {
|
||||
this.siteUrl = this.urlUtils.addOrRemoveWWW(this.siteUrl);
|
||||
|
||||
return this.checkLocalMobilePlugin(true);
|
||||
} else {
|
||||
throw data.error;
|
||||
}
|
||||
} 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' };
|
||||
}
|
||||
|
||||
const code = parseInt(data.code, 10);
|
||||
if (data.error) {
|
||||
switch (code) {
|
||||
case 1:
|
||||
// Site in maintenance mode.
|
||||
throw this.translate.instant('core.login.siteinmaintenance');
|
||||
case 2:
|
||||
// Web services not enabled.
|
||||
throw this.translate.instant('core.login.webservicesnotenabled');
|
||||
case 3:
|
||||
// Extended service not enabled, but the official is enabled.
|
||||
return { code: 0 };
|
||||
case 4:
|
||||
// Neither extended or official services enabled.
|
||||
throw this.translate.instant('core.login.mobileservicesnotenabled');
|
||||
default:
|
||||
throw this.translate.instant('core.unexpectederror');
|
||||
}
|
||||
} else {
|
||||
return { code: code, service: service, coreSupported: !!data.coresupported };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1970,7 +1974,7 @@ export class CoreSite {
|
|||
url = this.fixPluginfileURL(url);
|
||||
|
||||
this.tokenPluginFileWorksPromise = this.wsProvider.performHead(url).then((result) => {
|
||||
return result.ok;
|
||||
return result.status >= 200 && result.status < 300;
|
||||
}).catch((error) => {
|
||||
// Error performing head request.
|
||||
return false;
|
||||
|
|
|
@ -16,13 +16,16 @@ import {
|
|||
Component, Input, Output, OnInit, ViewChild, ElementRef, EventEmitter, OnChanges, SimpleChange, Optional
|
||||
} from '@angular/core';
|
||||
import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
|
||||
import { NavController } from 'ionic-angular';
|
||||
import { NavController, Platform } from 'ionic-angular';
|
||||
import { CoreFile } from '@providers/file';
|
||||
import { CoreLoggerProvider } from '@providers/logger';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
import { CoreUrlUtilsProvider } from '@providers/utils/url';
|
||||
import { CoreIframeUtilsProvider } from '@providers/utils/iframe';
|
||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
||||
import { CoreUrl } from '@singletons/url';
|
||||
import { WKWebViewCookiesWindow } from 'cordova-plugin-wkwebview-cookies';
|
||||
|
||||
@Component({
|
||||
selector: 'core-iframe',
|
||||
|
@ -49,7 +52,8 @@ export class CoreIframeComponent implements OnInit, OnChanges {
|
|||
protected navCtrl: NavController,
|
||||
protected urlUtils: CoreUrlUtilsProvider,
|
||||
protected utils: CoreUtilsProvider,
|
||||
@Optional() protected svComponent: CoreSplitViewComponent) {
|
||||
@Optional() protected svComponent: CoreSplitViewComponent,
|
||||
protected platform: Platform) {
|
||||
|
||||
this.logger = logger.getInstance('CoreIframe');
|
||||
this.loaded = new EventEmitter<HTMLIFrameElement>();
|
||||
|
@ -91,10 +95,28 @@ export class CoreIframeComponent implements OnInit, OnChanges {
|
|||
/**
|
||||
* Detect changes on input properties.
|
||||
*/
|
||||
ngOnChanges(changes: {[name: string]: SimpleChange }): void {
|
||||
async ngOnChanges(changes: {[name: string]: SimpleChange }): Promise<void> {
|
||||
if (changes.src) {
|
||||
const youtubeUrl = this.urlUtils.getYoutubeEmbedUrl(changes.src.currentValue);
|
||||
this.safeUrl = this.sanitizer.bypassSecurityTrustResourceUrl(youtubeUrl || changes.src.currentValue);
|
||||
const url = this.urlUtils.getYoutubeEmbedUrl(changes.src.currentValue) || changes.src.currentValue;
|
||||
|
||||
if (this.platform.is('ios')) {
|
||||
// Save a "fake" cookie for the iframe's domain to fix a bug in WKWebView.
|
||||
try {
|
||||
const win = <WKWebViewCookiesWindow> window;
|
||||
const urlParts = CoreUrl.parse(url);
|
||||
|
||||
await win.WKWebViewCookies.setCookie({
|
||||
name: 'MoodleAppCookieForWKWebView',
|
||||
value: '1',
|
||||
domain: urlParts.domain,
|
||||
});
|
||||
} catch (err) {
|
||||
// Ignore errors.
|
||||
this.logger.error('Error setting cookie', err);
|
||||
}
|
||||
}
|
||||
|
||||
this.safeUrl = this.sanitizer.bypassSecurityTrustResourceUrl(CoreFile.instance.convertFileSrc(url));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -93,5 +93,6 @@
|
|||
"statusbarbgremotetheme": "#000000",
|
||||
"statusbarlighttextremotetheme": true,
|
||||
"enableanalytics": false,
|
||||
"forceColorScheme": ""
|
||||
"forceColorScheme": "",
|
||||
"webviewscheme": "moodleappfs"
|
||||
}
|
|
@ -56,7 +56,7 @@ import { Md5 } from 'ts-md5/dist/md5';
|
|||
|
||||
// Import core classes that can be useful for site plugins.
|
||||
import { CoreSyncBaseProvider } from '@classes/base-sync';
|
||||
import { CoreUrl } from '@classes/utils/url';
|
||||
import { CoreUrl } from '@singletons/url';
|
||||
import { CoreCache } from '@classes/cache';
|
||||
import { CoreDelegate } from '@classes/delegate';
|
||||
import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler';
|
||||
|
|
|
@ -27,6 +27,7 @@ import { CoreH5PUtilsProvider } from './utils';
|
|||
import { CoreH5PContentValidator } from '../classes/content-validator';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { FileEntry } from '@ionic-native/file';
|
||||
import { makeSingleton } from '@singletons/core.singletons';
|
||||
|
||||
/**
|
||||
* Service to provide H5P functionalities.
|
||||
|
@ -410,7 +411,7 @@ export class CoreH5PProvider {
|
|||
* @return Promise resolved with all of the files content in one string.
|
||||
*/
|
||||
protected concatenateFiles(assets: CoreH5PDependencyAsset[], type: string): Promise<string> {
|
||||
const basePath = this.fileProvider.getBasePathInstant();
|
||||
const basePath = this.fileProvider.convertFileSrc(this.fileProvider.getBasePathInstant());
|
||||
let content = '',
|
||||
promise = Promise.resolve(); // Use a chain of promises so the order is kept.
|
||||
|
||||
|
@ -495,7 +496,8 @@ export class CoreH5PProvider {
|
|||
|
||||
const contentId = this.getContentId(id),
|
||||
basePath = this.fileProvider.getBasePathInstant(),
|
||||
contentUrl = this.textUtils.concatenatePaths(basePath, this.getContentFolderPath(content.folderName, site.getId()));
|
||||
contentUrl = this.fileProvider.convertFileSrc(this.textUtils.concatenatePaths(
|
||||
basePath, this.getContentFolderPath(content.folderName, site.getId())));
|
||||
|
||||
// Create the settings needed for the content.
|
||||
const contentSettings = {
|
||||
|
@ -560,6 +562,40 @@ export class CoreH5PProvider {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all content indexes of all sites from filesystem.
|
||||
*
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
async deleteAllContentIndexes(): Promise<void> {
|
||||
const siteIds = await this.sitesProvider.getSitesIds();
|
||||
|
||||
await Promise.all(siteIds.map((siteId) => this.deleteAllContentIndexesForSite(siteId)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all content indexes for a certain site from filesystem.
|
||||
*
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
async deleteAllContentIndexesForSite(siteId?: string): Promise<void> {
|
||||
|
||||
const site = await this.sitesProvider.getSite(siteId);
|
||||
|
||||
const records = await site.getDb().getAllRecords(this.CONTENT_TABLE);
|
||||
|
||||
const promises = records.map(async (record) => {
|
||||
try {
|
||||
await this.fileProvider.removeFile(this.getContentIndexPath(record.foldername, site.getId()));
|
||||
} catch (err) {
|
||||
// Ignore errors, maybe the file doesn't exist.
|
||||
}
|
||||
});
|
||||
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete cached assets from DB and filesystem.
|
||||
*
|
||||
|
@ -1033,12 +1069,11 @@ export class CoreH5PProvider {
|
|||
settings.loadedJs = [];
|
||||
settings.loadedCss = [];
|
||||
|
||||
const libUrl = this.getCoreH5PPath(),
|
||||
relPath = this.urlUtils.removeProtocolAndWWW(libUrl);
|
||||
const libUrl = this.getCoreH5PPath();
|
||||
|
||||
// Add core stylesheets.
|
||||
CoreH5PProvider.STYLES.forEach((style) => {
|
||||
settings.core.styles.push(relPath + style);
|
||||
settings.core.styles.push(libUrl + style);
|
||||
cssRequires.push(libUrl + style);
|
||||
});
|
||||
|
||||
|
@ -1279,8 +1314,10 @@ export class CoreH5PProvider {
|
|||
|
||||
return {
|
||||
baseUrl: this.fileProvider.getWWWPath(),
|
||||
url: this.textUtils.concatenatePaths(basePath, this.getExternalH5PFolderPath(site.getId())),
|
||||
urlLibraries: this.textUtils.concatenatePaths(basePath, this.getLibrariesFolderPath(site.getId())),
|
||||
url: this.fileProvider.convertFileSrc(this.textUtils.concatenatePaths(
|
||||
basePath, this.getExternalH5PFolderPath(site.getId()))),
|
||||
urlLibraries: this.fileProvider.convertFileSrc(this.textUtils.concatenatePaths(
|
||||
basePath, this.getLibrariesFolderPath(site.getId()))),
|
||||
postUserStatistics: false,
|
||||
ajax: ajaxPaths,
|
||||
saveFreq: false,
|
||||
|
@ -2623,6 +2660,8 @@ export class CoreH5PProvider {
|
|||
}
|
||||
}
|
||||
|
||||
export class CoreH5P extends makeSingleton(CoreH5PProvider) {}
|
||||
|
||||
/**
|
||||
* Display options behaviour constants.
|
||||
*/
|
||||
|
|
|
@ -22,7 +22,7 @@ import { CoreUrlUtilsProvider } from '@providers/utils/url';
|
|||
import { CoreConfigConstants } from '../../../../configconstants';
|
||||
import { CoreLoginHelperProvider } from '../../providers/helper';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { CoreUrl } from '@classes/utils/url';
|
||||
import { CoreUrl } from '@singletons/url';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
/**
|
||||
|
|
|
@ -16,6 +16,7 @@ import { Directive, Input, AfterViewInit, ElementRef, OnChanges, SimpleChange, O
|
|||
import { Platform } from 'ionic-angular';
|
||||
import { CoreAppProvider } from '@providers/app';
|
||||
import { CoreLoggerProvider } from '@providers/logger';
|
||||
import { CoreFile } from '@providers/file';
|
||||
import { CoreFilepoolProvider } from '@providers/filepool';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
|
@ -52,9 +53,15 @@ export class CoreExternalContentDirective implements AfterViewInit, OnChanges {
|
|||
|
||||
invalid = false;
|
||||
|
||||
constructor(element: ElementRef, logger: CoreLoggerProvider, private filepoolProvider: CoreFilepoolProvider,
|
||||
private platform: Platform, private sitesProvider: CoreSitesProvider, private domUtils: CoreDomUtilsProvider,
|
||||
private urlUtils: CoreUrlUtilsProvider, private appProvider: CoreAppProvider, private utils: CoreUtilsProvider) {
|
||||
constructor(element: ElementRef,
|
||||
logger: CoreLoggerProvider,
|
||||
protected filepoolProvider: CoreFilepoolProvider,
|
||||
protected platform: Platform,
|
||||
protected sitesProvider: CoreSitesProvider,
|
||||
protected domUtils: CoreDomUtilsProvider,
|
||||
protected urlUtils: CoreUrlUtilsProvider,
|
||||
protected appProvider: CoreAppProvider,
|
||||
protected utils: CoreUtilsProvider) {
|
||||
// This directive can be added dynamically. In that case, the first param is the HTMLElement.
|
||||
this.element = element.nativeElement || element;
|
||||
this.logger = logger.getInstance('CoreExternalContentDirective');
|
||||
|
@ -179,7 +186,7 @@ export class CoreExternalContentDirective implements AfterViewInit, OnChanges {
|
|||
* @param siteId Site ID.
|
||||
* @return Promise resolved if the element is successfully treated.
|
||||
*/
|
||||
protected handleExternalContent(targetAttr: string, url: string, siteId?: string): Promise<any> {
|
||||
protected async handleExternalContent(targetAttr: string, url: string, siteId?: string): Promise<any> {
|
||||
|
||||
const tagName = this.element.tagName;
|
||||
|
||||
|
@ -214,72 +221,70 @@ export class CoreExternalContentDirective implements AfterViewInit, OnChanges {
|
|||
this.addSource(url);
|
||||
}
|
||||
|
||||
return Promise.reject(null);
|
||||
throw 'Non-downloadable URL';
|
||||
}
|
||||
|
||||
// Get the webservice pluginfile URL, we ignore failures here.
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
if (!site.canDownloadFiles() && this.urlUtils.isPluginFileUrl(url)) {
|
||||
this.element.parentElement.removeChild(this.element); // Remove element since it'll be broken.
|
||||
const site = await this.sitesProvider.getSite(siteId);
|
||||
|
||||
return Promise.reject(null);
|
||||
if (!site.canDownloadFiles() && this.urlUtils.isPluginFileUrl(url)) {
|
||||
this.element.parentElement.removeChild(this.element); // Remove element since it'll be broken.
|
||||
|
||||
throw 'Site doesn\'t allow downloading files.';
|
||||
}
|
||||
|
||||
// Download images, tracks and posters if size is unknown.
|
||||
const dwnUnknown = tagName == 'IMG' || tagName == 'TRACK' || targetAttr == 'poster';
|
||||
let finalUrl: string;
|
||||
|
||||
if (targetAttr === 'src' && tagName !== 'SOURCE' && tagName !== 'TRACK' && tagName !== 'VIDEO' && tagName !== 'AUDIO') {
|
||||
finalUrl = await this.filepoolProvider.getSrcByUrl(siteId, url, this.component, this.componentId, 0, true, dwnUnknown);
|
||||
} else {
|
||||
finalUrl = await this.filepoolProvider.getUrlByUrl(siteId, url, this.component, this.componentId, 0, true, dwnUnknown);
|
||||
|
||||
finalUrl = CoreFile.instance.convertFileSrc(finalUrl);
|
||||
}
|
||||
|
||||
if (finalUrl.match(/^https?:\/\//i)) {
|
||||
/* In iOS, if we use the same URL in embedded file and background download then the download only
|
||||
downloads a few bytes (cached ones). Add a hash to the URL so both URLs are different. */
|
||||
finalUrl = finalUrl + '#moodlemobile-embedded';
|
||||
}
|
||||
|
||||
this.logger.debug('Using URL ' + finalUrl + ' for ' + url);
|
||||
if (tagName === 'SOURCE') {
|
||||
// The browser does not catch changes in SRC, we need to add a new source.
|
||||
this.addSource(finalUrl);
|
||||
} else {
|
||||
if (tagName === 'IMG') {
|
||||
this.loaded = false;
|
||||
this.waitForLoad();
|
||||
}
|
||||
this.element.setAttribute(targetAttr, finalUrl);
|
||||
this.element.setAttribute('data-original-' + targetAttr, url);
|
||||
}
|
||||
|
||||
// Set events to download big files (not downloaded automatically).
|
||||
if (finalUrl.indexOf('http') === 0 && targetAttr != 'poster' &&
|
||||
(tagName == 'VIDEO' || tagName == 'AUDIO' || tagName == 'A' || tagName == 'SOURCE')) {
|
||||
const eventName = tagName == 'A' ? 'click' : 'play';
|
||||
let clickableEl = this.element;
|
||||
|
||||
if (tagName == 'SOURCE') {
|
||||
clickableEl = <HTMLElement> this.domUtils.closest(this.element, 'video,audio');
|
||||
if (!clickableEl) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Download images, tracks and posters if size is unknown.
|
||||
const dwnUnknown = tagName == 'IMG' || tagName == 'TRACK' || targetAttr == 'poster';
|
||||
let promise;
|
||||
|
||||
if (targetAttr === 'src' && tagName !== 'SOURCE' && tagName !== 'TRACK' && tagName !== 'VIDEO' &&
|
||||
tagName !== 'AUDIO') {
|
||||
promise = this.filepoolProvider.getSrcByUrl(siteId, url, this.component, this.componentId, 0, true, dwnUnknown);
|
||||
} else {
|
||||
promise = this.filepoolProvider.getUrlByUrl(siteId, url, this.component, this.componentId, 0, true, dwnUnknown);
|
||||
}
|
||||
|
||||
return promise.then((finalUrl) => {
|
||||
if (finalUrl.match(/^https?:\/\//i)) {
|
||||
/* In iOS, if we use the same URL in embedded file and background download then the download only
|
||||
downloads a few bytes (cached ones). Add a hash to the URL so both URLs are different. */
|
||||
finalUrl = finalUrl + '#moodlemobile-embedded';
|
||||
}
|
||||
|
||||
this.logger.debug('Using URL ' + finalUrl + ' for ' + url);
|
||||
if (tagName === 'SOURCE') {
|
||||
// The browser does not catch changes in SRC, we need to add a new source.
|
||||
this.addSource(finalUrl);
|
||||
} else {
|
||||
if (tagName === 'IMG') {
|
||||
this.loaded = false;
|
||||
this.waitForLoad();
|
||||
}
|
||||
this.element.setAttribute(targetAttr, finalUrl);
|
||||
this.element.setAttribute('data-original-' + targetAttr, url);
|
||||
}
|
||||
|
||||
// Set events to download big files (not downloaded automatically).
|
||||
if (finalUrl.indexOf('http') === 0 && targetAttr != 'poster' &&
|
||||
(tagName == 'VIDEO' || tagName == 'AUDIO' || tagName == 'A' || tagName == 'SOURCE')) {
|
||||
const eventName = tagName == 'A' ? 'click' : 'play';
|
||||
let clickableEl = this.element;
|
||||
|
||||
if (tagName == 'SOURCE') {
|
||||
clickableEl = <HTMLElement> this.domUtils.closest(this.element, 'video,audio');
|
||||
if (!clickableEl) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
clickableEl.addEventListener(eventName, () => {
|
||||
// User played media or opened a downloadable link.
|
||||
// Download the file if in wifi and it hasn't been downloaded already (for big files).
|
||||
if (this.appProvider.isWifi()) {
|
||||
// We aren't using the result, so it doesn't matter which of the 2 functions we call.
|
||||
this.filepoolProvider.getUrlByUrl(siteId, url, this.component, this.componentId, 0, false);
|
||||
}
|
||||
});
|
||||
clickableEl.addEventListener(eventName, () => {
|
||||
// User played media or opened a downloadable link.
|
||||
// Download the file if in wifi and it hasn't been downloaded already (for big files).
|
||||
if (this.appProvider.isWifi()) {
|
||||
// We aren't using the result, so it doesn't matter which of the 2 functions we call.
|
||||
this.filepoolProvider.getUrlByUrl(siteId, url, this.component, this.componentId, 0, false);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -74,26 +74,27 @@ export class CoreFormatTextDirective implements OnChanges {
|
|||
protected loadingChangedListener;
|
||||
|
||||
constructor(element: ElementRef,
|
||||
private sitesProvider: CoreSitesProvider,
|
||||
private domUtils: CoreDomUtilsProvider,
|
||||
private textUtils: CoreTextUtilsProvider,
|
||||
private translate: TranslateService,
|
||||
private platform: Platform,
|
||||
private utils: CoreUtilsProvider,
|
||||
private urlUtils: CoreUrlUtilsProvider,
|
||||
private loggerProvider: CoreLoggerProvider,
|
||||
private filepoolProvider: CoreFilepoolProvider,
|
||||
private appProvider: CoreAppProvider,
|
||||
private contentLinksHelper: CoreContentLinksHelperProvider,
|
||||
@Optional() private navCtrl: NavController,
|
||||
@Optional() private content: Content, @Optional()
|
||||
private svComponent: CoreSplitViewComponent,
|
||||
private iframeUtils: CoreIframeUtilsProvider,
|
||||
private eventsProvider: CoreEventsProvider,
|
||||
private filterProvider: CoreFilterProvider,
|
||||
private filterHelper: CoreFilterHelperProvider,
|
||||
private filterDelegate: CoreFilterDelegate,
|
||||
private viewContainerRef: ViewContainerRef) {
|
||||
protected sitesProvider: CoreSitesProvider,
|
||||
protected domUtils: CoreDomUtilsProvider,
|
||||
protected textUtils: CoreTextUtilsProvider,
|
||||
protected translate: TranslateService,
|
||||
protected platform: Platform,
|
||||
protected utils: CoreUtilsProvider,
|
||||
protected urlUtils: CoreUrlUtilsProvider,
|
||||
protected loggerProvider: CoreLoggerProvider,
|
||||
protected filepoolProvider: CoreFilepoolProvider,
|
||||
protected appProvider: CoreAppProvider,
|
||||
protected contentLinksHelper: CoreContentLinksHelperProvider,
|
||||
@Optional() protected navCtrl: NavController,
|
||||
@Optional() protected content: Content, @Optional()
|
||||
protected svComponent: CoreSplitViewComponent,
|
||||
protected iframeUtils: CoreIframeUtilsProvider,
|
||||
protected eventsProvider: CoreEventsProvider,
|
||||
protected filterProvider: CoreFilterProvider,
|
||||
protected filterHelper: CoreFilterHelperProvider,
|
||||
protected filterDelegate: CoreFilterDelegate,
|
||||
protected viewContainerRef: ViewContainerRef,
|
||||
) {
|
||||
|
||||
this.element = element.nativeElement;
|
||||
this.element.classList.add('opacity-hide'); // Hide contents until they're treated.
|
||||
|
|
|
@ -92,7 +92,7 @@ export class CoreLinkDirective implements OnInit {
|
|||
protected navigate(href: string): void {
|
||||
const contentLinksScheme = CoreConfigConstants.customurlscheme + '://link=';
|
||||
|
||||
if (href.indexOf('cdvfile://') === 0 || href.indexOf('file://') === 0 || href.indexOf('filesystem:') === 0) {
|
||||
if (this.urlUtils.isLocalFileUrl(href)) {
|
||||
// We have a local file.
|
||||
this.utils.openFile(href).catch((error) => {
|
||||
this.domUtils.showErrorModal(error);
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<meta charset="UTF-8">
|
||||
<title>Moodle Desktop</title> <!-- Title is used in desktop, but should be ignored in mobile apps. -->
|
||||
<meta name="viewport" content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src * filesystem: cdvfile: file: data: gap: https://ssl.gstatic.com; img-src * filesystem: gap: data: cdvfile: file: https://ssl.gstatic.com android-webview-video-poster: blob:; style-src 'self' 'unsafe-inline' filesystem: cdvfile: file:; script-src 'self' 'unsafe-inline' 'unsafe-eval' http://localhost:* filesystem: cdvfile: file:; media-src * filesystem: cdvfile: file: gap: blob:">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src * filesystem: cdvfile: file: data: gap: https://ssl.gstatic.com moodleappfs:; img-src * filesystem: gap: data: cdvfile: file: https://ssl.gstatic.com android-webview-video-poster: blob: moodleappfs:; style-src 'self' 'unsafe-inline' filesystem: cdvfile: file: moodleappfs:; script-src 'self' 'unsafe-inline' 'unsafe-eval' http://localhost:* filesystem: cdvfile: file: moodleappfs:; media-src * filesystem: cdvfile: file: gap: blob: moodleappfs:">
|
||||
<meta name="format-detection" content="telephone=no">
|
||||
<meta name="msapplication-tap-highlight" content="no">
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ import { CoreLoggerProvider } from './logger';
|
|||
import { CoreEventsProvider } from './events';
|
||||
import { SQLiteDB, SQLiteDBTableSchema } from '@classes/sqlitedb';
|
||||
import { CoreConfigConstants } from '../configconstants';
|
||||
import { makeSingleton } from '@singletons/core.singletons';
|
||||
|
||||
/**
|
||||
* Data stored for a redirect to another page/site.
|
||||
|
@ -703,3 +704,5 @@ export class CoreAppProvider {
|
|||
this.forceOffline = !!value;
|
||||
}
|
||||
}
|
||||
|
||||
export class CoreApp extends makeSingleton(CoreAppProvider) {}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { CoreAppProvider, CoreAppSchema } from './app';
|
||||
import { SQLiteDB } from '@classes/sqlitedb';
|
||||
import { makeSingleton } from '@singletons/core.singletons';
|
||||
|
||||
/**
|
||||
* Factory to provide access to dynamic and permanent config and settings.
|
||||
|
@ -102,3 +103,5 @@ export class CoreConfigProvider {
|
|||
return this.appDB.insertRecord(this.TABLE_NAME, { name: name, value: value });
|
||||
}
|
||||
}
|
||||
|
||||
export class CoreConfig extends makeSingleton(CoreConfigProvider) {}
|
||||
|
|
|
@ -20,6 +20,7 @@ import { CoreLoggerProvider } from './logger';
|
|||
import { CoreUtilsProvider } from './utils/utils';
|
||||
import { CoreConstants } from '@core/constants';
|
||||
import { SQLiteDB } from '@classes/sqlitedb';
|
||||
import { makeSingleton } from '@singletons/core.singletons';
|
||||
|
||||
/**
|
||||
* Interface that all cron handlers must implement.
|
||||
|
@ -554,3 +555,5 @@ export class CoreCronDelegate {
|
|||
delete this.handlers[name].timeout;
|
||||
}
|
||||
}
|
||||
|
||||
export class CoreCron extends makeSingleton(CoreCronDelegate) {}
|
||||
|
|
|
@ -17,6 +17,7 @@ import { SQLite } from '@ionic-native/sqlite';
|
|||
import { Platform } from 'ionic-angular';
|
||||
import { SQLiteDB } from '@classes/sqlitedb';
|
||||
import { SQLiteDBMock } from '@core/emulator/classes/sqlitedb';
|
||||
import { makeSingleton } from '@singletons/core.singletons';
|
||||
|
||||
/**
|
||||
* This service allows interacting with the local database to store and retrieve data.
|
||||
|
@ -81,3 +82,5 @@ export class CoreDbProvider {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class CoreDB extends makeSingleton(CoreDbProvider) {}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { Subject } from 'rxjs';
|
||||
import { CoreLoggerProvider } from '@providers/logger';
|
||||
import { makeSingleton } from '@singletons/core.singletons';
|
||||
|
||||
/**
|
||||
* Observer instance to stop listening to an event.
|
||||
|
@ -174,3 +175,5 @@ export class CoreEventsProvider {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class CoreEvents extends makeSingleton(CoreEventsProvider) {}
|
||||
|
|
|
@ -21,6 +21,7 @@ import { CoreSitesProvider } from './sites';
|
|||
import { CoreWSProvider } from './ws';
|
||||
import { CoreUtilsProvider } from './utils/utils';
|
||||
import { CoreConstants } from '@core/constants';
|
||||
import { makeSingleton } from '@singletons/core.singletons';
|
||||
|
||||
/**
|
||||
* Provider to provide some helper functions regarding files and packages.
|
||||
|
@ -333,5 +334,6 @@ export class CoreFileHelperProvider {
|
|||
|
||||
throw new Error('Couldn\'t determine file size: ' + file.fileurl);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class CoreFileHelper extends makeSingleton(CoreFileHelperProvider) {}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { CoreSitesProvider } from './sites';
|
||||
import { makeSingleton } from '@singletons/core.singletons';
|
||||
|
||||
/**
|
||||
* Helper to store some temporary data for file submission.
|
||||
|
@ -146,3 +147,5 @@ export class CoreFileSessionProvider {
|
|||
this.files[siteId][component][id] = newFiles;
|
||||
}
|
||||
}
|
||||
|
||||
export class CoreFileSession extends makeSingleton(CoreFileSessionProvider) {}
|
||||
|
|
|
@ -20,7 +20,9 @@ import { CoreAppProvider } from './app';
|
|||
import { CoreLoggerProvider } from './logger';
|
||||
import { CoreMimetypeUtilsProvider } from './utils/mimetype';
|
||||
import { CoreTextUtilsProvider } from './utils/text';
|
||||
import { CoreConfigConstants } from '../configconstants';
|
||||
import { Zip } from '@ionic-native/zip';
|
||||
import { makeSingleton } from '@singletons/core.singletons';
|
||||
|
||||
/**
|
||||
* Progress event used when writing a file data into a file.
|
||||
|
@ -945,6 +947,7 @@ export class CoreFileProvider {
|
|||
|
||||
/**
|
||||
* Get the internal URL of a file.
|
||||
* Please notice that with WKWebView these URLs no longer work in mobile. Use fileEntry.toURL() along with convertFileSrc.
|
||||
*
|
||||
* @param fileEntry File Entry.
|
||||
* @return Internal URL.
|
||||
|
@ -1269,4 +1272,31 @@ export class CoreFileProvider {
|
|||
|
||||
return window.location.href;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to call Ionic WebView convertFileSrc only in the needed platforms.
|
||||
* This is needed to make files work with the Ionic WebView plugin.
|
||||
*
|
||||
* @param src Source to convert.
|
||||
* @return Converted src.
|
||||
*/
|
||||
convertFileSrc(src: string): string {
|
||||
return this.appProvider.isMobile() ? (<any> window).Ionic.WebView.convertFileSrc(src) : src;
|
||||
}
|
||||
|
||||
/**
|
||||
* Undo the conversion of convertFileSrc.
|
||||
*
|
||||
* @param src Source to unconvert.
|
||||
* @return Unconverted src.
|
||||
*/
|
||||
unconvertFileSrc(src: string): string {
|
||||
if (!this.appProvider.isMobile()) {
|
||||
return src;
|
||||
}
|
||||
|
||||
return src.replace(CoreConfigConstants.webviewscheme + '://localhost/_app_file_', 'file://');
|
||||
}
|
||||
}
|
||||
|
||||
export class CoreFile extends makeSingleton(CoreFileProvider) {}
|
||||
|
|
|
@ -31,6 +31,7 @@ import { CoreUtilsProvider } from './utils/utils';
|
|||
import { SQLiteDB } from '@classes/sqlitedb';
|
||||
import { CoreConstants } from '@core/constants';
|
||||
import { Md5 } from 'ts-md5/dist/md5';
|
||||
import { makeSingleton } from '@singletons/core.singletons';
|
||||
|
||||
/**
|
||||
* Entry from filepool.
|
||||
|
@ -471,11 +472,21 @@ export class CoreFilepoolProvider {
|
|||
protected packagesPromises = {};
|
||||
protected filePromises: { [s: string]: { [s: string]: Promise<any> } } = {};
|
||||
|
||||
constructor(logger: CoreLoggerProvider, private appProvider: CoreAppProvider, private fileProvider: CoreFileProvider,
|
||||
private sitesProvider: CoreSitesProvider, private wsProvider: CoreWSProvider, private textUtils: CoreTextUtilsProvider,
|
||||
private utils: CoreUtilsProvider, private mimeUtils: CoreMimetypeUtilsProvider, private urlUtils: CoreUrlUtilsProvider,
|
||||
private timeUtils: CoreTimeUtilsProvider, private eventsProvider: CoreEventsProvider, initDelegate: CoreInitDelegate,
|
||||
network: Network, private pluginFileDelegate: CorePluginFileDelegate, private domUtils: CoreDomUtilsProvider,
|
||||
constructor(logger: CoreLoggerProvider,
|
||||
protected appProvider: CoreAppProvider,
|
||||
protected fileProvider: CoreFileProvider,
|
||||
protected sitesProvider: CoreSitesProvider,
|
||||
protected wsProvider: CoreWSProvider,
|
||||
protected textUtils: CoreTextUtilsProvider,
|
||||
protected utils: CoreUtilsProvider,
|
||||
protected mimeUtils: CoreMimetypeUtilsProvider,
|
||||
protected urlUtils: CoreUrlUtilsProvider,
|
||||
protected timeUtils: CoreTimeUtilsProvider,
|
||||
protected eventsProvider: CoreEventsProvider,
|
||||
initDelegate: CoreInitDelegate,
|
||||
network: Network,
|
||||
protected pluginFileDelegate: CorePluginFileDelegate,
|
||||
protected domUtils: CoreDomUtilsProvider,
|
||||
zone: NgZone) {
|
||||
this.logger = logger.getInstance('CoreFilepoolProvider');
|
||||
|
||||
|
@ -1901,8 +1912,7 @@ export class CoreFilepoolProvider {
|
|||
if (this.fileProvider.isAvailable()) {
|
||||
return Promise.resolve(this.getFilePath(siteId, fileId)).then((path) => {
|
||||
return this.fileProvider.getFile(path).then((fileEntry) => {
|
||||
// We use toInternalURL so images are loaded in iOS8 using img HTML tags.
|
||||
return this.fileProvider.getInternalURL(fileEntry);
|
||||
return this.fileProvider.convertFileSrc(fileEntry.toURL());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -3250,3 +3260,5 @@ export class CoreFilepoolProvider {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class CoreFilepool extends makeSingleton(CoreFilepoolProvider) {}
|
||||
|
|
|
@ -17,6 +17,7 @@ import { TranslateService } from '@ngx-translate/core';
|
|||
import { CoreSitesProvider } from './sites';
|
||||
import { CoreCoursesProvider } from '@core/courses/providers/courses';
|
||||
import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
|
||||
import { makeSingleton } from '@singletons/core.singletons';
|
||||
|
||||
/**
|
||||
* Group info for an activity.
|
||||
|
@ -449,3 +450,5 @@ export class CoreGroupsProvider {
|
|||
return groupInfo.defaultGroupId;
|
||||
}
|
||||
}
|
||||
|
||||
export class CoreGroups extends makeSingleton(CoreGroupsProvider) {}
|
||||
|
|
|
@ -16,6 +16,7 @@ import { Injectable } from '@angular/core';
|
|||
import { Platform } from 'ionic-angular';
|
||||
import { CoreLoggerProvider } from './logger';
|
||||
import { CoreUtilsProvider } from './utils/utils';
|
||||
import { makeSingleton } from '@singletons/core.singletons';
|
||||
|
||||
/**
|
||||
* Interface that all init handlers must implement.
|
||||
|
@ -175,3 +176,5 @@ export class CoreInitDelegate {
|
|||
this.initProcesses[handler.name] = handler;
|
||||
}
|
||||
}
|
||||
|
||||
export class CoreInit extends makeSingleton(CoreInitDelegate) {}
|
||||
|
|
|
@ -20,6 +20,7 @@ import { Platform, Config } from 'ionic-angular';
|
|||
import { CoreAppProvider } from '@providers/app';
|
||||
import { CoreConfigProvider } from './config';
|
||||
import { CoreConfigConstants } from '../configconstants';
|
||||
import { makeSingleton } from '@singletons/core.singletons';
|
||||
|
||||
/*
|
||||
* Service to handle language features, like changing the current language.
|
||||
|
@ -453,3 +454,5 @@ export class CoreLangProvider {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class CoreLang extends makeSingleton(CoreLangProvider) {}
|
||||
|
|
|
@ -27,6 +27,7 @@ import { SQLiteDB } from '@classes/sqlitedb';
|
|||
import { CoreConstants } from '@core/constants';
|
||||
import { CoreConfigConstants } from '../configconstants';
|
||||
import { Subject, Subscription } from 'rxjs';
|
||||
import { makeSingleton } from '@singletons/core.singletons';
|
||||
|
||||
/*
|
||||
* Generated class for the LocalNotificationsProvider provider.
|
||||
|
@ -754,3 +755,5 @@ export class CoreLocalNotificationsProvider {
|
|||
return this.appDB.updateRecords(this.COMPONENTS_TABLE, {id: newId}, {id: oldId});
|
||||
}
|
||||
}
|
||||
|
||||
export class CoreLocalNotifications extends makeSingleton(CoreLocalNotificationsProvider) {}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
import { Injectable } from '@angular/core';
|
||||
import * as moment from 'moment';
|
||||
import { makeSingleton } from '@singletons/core.singletons';
|
||||
|
||||
/**
|
||||
* Helper service to display messages in the console.
|
||||
|
@ -72,3 +73,5 @@ export class CoreLoggerProvider {
|
|||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class CoreLogger extends makeSingleton(CoreLoggerProvider) {}
|
||||
|
|
|
@ -19,6 +19,7 @@ import { CoreSitesProvider } from './sites';
|
|||
import { CoreWSExternalFile } from '@providers/ws';
|
||||
import { FileEntry } from '@ionic-native/file';
|
||||
import { CoreDelegate, CoreDelegateHandler } from '@classes/delegate';
|
||||
import { makeSingleton } from '@singletons/core.singletons';
|
||||
|
||||
/**
|
||||
* Interface that all plugin file handlers must implement.
|
||||
|
@ -371,3 +372,5 @@ export class CorePluginFileDelegate extends CoreDelegate {
|
|||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
export class CorePluginFile extends makeSingleton(CorePluginFileDelegate) {}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
import { Injectable, Injector } from '@angular/core';
|
||||
import { CoreSite } from '@classes/site';
|
||||
import { makeSingleton } from '@singletons/core.singletons';
|
||||
|
||||
/*
|
||||
* Provider to create sites instances.
|
||||
|
@ -56,3 +57,5 @@ export class CoreSitesFactoryProvider {
|
|||
return methods;
|
||||
}
|
||||
}
|
||||
|
||||
export class CoreSitesFactory extends makeSingleton(CoreSitesFactoryProvider) {}
|
||||
|
|
|
@ -30,6 +30,7 @@ import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
|
|||
import { SQLiteDB, SQLiteDBTableSchema } from '@classes/sqlitedb';
|
||||
import { Md5 } from 'ts-md5/dist/md5';
|
||||
import { WP_PROVIDER } from '@app/app.module';
|
||||
import { makeSingleton } from '@singletons/core.singletons';
|
||||
|
||||
/**
|
||||
* Response of checking if a site exists and its configuration.
|
||||
|
@ -1925,5 +1926,6 @@ export class CoreSitesProvider {
|
|||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class CoreSites extends makeSingleton(CoreSitesProvider) {}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { CoreEventsProvider } from './events';
|
||||
import { CoreSitesProvider, CoreSiteSchema } from './sites';
|
||||
import { makeSingleton } from '@singletons/core.singletons';
|
||||
|
||||
/*
|
||||
* Service that provides some features regarding synchronization.
|
||||
|
@ -206,3 +207,5 @@ export class CoreSyncProvider {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class CoreSync extends makeSingleton(CoreSyncProvider) {}
|
||||
|
|
|
@ -17,6 +17,8 @@ import { CoreConfigProvider } from './config';
|
|||
import { CoreInitHandler, CoreInitDelegate } from './init';
|
||||
import { CoreLoggerProvider } from './logger';
|
||||
import { CoreConfigConstants } from '../configconstants';
|
||||
import { CoreH5P } from '@core/h5p/providers/h5p';
|
||||
import { makeSingleton } from '@singletons/core.singletons';
|
||||
|
||||
/**
|
||||
* Factory to handle app updates. This factory shouldn't be used outside of core.
|
||||
|
@ -33,7 +35,8 @@ export class CoreUpdateManagerProvider implements CoreInitHandler {
|
|||
protected VERSION_APPLIED = 'version_applied';
|
||||
protected logger;
|
||||
|
||||
constructor(logger: CoreLoggerProvider, private configProvider: CoreConfigProvider) {
|
||||
constructor(logger: CoreLoggerProvider,
|
||||
protected configProvider: CoreConfigProvider) {
|
||||
this.logger = logger.getInstance('CoreUpdateManagerProvider');
|
||||
}
|
||||
|
||||
|
@ -43,19 +46,24 @@ export class CoreUpdateManagerProvider implements CoreInitHandler {
|
|||
*
|
||||
* @return Promise resolved when the update process finishes.
|
||||
*/
|
||||
load(): Promise<any> {
|
||||
const promises = [],
|
||||
versionCode = CoreConfigConstants.versioncode;
|
||||
async load(): Promise<any> {
|
||||
const promises = [];
|
||||
const versionCode = CoreConfigConstants.versioncode;
|
||||
|
||||
return this.configProvider.get(this.VERSION_APPLIED, 0).then((versionApplied: number) => {
|
||||
const versionApplied: number = await this.configProvider.get(this.VERSION_APPLIED, 0);
|
||||
|
||||
// Put here the code to treat app updates.
|
||||
if (versionCode >= 3900 && versionApplied < 3900 && versionApplied > 0) {
|
||||
promises.push(CoreH5P.instance.deleteAllContentIndexes());
|
||||
}
|
||||
|
||||
return Promise.all(promises).then(() => {
|
||||
return this.configProvider.set(this.VERSION_APPLIED, versionCode);
|
||||
}).catch((error) => {
|
||||
this.logger.error(`Error applying update from ${versionApplied} to ${versionCode}`, error);
|
||||
});
|
||||
});
|
||||
try {
|
||||
await Promise.all(promises);
|
||||
|
||||
await this.configProvider.set(this.VERSION_APPLIED, versionCode);
|
||||
} catch (error) {
|
||||
this.logger.error(`Error applying update from ${versionApplied} to ${versionCode}`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class CoreUpdateManager extends makeSingleton(CoreUpdateManagerProvider) {}
|
||||
|
|
|
@ -28,6 +28,7 @@ import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate'
|
|||
import { CoreSitePluginsProvider } from '@core/siteplugins/providers/siteplugins';
|
||||
import { CoreConfigConstants } from '../configconstants';
|
||||
import { CoreConstants } from '@core/constants';
|
||||
import { makeSingleton } from '@singletons/core.singletons';
|
||||
|
||||
/**
|
||||
* All params that can be in a custom URL scheme.
|
||||
|
@ -488,3 +489,5 @@ export class CoreCustomURLSchemesProvider {
|
|||
return url.indexOf(CoreConfigConstants.customurlscheme + '://token=') != -1;
|
||||
}
|
||||
}
|
||||
|
||||
export class CoreCustomURLSchemes extends makeSingleton(CoreCustomURLSchemesProvider) {}
|
||||
|
|
|
@ -30,6 +30,7 @@ import { CoreConstants } from '@core/constants';
|
|||
import { CoreBSTooltipComponent } from '@components/bs-tooltip/bs-tooltip';
|
||||
import { Md5 } from 'ts-md5/dist/md5';
|
||||
import { Subject } from 'rxjs';
|
||||
import { makeSingleton } from '@singletons/core.singletons';
|
||||
|
||||
/**
|
||||
* Interface that defines an extension of the Ionic Alert class, to support multiple listeners.
|
||||
|
@ -1666,3 +1667,5 @@ export class CoreDomUtilsProvider {
|
|||
}, siteId);
|
||||
}
|
||||
}
|
||||
|
||||
export class CoreDomUtils extends makeSingleton(CoreDomUtilsProvider) {}
|
||||
|
|
|
@ -25,6 +25,7 @@ import { CoreTextUtilsProvider } from './text';
|
|||
import { CoreUrlUtilsProvider } from './url';
|
||||
import { CoreUtilsProvider } from './utils';
|
||||
import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper';
|
||||
import { makeSingleton } from '@singletons/core.singletons';
|
||||
|
||||
/*
|
||||
* "Utils" service with helper functions for iframes, embed and similar.
|
||||
|
@ -224,7 +225,7 @@ export class CoreIframeUtilsProvider {
|
|||
} else {
|
||||
element.setAttribute('src', url);
|
||||
}
|
||||
} else if (url.indexOf('cdvfile://') === 0 || url.indexOf('file://') === 0) {
|
||||
} else if (this.urlUtils.isLocalFileUrl(url)) {
|
||||
// It's a local file.
|
||||
this.utils.openFile(url).catch((error) => {
|
||||
this.domUtils.showErrorModal(error);
|
||||
|
@ -352,16 +353,14 @@ export class CoreIframeUtilsProvider {
|
|||
return;
|
||||
}
|
||||
|
||||
if (scheme && scheme != 'file' && scheme != 'filesystem') {
|
||||
if (!this.urlUtils.isLocalFileUrlScheme(scheme)) {
|
||||
// Scheme suggests it's an external resource.
|
||||
event.preventDefault();
|
||||
|
||||
const frameSrc = (<HTMLFrameElement> element).src || (<HTMLObjectElement> element).data,
|
||||
frameScheme = this.urlUtils.getUrlScheme(frameSrc);
|
||||
const frameSrc = (<HTMLFrameElement> element).src || (<HTMLObjectElement> element).data;
|
||||
|
||||
// If the frame is not local, check the target to identify how to treat the link.
|
||||
if (frameScheme && frameScheme != 'file' && frameScheme != 'filesystem' &&
|
||||
(!link.target || link.target == '_self')) {
|
||||
if (!this.urlUtils.isLocalFileUrl(frameSrc) && (!link.target || link.target == '_self')) {
|
||||
// Load the link inside the frame itself.
|
||||
if (element.tagName.toLowerCase() == 'object') {
|
||||
element.setAttribute('data', link.href);
|
||||
|
@ -396,6 +395,8 @@ export class CoreIframeUtilsProvider {
|
|||
}
|
||||
}
|
||||
|
||||
export class CoreIframeUtils extends makeSingleton(CoreIframeUtilsProvider) {}
|
||||
|
||||
/**
|
||||
* Subtype of HTMLAnchorElement, with some calculated data.
|
||||
*/
|
||||
|
|
|
@ -14,9 +14,11 @@
|
|||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { CoreFile } from '../file';
|
||||
import { CoreLoggerProvider } from '../logger';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { CoreTextUtilsProvider } from './text';
|
||||
import { makeSingleton } from '@singletons/core.singletons';
|
||||
|
||||
/*
|
||||
* "Utils" service with helper functions for mimetypes and extensions.
|
||||
|
@ -164,7 +166,7 @@ export class CoreMimetypeUtilsProvider {
|
|||
if (this.canBeEmbedded(ext)) {
|
||||
file.embedType = this.getExtensionType(ext);
|
||||
|
||||
path = path || file.fileurl || (file.toURL && file.toURL());
|
||||
path = CoreFile.instance.convertFileSrc(path || file.fileurl || (file.toURL && file.toURL()));
|
||||
|
||||
if (file.embedType == 'image') {
|
||||
return '<img src="' + path + '">';
|
||||
|
@ -549,3 +551,5 @@ export class CoreMimetypeUtilsProvider {
|
|||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
export class CoreMimetypeUtils extends makeSingleton(CoreMimetypeUtilsProvider) {}
|
||||
|
|
|
@ -17,6 +17,7 @@ import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
|
|||
import { ModalController, Platform } from 'ionic-angular';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { CoreLangProvider } from '../lang';
|
||||
import { makeSingleton } from '@singletons/core.singletons';
|
||||
|
||||
/**
|
||||
* Different type of errors the app can treat.
|
||||
|
@ -1106,3 +1107,5 @@ export class CoreTextUtilsProvider {
|
|||
return _unserialize((data + ''), 0)[2];
|
||||
}
|
||||
}
|
||||
|
||||
export class CoreTextUtils extends makeSingleton(CoreTextUtilsProvider) {}
|
||||
|
|
|
@ -16,6 +16,7 @@ import { Injectable } from '@angular/core';
|
|||
import { TranslateService } from '@ngx-translate/core';
|
||||
import * as moment from 'moment';
|
||||
import { CoreConstants } from '@core/constants';
|
||||
import { makeSingleton } from '@singletons/core.singletons';
|
||||
|
||||
/*
|
||||
* "Utils" service with helper functions for date and time.
|
||||
|
@ -353,3 +354,5 @@ export class CoreTimeUtilsProvider {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class CoreTimeUtils extends makeSingleton(CoreTimeUtilsProvider) {}
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { CoreLangProvider } from '../lang';
|
||||
import { CoreTextUtilsProvider } from './text';
|
||||
import { makeSingleton } from '@singletons/core.singletons';
|
||||
import { CoreConfigConstants } from '../../configconstants';
|
||||
|
||||
/*
|
||||
* "Utils" service with helper functions for URLs.
|
||||
|
@ -423,6 +425,26 @@ export class CoreUrlUtilsProvider {
|
|||
return /^https?\:\/\/.+/i.test(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether an URL belongs to a local file.
|
||||
*
|
||||
* @param url URL to check.
|
||||
* @return Whether the URL belongs to a local file.
|
||||
*/
|
||||
isLocalFileUrl(url: string): boolean {
|
||||
return this.isLocalFileUrlScheme(this.getUrlScheme(url));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a URL scheme belongs to a local file.
|
||||
*
|
||||
* @param scheme Scheme to check.
|
||||
* @return Whether the scheme belongs to a local file.
|
||||
*/
|
||||
isLocalFileUrlScheme(scheme: string): boolean {
|
||||
return scheme == 'cdvfile' || scheme == 'file' || scheme == 'filesystem' || scheme == CoreConfigConstants.webviewscheme;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if a URL is a pluginfile URL.
|
||||
*
|
||||
|
@ -498,3 +520,5 @@ export class CoreUrlUtilsProvider {
|
|||
return url;
|
||||
}
|
||||
}
|
||||
|
||||
export class CoreUrlUtils extends makeSingleton(CoreUrlUtilsProvider) {}
|
||||
|
|
|
@ -27,6 +27,8 @@ import { CoreLoggerProvider } from '../logger';
|
|||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { CoreLangProvider } from '../lang';
|
||||
import { CoreWSProvider, CoreWSError } from '../ws';
|
||||
import { CoreFile } from '../file';
|
||||
import { makeSingleton } from '@singletons/core.singletons';
|
||||
|
||||
/**
|
||||
* Deferred promise. It's similar to the result of $q.defer() in AngularJS.
|
||||
|
@ -862,8 +864,11 @@ export class CoreUtilsProvider {
|
|||
* @return Promise resolved when done.
|
||||
*/
|
||||
openFile(path: string): Promise<any> {
|
||||
const extension = this.mimetypeUtils.getFileExtension(path),
|
||||
mimetype = this.mimetypeUtils.getMimeType(extension);
|
||||
// Convert the path to a native path if needed.
|
||||
path = CoreFile.instance.unconvertFileSrc(path);
|
||||
|
||||
const extension = this.mimetypeUtils.getFileExtension(path);
|
||||
const mimetype = this.mimetypeUtils.getMimeType(extension);
|
||||
|
||||
// Path needs to be decoded, the file won't be opened if the path has %20 instead of spaces and so.
|
||||
try {
|
||||
|
@ -1416,3 +1421,5 @@ export class CoreUtilsProvider {
|
|||
return debounced;
|
||||
}
|
||||
}
|
||||
|
||||
export class CoreUtils extends makeSingleton(CoreUtilsProvider) {}
|
||||
|
|
|
@ -24,6 +24,9 @@ import { CoreTextUtilsProvider } from './utils/text';
|
|||
import { CoreConstants } from '@core/constants';
|
||||
import { Md5 } from 'ts-md5/dist/md5';
|
||||
import { CoreInterceptor } from '@classes/interceptor';
|
||||
import { makeSingleton } from '@singletons/core.singletons';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { CoreNativeToAngularHttpResponse } from '@classes/native-to-angular-http';
|
||||
|
||||
/**
|
||||
* PreSets accepted by the WS call.
|
||||
|
@ -80,6 +83,61 @@ export interface CoreWSAjaxPreSets {
|
|||
useGet?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for HTTP requests.
|
||||
*/
|
||||
export type HttpRequestOptions = {
|
||||
/**
|
||||
* The HTTP method.
|
||||
*/
|
||||
method: string;
|
||||
|
||||
/**
|
||||
* Payload to send to the server. Only applicable on post, put or patch methods.
|
||||
*/
|
||||
data?: any;
|
||||
|
||||
/**
|
||||
* Query params to be appended to the URL (only applicable on get, head, delete, upload or download methods).
|
||||
*/
|
||||
params?: any;
|
||||
|
||||
/**
|
||||
* Response type. Defaults to json.
|
||||
*/
|
||||
responseType?: 'json' | 'text' | 'arraybuffer' | 'blob';
|
||||
|
||||
/**
|
||||
* Timeout for the request in seconds. If undefined, the default value will be used. If null, no timeout.
|
||||
*/
|
||||
timeout?: number | null;
|
||||
|
||||
/**
|
||||
* Serializer to use. Defaults to 'urlencoded'. Only for mobile environments.
|
||||
*/
|
||||
serializer?: string;
|
||||
|
||||
/**
|
||||
* Whether to follow redirects. Defaults to true. Only for mobile environments.
|
||||
*/
|
||||
followRedirect?: boolean;
|
||||
|
||||
/**
|
||||
* Headers. Only for mobile environments.
|
||||
*/
|
||||
headers?: {[name: string]: string};
|
||||
|
||||
/**
|
||||
* File paths to use for upload or download. Only for mobile environments.
|
||||
*/
|
||||
filePath?: string;
|
||||
|
||||
/**
|
||||
* Name to use during upload. Only for mobile environments.
|
||||
*/
|
||||
name?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* This service allows performing WS calls and download/upload files.
|
||||
*/
|
||||
|
@ -376,8 +434,8 @@ export class CoreWSProvider {
|
|||
return Promise.resolve(this.mimeTypeCache[url]);
|
||||
}
|
||||
|
||||
return this.performHead(url).then((data) => {
|
||||
let mimeType = data.headers.get('Content-Type');
|
||||
return this.performHead(url).then((response) => {
|
||||
let mimeType = response.headers.get('Content-Type');
|
||||
if (mimeType) {
|
||||
// Remove "parameters" like charset.
|
||||
mimeType = mimeType.split(';')[0];
|
||||
|
@ -398,8 +456,8 @@ export class CoreWSProvider {
|
|||
* @return Promise resolved with the size or -1 if failure.
|
||||
*/
|
||||
getRemoteFileSize(url: string): Promise<number> {
|
||||
return this.performHead(url).then((data) => {
|
||||
const size = parseInt(data.headers.get('Content-Length'), 10);
|
||||
return this.performHead(url).then((response) => {
|
||||
const size = parseInt(response.headers.get('Content-Length'), 10);
|
||||
|
||||
if (size) {
|
||||
return size;
|
||||
|
@ -462,12 +520,12 @@ export class CoreWSProvider {
|
|||
preSets.responseExpected = true;
|
||||
}
|
||||
|
||||
const script = preSets.noLogin ? 'service-nologin.php' : 'service.php',
|
||||
ajaxData = JSON.stringify([{
|
||||
index: 0,
|
||||
methodname: method,
|
||||
args: this.convertValuesToString(data)
|
||||
}]);
|
||||
const script = preSets.noLogin ? 'service-nologin.php' : 'service.php';
|
||||
const ajaxData = [{
|
||||
index: 0,
|
||||
methodname: method,
|
||||
args: this.convertValuesToString(data)
|
||||
}];
|
||||
|
||||
// The info= parameter has no function. It is just to help with debugging.
|
||||
// We call it info to match the parameter name use by Moodle's AMD ajax module.
|
||||
|
@ -475,13 +533,22 @@ export class CoreWSProvider {
|
|||
|
||||
if (preSets.noLogin && preSets.useGet) {
|
||||
// Send params using GET.
|
||||
siteUrl += '&args=' + encodeURIComponent(ajaxData);
|
||||
promise = this.http.get(siteUrl).timeout(this.getRequestTimeout()).toPromise();
|
||||
siteUrl += '&args=' + encodeURIComponent(JSON.stringify(ajaxData));
|
||||
|
||||
promise = this.sendHTTPRequest(siteUrl, {
|
||||
method: 'get',
|
||||
});
|
||||
} else {
|
||||
promise = this.http.post(siteUrl, ajaxData).timeout(this.getRequestTimeout()).toPromise();
|
||||
promise = this.sendHTTPRequest(siteUrl, {
|
||||
method: 'post',
|
||||
data: ajaxData,
|
||||
serializer: 'json',
|
||||
});
|
||||
}
|
||||
|
||||
return promise.then((data: any) => {
|
||||
return promise.then((response: HttpResponse<any>) => {
|
||||
let data = response.body;
|
||||
|
||||
// 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.
|
||||
if (!data && !preSets.responseExpected) {
|
||||
|
@ -535,8 +602,11 @@ export class CoreWSProvider {
|
|||
let promise = this.getPromiseHttp('head', url);
|
||||
|
||||
if (!promise) {
|
||||
promise = this.http.head(url, {observe: 'response', responseType: 'blob'}).timeout(this.getRequestTimeout())
|
||||
.toPromise();
|
||||
promise = this.sendHTTPRequest(url, {
|
||||
method: 'head',
|
||||
responseType: 'text',
|
||||
});
|
||||
|
||||
promise = this.setPromiseHttp(promise, 'head', url);
|
||||
}
|
||||
|
||||
|
@ -570,6 +640,7 @@ export class CoreWSProvider {
|
|||
const promise = this.http.post(requestUrl, ajaxData, options).timeout(this.getRequestTimeout()).toPromise();
|
||||
|
||||
return promise.then((data: any) => {
|
||||
|
||||
// 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 (!data && !preSets.responseExpected) {
|
||||
|
@ -870,15 +941,107 @@ export class CoreWSProvider {
|
|||
*/
|
||||
async getText(url: string): Promise<string> {
|
||||
// Fetch the URL content.
|
||||
const content = await this.http.get(url, { responseType: 'text' }).toPromise();
|
||||
const options: HttpRequestOptions = {
|
||||
method: 'get',
|
||||
responseType: 'text',
|
||||
};
|
||||
|
||||
const response = await this.sendHTTPRequest(url, options);
|
||||
|
||||
const content = response.body;
|
||||
|
||||
if (typeof content !== 'string') {
|
||||
return Promise.reject(null);
|
||||
throw 'Error reading content';
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an HTTP request. In mobile devices it will use the cordova plugin.
|
||||
*
|
||||
* @param url URL of the request.
|
||||
* @param options Options for the request.
|
||||
* @return Promise resolved with the response.
|
||||
*/
|
||||
async sendHTTPRequest(url: string, options: HttpRequestOptions): Promise<HttpResponse<any>> {
|
||||
|
||||
// Set default values.
|
||||
options.responseType = options.responseType || 'json';
|
||||
options.timeout = typeof options.timeout == 'undefined' ? this.getRequestTimeout() : options.timeout;
|
||||
|
||||
if (this.appProvider.isMobile()) {
|
||||
// Use the cordova plugin.
|
||||
if (url.indexOf('file://') === 0) {
|
||||
// We cannot load local files using the http native plugin. Use file provider instead.
|
||||
const format = options.responseType == 'json' ? CoreFileProvider.FORMATJSON : CoreFileProvider.FORMATTEXT;
|
||||
|
||||
const content = await this.fileProvider.readFile(url, format);
|
||||
|
||||
return new HttpResponse({
|
||||
body: content,
|
||||
headers: null,
|
||||
status: 200,
|
||||
statusText: 'OK',
|
||||
url: url
|
||||
});
|
||||
}
|
||||
|
||||
return new Promise<HttpResponse<any>>((resolve, reject): void => {
|
||||
// We cannot use Ionic Native plugin because it doesn't have the sendRequest method.
|
||||
(<any> cordova).plugin.http.sendRequest(url, options, (response) => {
|
||||
resolve(new CoreNativeToAngularHttpResponse(response));
|
||||
}, reject);
|
||||
});
|
||||
} else {
|
||||
let observable: Observable<any>;
|
||||
|
||||
// Use Angular's library.
|
||||
switch (options.method) {
|
||||
case 'get':
|
||||
observable = this.http.get(url, {
|
||||
headers: options.headers,
|
||||
params: options.params,
|
||||
observe: 'response',
|
||||
responseType: <any> options.responseType,
|
||||
});
|
||||
break;
|
||||
|
||||
case 'post':
|
||||
if (options.serializer == 'json') {
|
||||
options.data = JSON.stringify(options.data);
|
||||
}
|
||||
|
||||
observable = this.http.post(url, options.data, {
|
||||
headers: options.headers,
|
||||
observe: 'response',
|
||||
responseType: <any> options.responseType,
|
||||
});
|
||||
break;
|
||||
|
||||
case 'head':
|
||||
observable = this.http.head(url, {
|
||||
headers: options.headers,
|
||||
observe: 'response',
|
||||
responseType: <any> options.responseType
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
return Promise.reject('Method not implemented yet.');
|
||||
}
|
||||
|
||||
if (options.timeout) {
|
||||
observable = observable.timeout(options.timeout);
|
||||
}
|
||||
|
||||
return observable.toPromise();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class CoreWS extends makeSingleton(CoreWSProvider) {}
|
||||
|
||||
/**
|
||||
* Error returned by a WS call.
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { AlertController, App } from 'ionic-angular';
|
||||
import { Injector } from '@angular/core';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
|
||||
import { CoreSingletonsFactory, CoreInjectionToken, CoreSingletonClass } from '@classes/singletons-factory';
|
||||
|
||||
const factory = new CoreSingletonsFactory();
|
||||
|
||||
/**
|
||||
* Set the injector that will be used to resolve instances in the singletons of this module.
|
||||
*
|
||||
* @param injector Module injector.
|
||||
*/
|
||||
export function setSingletonsInjector(injector: Injector): void {
|
||||
factory.setInjector(injector);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a singleton for this module.
|
||||
*
|
||||
* @param injectionToken Injection token used to resolve the singleton instance. This is usually the service class if the
|
||||
* provider was defined using a class or the string used in the `provide` key if it was defined using an object.
|
||||
*/
|
||||
export function makeSingleton<Service>(injectionToken: CoreInjectionToken<Service>): CoreSingletonClass<Service> {
|
||||
return factory.makeSingleton(injectionToken);
|
||||
}
|
||||
|
||||
export class Translate extends makeSingleton(TranslateService) {}
|
||||
|
||||
export class Alerts extends makeSingleton(AlertController) {}
|
||||
|
||||
export class Ionic extends makeSingleton(App) {}
|
||||
|
||||
export class Http extends makeSingleton(HttpClient) {}
|
|
@ -21,7 +21,8 @@
|
|||
"@providers/*": ["providers/*"],
|
||||
"@components/*": ["components/*"],
|
||||
"@directives/*": ["directives/*"],
|
||||
"@pipes/*": ["pipes/*"]
|
||||
"@pipes/*": ["pipes/*"],
|
||||
"@singletons/*": ["singletons/*"]
|
||||
},
|
||||
"typeRoots": [
|
||||
"node_modules/@types"
|
||||
|
|
Loading…
Reference in New Issue