commit
ec29493a33
|
@ -29,5 +29,8 @@ npm-debug.log*
|
|||
/plugins
|
||||
/www
|
||||
|
||||
/config/config.*.json
|
||||
/moodle.*.config.json
|
||||
!/moodle.example.config.json
|
||||
|
||||
/src/assets/lang/*
|
||||
/src/assets/env.json
|
||||
|
|
|
@ -5,4 +5,3 @@ script:
|
|||
- npm run lint
|
||||
- npm run test:ci
|
||||
- npm run build:prod
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
|
||||
"files.associations": {
|
||||
"config.json": "jsonc",
|
||||
"config.*.json": "jsonc",
|
||||
"moodle.config.json": "jsonc",
|
||||
"moodle.*.config.json": "jsonc",
|
||||
},
|
||||
|
||||
}
|
||||
|
|
15
angular.json
15
angular.json
|
@ -12,7 +12,7 @@
|
|||
"schematics": {},
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-builders/custom-webpack:browser",
|
||||
"builder": "@angular-devkit/build-angular:browser",
|
||||
"options": {
|
||||
"outputPath": "www",
|
||||
"index": "src/index.html",
|
||||
|
@ -36,10 +36,7 @@
|
|||
"input": "src/theme/theme.scss"
|
||||
}
|
||||
],
|
||||
"scripts": [],
|
||||
"customWebpackConfig": {
|
||||
"path": "./config/webpack.config.js"
|
||||
}
|
||||
"scripts": []
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
|
@ -66,7 +63,7 @@
|
|||
}
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular-builders/custom-webpack:dev-server",
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"options": {
|
||||
"browserTarget": "app:build"
|
||||
},
|
||||
|
@ -89,9 +86,9 @@
|
|||
"builder": "@angular-eslint/builder:lint",
|
||||
"options": {
|
||||
"lintFilePatterns": [
|
||||
"src/**/*.ts",
|
||||
"src/core/**/*.html",
|
||||
"src/addons/**/*.html"
|
||||
"src/**/*.ts",
|
||||
"src/core/**/*.html",
|
||||
"src/addons/**/*.html"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
// (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.
|
||||
|
||||
const webpack = require('webpack');
|
||||
const { getConfig, getBuild } = require('./utils');
|
||||
const { resolve } = require('path');
|
||||
|
||||
module.exports = config => {
|
||||
config.resolve.alias['@'] = resolve('src');
|
||||
config.resolve.alias['@classes'] = resolve('src/core/classes');
|
||||
config.resolve.alias['@components'] = resolve('src/core/components');
|
||||
config.resolve.alias['@directives'] = resolve('src/core/directives');
|
||||
config.resolve.alias['@features'] = resolve('src/core/features');
|
||||
config.resolve.alias['@guards'] = resolve('src/core/guards');
|
||||
config.resolve.alias['@pipes'] = resolve('src/core/pipes');
|
||||
config.resolve.alias['@services'] = resolve('src/core/services');
|
||||
config.resolve.alias['@singletons'] = resolve('src/core/singletons');
|
||||
|
||||
config.plugins.push(
|
||||
new webpack.DefinePlugin({
|
||||
'window.MoodleApp': {
|
||||
CONFIG: JSON.stringify(getConfig(process.env.NODE_ENV || 'development')),
|
||||
BUILD: JSON.stringify(getBuild(process.env.NODE_ENV || 'development')),
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
return config;
|
||||
};
|
|
@ -0,0 +1,41 @@
|
|||
// (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.
|
||||
|
||||
const { getConfig, getBuild } = require('../scripts/env-utils');
|
||||
const { resolve } = require('path');
|
||||
const { writeFile } = require('fs');
|
||||
|
||||
/**
|
||||
* Task to build an env file depending on the current environment.
|
||||
*/
|
||||
class BuildEnvTask {
|
||||
|
||||
/**
|
||||
* Run the task.
|
||||
*
|
||||
* @param done Function to call when done.
|
||||
*/
|
||||
run(done) {
|
||||
const envFile = resolve(__dirname, '../src/assets/env.json');
|
||||
const env = {
|
||||
CONFIG: getConfig(process.env.NODE_ENV || 'development'),
|
||||
BUILD: getBuild(process.env.NODE_ENV || 'development'),
|
||||
};
|
||||
|
||||
writeFile(envFile, JSON.stringify(env), done);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = BuildEnvTask;
|
|
@ -13,6 +13,7 @@
|
|||
// limitations under the License.
|
||||
|
||||
const BuildLangTask = require('./gulp/task-build-lang');
|
||||
const BuildEnvTask = require('./gulp/task-build-env');
|
||||
const PushTask = require('./gulp/task-push');
|
||||
const Utils = require('./gulp/utils');
|
||||
const gulp = require('gulp');
|
||||
|
@ -34,12 +35,18 @@ gulp.task('lang', (done) => {
|
|||
new BuildLangTask().run(paths.lang, done);
|
||||
});
|
||||
|
||||
// Build an env file depending on the current environment.
|
||||
gulp.task('env', (done) => {
|
||||
new BuildEnvTask().run(done);
|
||||
});
|
||||
|
||||
gulp.task('push', (done) => {
|
||||
new PushTask().run(args, done);
|
||||
});
|
||||
|
||||
gulp.task('default', gulp.parallel('lang'));
|
||||
gulp.task('default', gulp.parallel(['lang', 'env']));
|
||||
|
||||
gulp.task('watch', () => {
|
||||
gulp.watch(paths.lang, { interval: 500 }, gulp.parallel('lang'));
|
||||
gulp.watch(['./moodle.config.json', './moodle.*.config.json'], { interval: 500 }, gulp.parallel('env'));
|
||||
});
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
/**
|
||||
* Application config.
|
||||
*
|
||||
* You can create your own environment files such as "config.prod.json" and "config.dev.json"
|
||||
* to override some values. The values will be merged, so you don't need to duplicate everything
|
||||
* in this file.
|
||||
* You can create your own environment files such as "moodle.config.prod.json" and "moodle.config.dev.json"
|
||||
* to override some values. The values will be merged, so you don't need to duplicate everything in this file.
|
||||
*/
|
||||
|
||||
{
|
|
@ -4,58 +4,29 @@
|
|||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"@angular-builders/custom-webpack": {
|
||||
"version": "10.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@angular-builders/custom-webpack/-/custom-webpack-10.0.1.tgz",
|
||||
"integrity": "sha512-YDy5zEKVwXdoXLjmbsY6kGaEbmunQxaPipxrwLUc9hIjRLU2WcrX9vopf1R9Pgj4POad73IPBNGu+ibqNRFIEQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@angular-devkit/architect": ">=0.1000.0 < 0.1100.0",
|
||||
"@angular-devkit/build-angular": ">=0.1000.0 < 0.1100.0",
|
||||
"@angular-devkit/core": "^10.0.0",
|
||||
"lodash": "^4.17.15",
|
||||
"ts-node": "^9.0.0",
|
||||
"webpack-merge": "^4.2.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"ts-node": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.0.0.tgz",
|
||||
"integrity": "sha512-/TqB4SnererCDR/vb4S/QvSZvzQMJN8daAslg7MeaiHvD8rDZsSfXmNeNumyZZzMned72Xoq/isQljYSt8Ynfg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"arg": "^4.1.0",
|
||||
"diff": "^4.0.1",
|
||||
"make-error": "^1.1.1",
|
||||
"source-map-support": "^0.5.17",
|
||||
"yn": "3.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@angular-devkit/architect": {
|
||||
"version": "0.1001.4",
|
||||
"resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1001.4.tgz",
|
||||
"integrity": "sha512-0U/w+61vWxnEe9Ln/hNOH6O27FVcU+s/sbJAuPREbP875R4bQzK2PX0eYRlISzkDtQyw16GzlsikLWOoJ3vjTA==",
|
||||
"version": "0.1101.2",
|
||||
"resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1101.2.tgz",
|
||||
"integrity": "sha512-MLmBfHiiyPhbFSSAX4oMecPjEuBauOui5uBpI6BKNnk/7783fznbkbAKjXlOco7M81gkNeEoHMR8c+mOfcvv7g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@angular-devkit/core": "10.1.4",
|
||||
"rxjs": "6.6.2"
|
||||
"@angular-devkit/core": "11.1.2",
|
||||
"rxjs": "6.6.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"rxjs": {
|
||||
"version": "6.6.2",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.2.tgz",
|
||||
"integrity": "sha512-BHdBMVoWC2sL26w//BCu3YzKT4s2jip/WhwsGEDmeKYBhKDZeYezVUnHatYB7L85v5xs0BAQmg6BEYJEKxBabg==",
|
||||
"version": "6.6.3",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz",
|
||||
"integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"tslib": "^1.9.0"
|
||||
}
|
||||
},
|
||||
"tslib": {
|
||||
"version": "1.13.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz",
|
||||
"integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==",
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
|
@ -249,22 +220,22 @@
|
|||
}
|
||||
},
|
||||
"@angular-devkit/core": {
|
||||
"version": "10.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-10.1.4.tgz",
|
||||
"integrity": "sha512-B1cwVcfChBvmEacydE2uqZ1UC2ez1G+KY0GyVnCQKpAb/DdfDgtaYjTx9JLvGQjE/BlVklEj8YCKDjVV0WPE5g==",
|
||||
"version": "11.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-11.1.2.tgz",
|
||||
"integrity": "sha512-V7zOMqL2l56JcwXVyswkG+7+t67r9XtkrVzRcG2Z5ZYwafU+iKWMwg5kBFZr1SX7fM1M9E4MpskxqtagQeUKng==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ajv": "6.12.4",
|
||||
"ajv": "6.12.6",
|
||||
"fast-json-stable-stringify": "2.1.0",
|
||||
"magic-string": "0.25.7",
|
||||
"rxjs": "6.6.2",
|
||||
"rxjs": "6.6.3",
|
||||
"source-map": "0.7.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"ajv": {
|
||||
"version": "6.12.4",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz",
|
||||
"integrity": "sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==",
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
|
@ -274,18 +245,18 @@
|
|||
}
|
||||
},
|
||||
"rxjs": {
|
||||
"version": "6.6.2",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.2.tgz",
|
||||
"integrity": "sha512-BHdBMVoWC2sL26w//BCu3YzKT4s2jip/WhwsGEDmeKYBhKDZeYezVUnHatYB7L85v5xs0BAQmg6BEYJEKxBabg==",
|
||||
"version": "6.6.3",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz",
|
||||
"integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"tslib": "^1.9.0"
|
||||
}
|
||||
},
|
||||
"tslib": {
|
||||
"version": "1.13.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz",
|
||||
"integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==",
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
|
@ -6422,9 +6393,9 @@
|
|||
"integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA=="
|
||||
},
|
||||
"com-darryncampbell-cordova-plugin-intent": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/com-darryncampbell-cordova-plugin-intent/-/com-darryncampbell-cordova-plugin-intent-2.0.0.tgz",
|
||||
"integrity": "sha512-4f5BAyhpiGVsuouj2cokZCb99RA8V4O5YZnwGMliceFCu35BQcQvC0VLW55jl+xVDLhVymomnaBiHSktKKnK4w=="
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/com-darryncampbell-cordova-plugin-intent/-/com-darryncampbell-cordova-plugin-intent-1.3.0.tgz",
|
||||
"integrity": "sha512-JXslndd4UiRHmirGZrwrHZHczoZ5sxM7zAylm4bPX7ZDwD4FdCHhILgDA8AeaG8wc11e0A7OEAFo0Esgc0M4yA=="
|
||||
},
|
||||
"combined-stream": {
|
||||
"version": "1.0.8",
|
||||
|
@ -7174,9 +7145,9 @@
|
|||
}
|
||||
},
|
||||
"cordova-plugin-advanced-http": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cordova-plugin-advanced-http/-/cordova-plugin-advanced-http-3.0.1.tgz",
|
||||
"integrity": "sha512-7P3ZoSvxvYZXNYsygkxrUIw+pnzsCVvQgRsm26XhymNqqmD9yZIcF878p6wfFVQfLzf5iRHQRwgAMcrcm+cnow=="
|
||||
"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",
|
||||
|
@ -7194,9 +7165,9 @@
|
|||
"integrity": "sha512-GfAibvrPdWe/ri+h3e3xkmq5bietY6yJRBIZawYDE7w600j2mtRsxgat7siWZtjRRhJuVsVwUG6H86Hyp3WKvA=="
|
||||
},
|
||||
"cordova-plugin-customurlscheme": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/cordova-plugin-customurlscheme/-/cordova-plugin-customurlscheme-5.0.2.tgz",
|
||||
"integrity": "sha512-g139Av7iYD3xcSsCd5S6a7B7dp4GTqGYtvdhh44g4OS38+aX6XkC1lsCRmROuhLIs4fkwJqkrvxacH9H4U9Gsg=="
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cordova-plugin-customurlscheme/-/cordova-plugin-customurlscheme-5.0.1.tgz",
|
||||
"integrity": "sha512-Nn3+MUrEGfBSFzkC9s5izzOcmpVy8Pya5oYF+CkcdqAlsqL7EqpUan3Q0Eold4EWFisVG5jRCg0XjyxL4uHGfw=="
|
||||
},
|
||||
"cordova-plugin-device": {
|
||||
"version": "2.0.3",
|
||||
|
@ -7209,9 +7180,9 @@
|
|||
"integrity": "sha512-m7cughw327CjONN/qjzsTpSesLaeybksQh420/gRuSXJX5Zt9NfgsSbqqKDon6jnQ9Mm7h7imgyO2uJ34XMBtA=="
|
||||
},
|
||||
"cordova-plugin-file-opener2": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/cordova-plugin-file-opener2/-/cordova-plugin-file-opener2-3.0.5.tgz",
|
||||
"integrity": "sha512-tjLHDamH5+y0bJZYVe2967L1S4R8tL4Y0rJUzJGoxsyiw3FUlrJNS199POOpzZZ6Xhlntn9a2o7+84r1dMN21A=="
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/cordova-plugin-file-opener2/-/cordova-plugin-file-opener2-3.0.4.tgz",
|
||||
"integrity": "sha512-bd1aCx62X2RwpC+KUiuB7quoxL/8RnPMEJU7x38Tvs+cUGLWBvsmR9+/LqGBsSns2CIqgnJ34TW0Vazoqu7Ieg=="
|
||||
},
|
||||
"cordova-plugin-file-transfer": {
|
||||
"version": "1.7.1",
|
||||
|
@ -7222,6 +7193,11 @@
|
|||
"version": "git+https://github.com/apache/cordova-plugin-geolocation.git#89cf51d222e8f225bdfb661965b3007d669c40ff",
|
||||
"from": "git+https://github.com/apache/cordova-plugin-geolocation.git#89cf51d222e8f225bdfb661965b3007d669c40ff"
|
||||
},
|
||||
"cordova-plugin-globalization": {
|
||||
"version": "1.11.0",
|
||||
"resolved": "https://registry.npmjs.org/cordova-plugin-globalization/-/cordova-plugin-globalization-1.11.0.tgz",
|
||||
"integrity": "sha1-6sMVgQAphJOvowvolA5pj2HvvP4="
|
||||
},
|
||||
"cordova-plugin-inappbrowser": {
|
||||
"version": "git+https://github.com/moodlemobile/cordova-plugin-inappbrowser.git#715c858975cc1cb5d140afaa7973938511d38509",
|
||||
"from": "git+https://github.com/moodlemobile/cordova-plugin-inappbrowser.git#moodle"
|
||||
|
@ -7232,8 +7208,9 @@
|
|||
"integrity": "sha512-6ucQ6FdlLdBm8kJfFnzozmBTjru/0xekHP/dAhjoCZggkGRlgs8TsUJFkxa/bV+qi7Dlo50JjmpE4UMWAO+aOQ=="
|
||||
},
|
||||
"cordova-plugin-ionic-webview": {
|
||||
"version": "git+https://github.com/moodlemobile/cordova-plugin-ionic-webview.git#ac90a8ac88e2c0512d6b250249b1f673f2fbcb68",
|
||||
"from": "git+https://github.com/moodlemobile/cordova-plugin-ionic-webview.git#500-moodle"
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/cordova-plugin-ionic-webview/-/cordova-plugin-ionic-webview-4.2.1.tgz",
|
||||
"integrity": "sha512-7KrmqLaOGq1RP8N2z1ezN1kqkWFzTwwMvQ3/qAkd+exxFZuOe3DIN4eaU1gdNphsxdirI8Ajnr9q4So5vQbWqw=="
|
||||
},
|
||||
"cordova-plugin-local-notification": {
|
||||
"version": "git+https://github.com/moodlemobile/cordova-plugin-local-notification.git#0bb96b757fb484553ceabf35a59802f7983a2836",
|
||||
|
@ -7308,27 +7285,27 @@
|
|||
}
|
||||
},
|
||||
"cordova-sqlite-storage": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cordova-sqlite-storage/-/cordova-sqlite-storage-5.1.0.tgz",
|
||||
"integrity": "sha512-UmHe9yQiYblDBToh3z91WHuD6ZgmCm3VX+1QFseYQs4WVQ3+ndj22qyGby/NV0uyCgok91gB1obLjLM+9vYJEw==",
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cordova-sqlite-storage/-/cordova-sqlite-storage-4.0.0.tgz",
|
||||
"integrity": "sha512-/n5KT3TyRAC7QRe9A4Sn7bMpdsBJ6aMmHat2PsMxFZBot45SOxbAEgfGmXtq0e7OEdVzk573sIn42bLS6lNLjQ==",
|
||||
"requires": {
|
||||
"cordova-sqlite-storage-dependencies": "3.0.0"
|
||||
"cordova-sqlite-storage-dependencies": "2.1.1"
|
||||
}
|
||||
},
|
||||
"cordova-sqlite-storage-dependencies": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cordova-sqlite-storage-dependencies/-/cordova-sqlite-storage-dependencies-3.0.0.tgz",
|
||||
"integrity": "sha512-A7gV5lQZc0oPrJ/a+lsZmMZr7vYou4MXyQFOY+b/dwuCMsagLT0EsL7oY54tqzpvjtzLfh0aZGGm9i8DMAIFSA=="
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/cordova-sqlite-storage-dependencies/-/cordova-sqlite-storage-dependencies-2.1.1.tgz",
|
||||
"integrity": "sha512-1lV5Pg1FttjBmGO8z4gxtuA4BbPKtgTfUEh1Vx4boa41inizyxaowRyTeaaqEhi5gmYAaX8sRTABm9U/XckRFg=="
|
||||
},
|
||||
"cordova-support-google-services": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/cordova-support-google-services/-/cordova-support-google-services-1.4.1.tgz",
|
||||
"integrity": "sha512-1VgF9kFCOMbzgdnsDtSKaYGmWXmeciGP8+N0wTcTkL2m6Qrs1xZ82NiYEJYXe7BjHad2d06liWThqQv7iXt5HA=="
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/cordova-support-google-services/-/cordova-support-google-services-1.3.2.tgz",
|
||||
"integrity": "sha512-RtEWzULreUX662MFWopGhFispLiHX7gUf2GijPOC2mY2oCNuUobj2mO4tl5q7PYbOreSxq+PrSekhmS6TAAWdw=="
|
||||
},
|
||||
"cordova.plugins.diagnostic": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/cordova.plugins.diagnostic/-/cordova.plugins.diagnostic-6.0.2.tgz",
|
||||
"integrity": "sha512-X3Nd0Ume1ZWndEJRtJ+BQTuTXBJfJv9hoI3PX7T/JiMMFQ/PgMwcn2DFTb27LWa65lAvMiEakMSRWmOa3/zvNg==",
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/cordova.plugins.diagnostic/-/cordova.plugins.diagnostic-5.0.2.tgz",
|
||||
"integrity": "sha512-H59o7YxJ2/COzvg+jyTpUqX8QoDcvti9dluJ9a+pHumE8lf3meWemwCl0QFa9GH+xgVd6X1Ikj/6P3+DKWd9eg==",
|
||||
"requires": {
|
||||
"colors": "^1.1.2",
|
||||
"elementtree": "^0.1.6",
|
||||
|
|
31
package.json
31
package.json
|
@ -22,10 +22,10 @@
|
|||
"start": "ionic serve",
|
||||
"build": "ionic build",
|
||||
"build:prod": "ionic build --prod",
|
||||
"test": "jest --verbose",
|
||||
"test:ci": "jest -ci --runInBand --verbose",
|
||||
"test:watch": "jest --watch",
|
||||
"test:coverage": "jest --coverage",
|
||||
"test": "NODE_ENV=testing gulp && jest --verbose",
|
||||
"test:ci": "NODE_ENV=testing gulp && jest -ci --runInBand --verbose",
|
||||
"test:watch": "NODE_ENV=testing gulp watch & jest --watch",
|
||||
"test:coverage": "NODE_ENV=testing gulp && jest --coverage",
|
||||
"lint": "ng lint",
|
||||
"ionic:serve:before": "gulp",
|
||||
"ionic:serve": "gulp watch & ng serve",
|
||||
|
@ -71,26 +71,27 @@
|
|||
"@types/cordova": "0.0.34",
|
||||
"@types/cordova-plugin-file-transfer": "^1.6.2",
|
||||
"@types/dom-mediacapture-record": "^1.0.7",
|
||||
"com-darryncampbell-cordova-plugin-intent": "^2.0.0",
|
||||
"com-darryncampbell-cordova-plugin-intent": "^1.3.0",
|
||||
"cordova": "^10.0.0",
|
||||
"cordova-android": "^8.1.0",
|
||||
"cordova-android-support-gradle-release": "^3.0.1",
|
||||
"cordova-clipboard": "^1.3.0",
|
||||
"cordova-ios": "^5.1.1",
|
||||
"cordova-plugin-add-swift-support": "^2.0.2",
|
||||
"cordova-plugin-advanced-http": "^3.0.1",
|
||||
"cordova-plugin-advanced-http": "^2.4.1",
|
||||
"cordova-plugin-badge": "^0.8.8",
|
||||
"cordova-plugin-camera": "^4.1.0",
|
||||
"cordova-plugin-chooser": "^1.3.2",
|
||||
"cordova-plugin-customurlscheme": "^5.0.2",
|
||||
"cordova-plugin-customurlscheme": "^5.0.1",
|
||||
"cordova-plugin-device": "^2.0.3",
|
||||
"cordova-plugin-file": "^6.0.2",
|
||||
"cordova-plugin-file-opener2": "^3.0.5",
|
||||
"cordova-plugin-file-opener2": "^3.0.4",
|
||||
"cordova-plugin-file-transfer": "1.7.1",
|
||||
"cordova-plugin-geolocation": "git+https://github.com/apache/cordova-plugin-geolocation.git#89cf51d222e8f225bdfb661965b3007d669c40ff",
|
||||
"cordova-plugin-globalization": "^1.11.0",
|
||||
"cordova-plugin-inappbrowser": "git+https://github.com/moodlemobile/cordova-plugin-inappbrowser.git#moodle",
|
||||
"cordova-plugin-ionic-keyboard": "2.1.3",
|
||||
"cordova-plugin-ionic-webview": "git+https://github.com/moodlemobile/cordova-plugin-ionic-webview.git#500-moodle",
|
||||
"cordova-plugin-ionic-webview": "^4.2.1",
|
||||
"cordova-plugin-local-notification": "git+https://github.com/moodlemobile/cordova-plugin-local-notification.git#moodle",
|
||||
"cordova-plugin-media": "^5.0.3",
|
||||
"cordova-plugin-media-capture": "^3.0.3",
|
||||
|
@ -103,9 +104,9 @@
|
|||
"cordova-plugin-wkuserscript": "git+https://github.com/moodlemobile/cordova-plugin-wkuserscript.git",
|
||||
"cordova-plugin-wkwebview-cookies": "git+https://github.com/moodlemobile/cordova-plugin-wkwebview-cookies.git",
|
||||
"cordova-plugin-zip": "^3.1.0",
|
||||
"cordova-sqlite-storage": "^5.1.0",
|
||||
"cordova-sqlite-storage": "^4.0.0",
|
||||
"cordova-support-google-services": "^1.2.1",
|
||||
"cordova.plugins.diagnostic": "^6.0.2",
|
||||
"cordova.plugins.diagnostic": "^5.0.2",
|
||||
"es6-promise-plugin": "^4.2.2",
|
||||
"jszip": "^3.5.0",
|
||||
"moment": "^2.29.0",
|
||||
|
@ -118,7 +119,7 @@
|
|||
"zone.js": "~0.10.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-builders/custom-webpack": "^10.0.1",
|
||||
"@angular-devkit/architect": "^0.1101.2",
|
||||
"@angular-devkit/build-angular": "~0.1000.0",
|
||||
"@angular-eslint/builder": "0.5.0-beta.2",
|
||||
"@angular-eslint/eslint-plugin": "0.5.0-beta.2",
|
||||
|
@ -169,7 +170,9 @@
|
|||
"ios"
|
||||
],
|
||||
"plugins": {
|
||||
"cordova-plugin-advanced-http": {},
|
||||
"cordova-plugin-advanced-http": {
|
||||
"OKHTTP_VERSION": "3.10.0"
|
||||
},
|
||||
"cordova-clipboard": {},
|
||||
"cordova-plugin-badge": {},
|
||||
"cordova-plugin-camera": {
|
||||
|
@ -219,7 +222,7 @@
|
|||
"ANDROID_SUPPORT_VERSION": "27.+"
|
||||
},
|
||||
"cordova.plugins.diagnostic": {
|
||||
"ANDROIDX_VERSION": "1.+"
|
||||
"ANDROID_SUPPORT_VERSION": "28.+"
|
||||
},
|
||||
"cordova-plugin-globalization": {},
|
||||
"cordova-plugin-file-transfer": {}
|
||||
|
|
|
@ -23,9 +23,9 @@ function getConfig(environment) {
|
|||
development: ['dev', 'development'],
|
||||
production: ['prod', 'production'],
|
||||
};
|
||||
const config = parseJsonc(readFileSync(resolve('config/config.json')).toString());
|
||||
const config = parseJsonc(readFileSync(resolve(__dirname, '../moodle.config.json')).toString());
|
||||
const envSuffixes = (envSuffixesMap[environment] || []);
|
||||
const envConfigPath = envSuffixes.map(suffix => resolve(`config/config.${suffix}.json`)).find(existsSync);
|
||||
const envConfigPath = envSuffixes.map(suffix => resolve(__dirname, `../moodle.${suffix}.config.json`)).find(existsSync);
|
||||
|
||||
if (envConfigPath) {
|
||||
const envConfig = parseJsonc(readFileSync(envConfigPath).toString());
|
|
@ -26,6 +26,7 @@ import {
|
|||
} from '@angular/router';
|
||||
|
||||
import { CoreArray } from '@singletons/array';
|
||||
import { CoreRedirectGuard } from '@guards/redirect';
|
||||
|
||||
/**
|
||||
* Build app routes.
|
||||
|
@ -34,7 +35,16 @@ import { CoreArray } from '@singletons/array';
|
|||
* @return App routes.
|
||||
*/
|
||||
function buildAppRoutes(injector: Injector): Routes {
|
||||
return CoreArray.flatten(injector.get<Routes[]>(APP_ROUTES, []));
|
||||
const appRoutes = CoreArray.flatten(injector.get<Routes[]>(APP_ROUTES, []));
|
||||
|
||||
return appRoutes.map(route => {
|
||||
route.canLoad = route.canLoad ?? [];
|
||||
route.canActivate = route.canActivate ?? [];
|
||||
route.canLoad.push(CoreRedirectGuard);
|
||||
route.canActivate.push(CoreRedirectGuard);
|
||||
|
||||
return route;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -17,17 +17,16 @@ import { Observable } from 'rxjs';
|
|||
import { AppComponent } from '@/app/app.component';
|
||||
import { CoreApp } from '@services/app';
|
||||
import { CoreEvents } from '@singletons/events';
|
||||
import { CoreLangProvider } from '@services/lang';
|
||||
import { CoreLang, CoreLangProvider } from '@services/lang';
|
||||
import { Network, Platform, NgZone } from '@singletons';
|
||||
|
||||
import { mock, mockSingleton, renderComponent, RenderConfig } from '@/testing/utils';
|
||||
import { mockSingleton, renderComponent } from '@/testing/utils';
|
||||
import { CoreNavigator, CoreNavigatorService } from '@services/navigator';
|
||||
|
||||
describe('AppComponent', () => {
|
||||
|
||||
let langProvider: CoreLangProvider;
|
||||
let navigator: CoreNavigatorService;
|
||||
let config: Partial<RenderConfig>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockSingleton(CoreApp, { setStatusBarColor: jest.fn() });
|
||||
|
@ -36,23 +35,18 @@ describe('AppComponent', () => {
|
|||
mockSingleton(NgZone, { run: jest.fn() });
|
||||
|
||||
navigator = mockSingleton(CoreNavigator, ['navigate']);
|
||||
langProvider = mock<CoreLangProvider>(['clearCustomStrings']);
|
||||
config = {
|
||||
providers: [
|
||||
{ provide: CoreLangProvider, useValue: langProvider },
|
||||
],
|
||||
};
|
||||
langProvider = mockSingleton(CoreLang, ['clearCustomStrings']);
|
||||
});
|
||||
|
||||
it('should render', async () => {
|
||||
const fixture = await renderComponent(AppComponent, config);
|
||||
const fixture = await renderComponent(AppComponent);
|
||||
|
||||
expect(fixture.debugElement.componentInstance).toBeTruthy();
|
||||
expect(fixture.nativeElement.querySelector('ion-router-outlet')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('cleans up on logout', async () => {
|
||||
const fixture = await renderComponent(AppComponent, config);
|
||||
const fixture = await renderComponent(AppComponent);
|
||||
|
||||
fixture.componentInstance.ngOnInit();
|
||||
CoreEvents.trigger(CoreEvents.LOGOUT);
|
||||
|
@ -61,6 +55,4 @@ describe('AppComponent', () => {
|
|||
expect(navigator.navigate).toHaveBeenCalledWith('/login/sites', { reset: true });
|
||||
});
|
||||
|
||||
it.todo('shows loading while app isn\'t ready');
|
||||
|
||||
});
|
||||
|
|
|
@ -12,10 +12,11 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { AfterViewInit, Component, OnInit, ViewChild } from '@angular/core';
|
||||
import { IonRouterOutlet } from '@ionic/angular';
|
||||
|
||||
import { CoreLangProvider } from '@services/lang';
|
||||
import { CoreLoginHelperProvider } from '@features/login/services/login-helper';
|
||||
import { CoreLang } from '@services/lang';
|
||||
import { CoreLoginHelper } from '@features/login/services/login-helper';
|
||||
import {
|
||||
CoreEvents,
|
||||
CoreEventSessionExpiredData,
|
||||
|
@ -23,23 +24,20 @@ import {
|
|||
CoreEventSiteData,
|
||||
CoreEventSiteUpdatedData,
|
||||
} from '@singletons/events';
|
||||
import { Network, NgZone, Platform } from '@singletons';
|
||||
import { Network, NgZone, Platform, SplashScreen } from '@singletons';
|
||||
import { CoreApp } from '@services/app';
|
||||
import { CoreSites } from '@services/sites';
|
||||
import { CoreNavigator } from '@services/navigator';
|
||||
import { CoreSubscriptions } from '@singletons/subscriptions';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: 'app.component.html',
|
||||
styleUrls: ['app.component.scss'],
|
||||
})
|
||||
export class AppComponent implements OnInit {
|
||||
export class AppComponent implements OnInit, AfterViewInit {
|
||||
|
||||
constructor(
|
||||
protected langProvider: CoreLangProvider,
|
||||
protected loginHelper: CoreLoginHelperProvider,
|
||||
) {
|
||||
}
|
||||
@ViewChild(IonRouterOutlet) outlet?: IonRouterOutlet;
|
||||
|
||||
/**
|
||||
* Component being initialized.
|
||||
|
@ -58,7 +56,7 @@ export class AppComponent implements OnInit {
|
|||
CoreNavigator.instance.navigate('/login/sites', { reset: true });
|
||||
|
||||
// Unload lang custom strings.
|
||||
this.langProvider.clearCustomStrings();
|
||||
CoreLang.instance.clearCustomStrings();
|
||||
|
||||
// Remove version classes from body.
|
||||
this.removeVersionClass();
|
||||
|
@ -66,20 +64,20 @@ export class AppComponent implements OnInit {
|
|||
|
||||
// Listen for session expired events.
|
||||
CoreEvents.on(CoreEvents.SESSION_EXPIRED, (data: CoreEventSessionExpiredData) => {
|
||||
this.loginHelper.sessionExpired(data);
|
||||
CoreLoginHelper.instance.sessionExpired(data);
|
||||
});
|
||||
|
||||
// Listen for passwordchange and usernotfullysetup events to open InAppBrowser.
|
||||
CoreEvents.on(CoreEvents.PASSWORD_CHANGE_FORCED, (data: CoreEventSiteData) => {
|
||||
this.loginHelper.passwordChangeForced(data.siteId!);
|
||||
CoreLoginHelper.instance.passwordChangeForced(data.siteId!);
|
||||
});
|
||||
CoreEvents.on(CoreEvents.USER_NOT_FULLY_SETUP, (data: CoreEventSiteData) => {
|
||||
this.loginHelper.openInAppForEdit(data.siteId!, '/user/edit.php', 'core.usernotfullysetup');
|
||||
CoreLoginHelper.instance.openInAppForEdit(data.siteId!, '/user/edit.php', 'core.usernotfullysetup');
|
||||
});
|
||||
|
||||
// Listen for sitepolicynotagreed event to accept the site policy.
|
||||
CoreEvents.on(CoreEvents.SITE_POLICY_NOT_AGREED, (data: CoreEventSiteData) => {
|
||||
this.loginHelper.sitePolicyNotAgreed(data.siteId);
|
||||
CoreLoginHelper.instance.sitePolicyNotAgreed(data.siteId);
|
||||
});
|
||||
|
||||
CoreEvents.on(CoreEvents.LOGIN, async (data: CoreEventSiteData) => {
|
||||
|
@ -119,6 +117,17 @@ export class AppComponent implements OnInit {
|
|||
this.onPlatformReady();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
ngAfterViewInit(): void {
|
||||
if (!this.outlet) {
|
||||
return;
|
||||
}
|
||||
|
||||
CoreSubscriptions.once(this.outlet.activateEvents, () => SplashScreen.instance.hide());
|
||||
}
|
||||
|
||||
/**
|
||||
* Async init function on platform ready.
|
||||
*/
|
||||
|
@ -155,8 +164,9 @@ export class AppComponent implements OnInit {
|
|||
*/
|
||||
protected loadCustomStrings(): void {
|
||||
const currentSite = CoreSites.instance.getCurrentSite();
|
||||
|
||||
if (currentSite) {
|
||||
this.langProvider.loadCustomStringsFromSite(currentSite);
|
||||
CoreLang.instance.loadCustomStringsFromSite(currentSite);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 55 KiB |
|
@ -1087,7 +1087,7 @@ export class SQLiteDB {
|
|||
}
|
||||
|
||||
export type SQLiteDBRecordValues = {
|
||||
[key in string ]: SQLiteDBRecordValue | undefined | null;
|
||||
[key: string]: SQLiteDBRecordValue | undefined | null;
|
||||
};
|
||||
|
||||
export type SQLiteDBQueryParams = {
|
||||
|
|
|
@ -12,6 +12,12 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
|
||||
import { CoreColorScheme } from '@features/settings/services/settings-helper';
|
||||
import { CoreSitesDemoSiteData } from '@services/sites';
|
||||
import envJson from '@/assets/env.json';
|
||||
|
||||
/**
|
||||
* Context levels enumeration.
|
||||
*/
|
||||
|
@ -114,7 +120,51 @@ export class CoreConstants {
|
|||
static readonly MOD_ARCHETYPE_SYSTEM = 3; // System (not user-addable) module archetype.
|
||||
|
||||
// Config & environment constants.
|
||||
static readonly CONFIG = (window as unknown as MoodleAppWindow).MoodleApp.CONFIG; // Data parsed from config.json files.
|
||||
static readonly BUILD = (window as unknown as MoodleAppWindow).MoodleApp.BUILD; // Environment info.
|
||||
static readonly CONFIG = envJson.CONFIG as unknown as EnvironmentConfig; // Data parsed from config.json files.
|
||||
static readonly BUILD = envJson.BUILD as unknown as EnvironmentBuild; // Build info.
|
||||
|
||||
}
|
||||
|
||||
type EnvironmentConfig = {
|
||||
app_id: string;
|
||||
appname: string;
|
||||
versioncode: number;
|
||||
versionname: string;
|
||||
cache_update_frequency_usually: number;
|
||||
cache_update_frequency_often: number;
|
||||
cache_update_frequency_sometimes: number;
|
||||
cache_update_frequency_rarely: number;
|
||||
default_lang: string;
|
||||
languages: Record<string, string>;
|
||||
wsservice: string;
|
||||
wsextservice: string;
|
||||
demo_sites: Record<string, CoreSitesDemoSiteData>;
|
||||
font_sizes: number[];
|
||||
customurlscheme: string;
|
||||
siteurl: string;
|
||||
sitename: string;
|
||||
multisitesdisplay: string;
|
||||
sitefindersettings: Record<string, unknown>;
|
||||
onlyallowlistedsites: boolean;
|
||||
skipssoconfirmation: boolean;
|
||||
forcedefaultlanguage: boolean;
|
||||
privacypolicy: string;
|
||||
notificoncolor: string;
|
||||
enableanalytics: boolean;
|
||||
enableonboarding: boolean;
|
||||
forceColorScheme: CoreColorScheme;
|
||||
forceLoginLogo: boolean;
|
||||
ioswebviewscheme: string;
|
||||
appstores: Record<string, string>;
|
||||
displayqroncredentialscreen?: boolean;
|
||||
displayqronsitescreen?: boolean;
|
||||
forceOpenLinksIn: 'app' | 'browser';
|
||||
};
|
||||
|
||||
type EnvironmentBuild = {
|
||||
isProduction: boolean;
|
||||
isTesting: boolean;
|
||||
isDevelopment: boolean;
|
||||
lastCommitHash: string;
|
||||
compilationTime: number;
|
||||
};
|
||||
|
|
|
@ -38,6 +38,7 @@ import { CoreLinkDirective } from './link';
|
|||
import { CoreFilter, CoreFilterFilter, CoreFilterFormatTextOptions } from '@features/filter/services/filter';
|
||||
import { CoreFilterDelegate } from '@features/filter/services/filter-delegate';
|
||||
import { CoreFilterHelper } from '@features/filter/services/filter-helper';
|
||||
import { CoreSubscriptions } from '@singletons/subscriptions';
|
||||
|
||||
/**
|
||||
* Directive to format text rendered. It renders the HTML and treats all links and media, using CoreLinkDirective
|
||||
|
@ -567,12 +568,7 @@ export class CoreFormatTextDirective implements OnChanges {
|
|||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return new Promise((resolve): void => {
|
||||
const subscription = externalImage.onLoad.subscribe(() => {
|
||||
subscription.unsubscribe();
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
return new Promise(resolve => CoreSubscriptions.once(externalImage.onLoad, resolve));
|
||||
}));
|
||||
|
||||
// Automatically reject the promise after 5 seconds to prevent blocking the user forever.
|
||||
|
|
|
@ -17,13 +17,7 @@ import { APP_INITIALIZER, NgModule } from '@angular/core';
|
|||
import { CorePluginFileDelegate } from '@services/plugin-file-delegate';
|
||||
import { CORE_SITE_SCHEMAS } from '@services/sites';
|
||||
import { CoreH5PComponentsModule } from './components/components.module';
|
||||
import {
|
||||
CONTENT_TABLE_NAME,
|
||||
LIBRARIES_TABLE_NAME,
|
||||
LIBRARY_DEPENDENCIES_TABLE_NAME,
|
||||
CONTENTS_LIBRARIES_TABLE_NAME,
|
||||
LIBRARIES_CACHEDASSETS_TABLE_NAME,
|
||||
} from './services/database/h5p';
|
||||
import { SITE_SCHEMA } from './services/database/h5p';
|
||||
import { CoreH5PPluginFileHandler } from './services/handlers/pluginfile';
|
||||
|
||||
@NgModule({
|
||||
|
@ -33,13 +27,7 @@ import { CoreH5PPluginFileHandler } from './services/handlers/pluginfile';
|
|||
providers: [
|
||||
{
|
||||
provide: CORE_SITE_SCHEMAS,
|
||||
useValue: [
|
||||
CONTENT_TABLE_NAME,
|
||||
LIBRARIES_TABLE_NAME,
|
||||
LIBRARY_DEPENDENCIES_TABLE_NAME,
|
||||
CONTENTS_LIBRARIES_TABLE_NAME,
|
||||
LIBRARIES_CACHEDASSETS_TABLE_NAME,
|
||||
],
|
||||
useValue: [SITE_SCHEMA],
|
||||
multi: true,
|
||||
},
|
||||
{
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
// (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 { Injectable } from '@angular/core';
|
||||
import { CanActivate, CanLoad, UrlTree } from '@angular/router';
|
||||
|
||||
import { CoreSites } from '@services/sites';
|
||||
import { CoreUtils } from '@services/utils/utils';
|
||||
import { Router } from '@singletons';
|
||||
|
||||
import { CoreLoginHelper } from '../services/login-helper';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class CoreLoginHasSitesGuard implements CanActivate, CanLoad {
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
canActivate(): Promise<true | UrlTree> {
|
||||
return this.guard();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
canLoad(): Promise<true | UrlTree> {
|
||||
return this.guard();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the user has any sites stored.
|
||||
*/
|
||||
private async guard(): Promise<true | UrlTree> {
|
||||
const sites = await CoreUtils.instance.ignoreErrors(CoreSites.instance.getSites(), []);
|
||||
|
||||
if (sites.length > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const [path, params] = CoreLoginHelper.instance.getAddSiteRouteInfo();
|
||||
const route = Router.instance.parseUrl(path);
|
||||
|
||||
route.queryParams = params;
|
||||
|
||||
return route;
|
||||
}
|
||||
|
||||
}
|
|
@ -21,16 +21,13 @@ import { TranslateModule } from '@ngx-translate/core';
|
|||
import { CoreSharedModule } from '@/core/shared.module';
|
||||
import { CoreLoginSiteHelpComponent } from './components/site-help/site-help';
|
||||
import { CoreLoginSiteOnboardingComponent } from './components/site-onboarding/site-onboarding';
|
||||
import { CoreLoginHasSitesGuard } from './guards/has-sites';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
redirectTo: 'init',
|
||||
pathMatch: 'full',
|
||||
},
|
||||
{
|
||||
path: 'init',
|
||||
loadChildren: () => import('./pages/init/init.module').then( m => m.CoreLoginInitPageModule),
|
||||
redirectTo: 'sites',
|
||||
},
|
||||
{
|
||||
path: 'site',
|
||||
|
@ -43,6 +40,8 @@ const routes: Routes = [
|
|||
{
|
||||
path: 'sites',
|
||||
loadChildren: () => import('./pages/sites/sites.module').then( m => m.CoreLoginSitesPageModule),
|
||||
canLoad: [CoreLoginHasSitesGuard],
|
||||
canActivate: [CoreLoginHasSitesGuard],
|
||||
},
|
||||
{
|
||||
path: 'forgottenpassword',
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
<ion-content fullscreen="true" scrollY="false">
|
||||
<div class="core-bglogo" slot="fixed">
|
||||
<div class="core-center-spinner">
|
||||
<ion-spinner></ion-spinner>
|
||||
</div>
|
||||
</div>
|
||||
</ion-content>
|
|
@ -1,25 +0,0 @@
|
|||
ion-content::part(background) {
|
||||
--background: var(--core-splash-screen-background, #ffffff);
|
||||
|
||||
background-image: url("~@/assets/img/splash.png");
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100%;
|
||||
background-size: var(--core-splash-bgsize, 100vmax);
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.core-bglogo {
|
||||
display: table;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.core-center-spinner {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
ion-spinner {
|
||||
--color: var(--core-splash-spinner-color, var(--core-color));
|
||||
}
|
||||
}
|
|
@ -1,125 +0,0 @@
|
|||
// (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 { Component, OnInit } from '@angular/core';
|
||||
|
||||
import { CoreApp, CoreRedirectData } from '@services/app';
|
||||
import { ApplicationInit, SplashScreen } from '@singletons';
|
||||
import { CoreConstants } from '@/core/constants';
|
||||
import { CoreSites } from '@services/sites';
|
||||
import { CoreLoginHelper } from '@features/login/services/login-helper';
|
||||
import { CoreNavigator } from '@services/navigator';
|
||||
|
||||
/**
|
||||
* Page that displays a "splash screen" while the app is being initialized.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'page-core-login-init',
|
||||
templateUrl: 'init.html',
|
||||
styleUrls: ['init.scss'],
|
||||
})
|
||||
export class CoreLoginInitPage implements OnInit {
|
||||
|
||||
// @todo this page should be removed in favor of native splash
|
||||
// or a splash component rendered in the root app component
|
||||
|
||||
/**
|
||||
* Initialize the component.
|
||||
*/
|
||||
async ngOnInit(): Promise<void> {
|
||||
// Wait for the app to be ready.
|
||||
await ApplicationInit.instance.donePromise;
|
||||
|
||||
// Check if there was a pending redirect.
|
||||
const redirectData = CoreApp.instance.getRedirect();
|
||||
|
||||
if (redirectData.siteId) {
|
||||
await this.handleRedirect(redirectData);
|
||||
} else {
|
||||
await this.loadPage();
|
||||
}
|
||||
|
||||
// If we hide the splash screen now, the init view is still seen for an instant. Wait a bit to make sure it isn't seen.
|
||||
setTimeout(() => {
|
||||
SplashScreen.instance.hide();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Treat redirect data.
|
||||
*
|
||||
* @param redirectData Redirect data.
|
||||
*/
|
||||
protected async handleRedirect(redirectData: CoreRedirectData): Promise<void> {
|
||||
// Unset redirect data.
|
||||
CoreApp.instance.storeRedirect('', '', {});
|
||||
|
||||
// Only accept the redirect if it was stored less than 20 seconds ago.
|
||||
if (redirectData.timemodified && Date.now() - redirectData.timemodified < 20000) {
|
||||
if (redirectData.siteId != CoreConstants.NO_SITE_ID) {
|
||||
// The redirect is pointing to a site, load it.
|
||||
try {
|
||||
const loggedIn = await CoreSites.instance.loadSite(
|
||||
redirectData.siteId!,
|
||||
redirectData.page,
|
||||
redirectData.params,
|
||||
);
|
||||
|
||||
if (!loggedIn) {
|
||||
return;
|
||||
}
|
||||
|
||||
await CoreNavigator.instance.navigateToSiteHome({
|
||||
params: {
|
||||
redirectPath: redirectData.page,
|
||||
redirectParams: redirectData.params,
|
||||
},
|
||||
});
|
||||
|
||||
return;
|
||||
} catch (error) {
|
||||
// Site doesn't exist.
|
||||
return this.loadPage();
|
||||
}
|
||||
} else if (redirectData.page) {
|
||||
// No site to load, open the page.
|
||||
// @todo return CoreNavigator.instance.goToNoSitePage(redirectData.page, redirectData.params);
|
||||
}
|
||||
}
|
||||
|
||||
return this.loadPage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the right page.
|
||||
*
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
protected async loadPage(): Promise<void> {
|
||||
if (CoreSites.instance.isLoggedIn()) {
|
||||
if (CoreLoginHelper.instance.isSiteLoggedOut()) {
|
||||
await CoreSites.instance.logout();
|
||||
|
||||
return this.loadPage();
|
||||
}
|
||||
|
||||
await CoreNavigator.instance.navigateToSiteHome();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await CoreNavigator.instance.navigate('/login/sites', { reset: true });
|
||||
}
|
||||
|
||||
}
|
|
@ -45,13 +45,7 @@ export class CoreLoginSitesPage implements OnInit {
|
|||
* @return Promise resolved when done.
|
||||
*/
|
||||
async ngOnInit(): Promise<void> {
|
||||
const sites = await CoreUtils.instance.ignoreErrors(CoreSites.instance.getSortedSites());
|
||||
|
||||
if (!sites || sites.length == 0) {
|
||||
CoreLoginHelper.instance.goToAddSite(true);
|
||||
|
||||
return;
|
||||
}
|
||||
const sites = await CoreUtils.instance.ignoreErrors(CoreSites.instance.getSortedSites(), [] as CoreSiteBasicInfo[]);
|
||||
|
||||
// Remove protocol from the url to show more url text.
|
||||
this.sites = sites.map((site) => {
|
||||
|
|
|
@ -34,6 +34,7 @@ import { makeSingleton, Translate } from '@singletons';
|
|||
import { CoreLogger } from '@singletons/logger';
|
||||
import { CoreUrl } from '@singletons/url';
|
||||
import { CoreNavigator } from '@services/navigator';
|
||||
import { CoreObject } from '@singletons/object';
|
||||
|
||||
/**
|
||||
* Helper provider that provides some common features regarding authentication.
|
||||
|
@ -408,22 +409,27 @@ export class CoreLoginHelperProvider {
|
|||
* @return Promise resolved when done.
|
||||
*/
|
||||
async goToAddSite(setRoot?: boolean, showKeyboard?: boolean): Promise<void> {
|
||||
let pageRoute: string;
|
||||
let params: Params;
|
||||
const [path, params] = this.getAddSiteRouteInfo(showKeyboard);
|
||||
|
||||
await CoreNavigator.instance.navigate(path, { params, reset: setRoot });
|
||||
}
|
||||
|
||||
/**
|
||||
* Get path and params to visit the route to add site.
|
||||
*
|
||||
* @param showKeyboard Whether to show keyboard in the new page. Only if no fixed URL set.
|
||||
* @return Path and params.
|
||||
*/
|
||||
getAddSiteRouteInfo(showKeyboard?: boolean): [string, Params] {
|
||||
if (this.isFixedUrlSet()) {
|
||||
// Fixed URL is set, go to credentials page.
|
||||
const fixedSites = this.getFixedSites();
|
||||
const url = typeof fixedSites == 'string' ? fixedSites : fixedSites[0].url;
|
||||
|
||||
pageRoute = '/login/credentials';
|
||||
params = { siteUrl: url };
|
||||
} else {
|
||||
pageRoute = '/login/site';
|
||||
params = { showKeyboard: showKeyboard };
|
||||
return ['/login/credentials', { siteUrl: url }];
|
||||
}
|
||||
|
||||
await CoreNavigator.instance.navigate(pageRoute, { params, reset: setRoot });
|
||||
return ['/login/site', CoreObject.withoutEmpty({ showKeyboard: showKeyboard })];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
// (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 { CoreApp } from '@services/app';
|
||||
import { CoreLoginInitPage } from '@features/login/pages/init/init';
|
||||
import { CoreSites } from '@services/sites';
|
||||
import { ApplicationInit, SplashScreen } from '@singletons';
|
||||
|
||||
import { mockSingleton, renderComponent } from '@/testing/utils';
|
||||
import { CoreNavigator, CoreNavigatorService } from '@services/navigator';
|
||||
|
||||
describe('CoreLoginInitPage', () => {
|
||||
|
||||
let navigator: CoreNavigatorService;
|
||||
|
||||
beforeEach(() => {
|
||||
mockSingleton(CoreApp, { getRedirect: () => ({}) });
|
||||
mockSingleton(ApplicationInit, { donePromise: Promise.resolve() });
|
||||
mockSingleton(CoreSites, { isLoggedIn: () => false });
|
||||
mockSingleton(SplashScreen, ['hide']);
|
||||
|
||||
navigator = mockSingleton(CoreNavigator, ['navigate']);
|
||||
});
|
||||
|
||||
it('should render', async () => {
|
||||
const fixture = await renderComponent(CoreLoginInitPage, {});
|
||||
|
||||
expect(fixture.debugElement.componentInstance).toBeTruthy();
|
||||
expect(fixture.nativeElement.querySelector('ion-spinner')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('navigates to sites page after loading', async () => {
|
||||
const fixture = await renderComponent(CoreLoginInitPage, {});
|
||||
|
||||
fixture.componentInstance.ngOnInit();
|
||||
await ApplicationInit.instance.donePromise;
|
||||
|
||||
expect(navigator.navigate).toHaveBeenCalledWith('/login/sites', { reset: true });
|
||||
});
|
||||
|
||||
});
|
|
@ -13,28 +13,44 @@
|
|||
// limitations under the License.
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Router, CanLoad, CanActivate, UrlTree } from '@angular/router';
|
||||
import { CanLoad, CanActivate, UrlTree } from '@angular/router';
|
||||
import { CoreLoginHelper } from '@features/login/services/login-helper';
|
||||
|
||||
import { CoreSites } from '@services/sites';
|
||||
import { ApplicationInit } from '@singletons';
|
||||
import { Router } from '@singletons';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class AuthGuard implements CanLoad, CanActivate {
|
||||
|
||||
constructor(private router: Router) {}
|
||||
export class CoreMainMenuAuthGuard implements CanLoad, CanActivate {
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
canActivate(): Promise<true | UrlTree> {
|
||||
return this.guard();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
canLoad(): Promise<true | UrlTree> {
|
||||
return this.guard();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current user should be redirected to the authentication page.
|
||||
*/
|
||||
private async guard(): Promise<true | UrlTree> {
|
||||
await ApplicationInit.instance.donePromise;
|
||||
if (!CoreSites.instance.isLoggedIn()) {
|
||||
return Router.instance.parseUrl('/login');
|
||||
}
|
||||
|
||||
return CoreSites.instance.isLoggedIn() || this.router.parseUrl('/login');
|
||||
if (CoreLoginHelper.instance.isSiteLoggedOut()) {
|
||||
await CoreSites.instance.logout();
|
||||
|
||||
return Router.instance.parseUrl('/login');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
import { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||
import { Routes } from '@angular/router';
|
||||
import { AuthGuard } from '@guards/auth';
|
||||
import { CoreMainMenuAuthGuard } from '@features/mainmenu/guards/auth';
|
||||
|
||||
import { AppRoutingModule } from '@/app/app-routing.module';
|
||||
|
||||
|
@ -30,8 +30,8 @@ const appRoutes: Routes = [
|
|||
{
|
||||
path: 'main',
|
||||
loadChildren: () => import('./mainmenu-lazy.module').then(m => m.CoreMainMenuLazyModule),
|
||||
canActivate: [AuthGuard],
|
||||
canLoad: [AuthGuard],
|
||||
canActivate: [CoreMainMenuAuthGuard],
|
||||
canLoad: [CoreMainMenuAuthGuard],
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
@ -15,16 +15,14 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
|
||||
import { CORE_SITE_SCHEMAS } from '@services/sites';
|
||||
import { STATEMENTS_TABLE_NAME } from './services/database/xapi';
|
||||
import { SITE_SCHEMA } from './services/database/xapi';
|
||||
|
||||
@NgModule({
|
||||
imports: [],
|
||||
providers: [
|
||||
{
|
||||
provide: CORE_SITE_SCHEMAS,
|
||||
useValue: [
|
||||
STATEMENTS_TABLE_NAME,
|
||||
],
|
||||
useValue: [SITE_SCHEMA],
|
||||
multi: true,
|
||||
},
|
||||
],
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
// (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 { Injectable } from '@angular/core';
|
||||
import { CanActivate, CanLoad, UrlTree } from '@angular/router';
|
||||
import { CoreApp } from '@services/app';
|
||||
import { CoreSites } from '@services/sites';
|
||||
import { Router } from '@singletons';
|
||||
import { CoreObject } from '@singletons/object';
|
||||
import { CoreConstants } from '../constants';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class CoreRedirectGuard implements CanLoad, CanActivate {
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
canLoad(): Promise<true | UrlTree> {
|
||||
return this.guard();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
canActivate(): Promise<true | UrlTree> {
|
||||
return this.guard();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there is a pending redirect and trigger it.
|
||||
*/
|
||||
private async guard(): Promise<true | UrlTree> {
|
||||
const redirect = CoreApp.instance.getRedirect();
|
||||
|
||||
if (!redirect) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
// Only accept the redirect if it was stored less than 20 seconds ago.
|
||||
if (!redirect.timemodified || Date.now() - redirect.timemodified < 20000) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Redirect to site path.
|
||||
if (redirect.siteId && redirect.siteId !== CoreConstants.NO_SITE_ID) {
|
||||
const loggedIn = await CoreSites.instance.loadSite(
|
||||
redirect.siteId,
|
||||
redirect.page,
|
||||
redirect.params,
|
||||
);
|
||||
const route = Router.instance.parseUrl('/main');
|
||||
|
||||
route.queryParams = CoreObject.withoutEmpty({
|
||||
redirectPath: redirect.page,
|
||||
redirectParams: redirect.params,
|
||||
});
|
||||
|
||||
return loggedIn ? route : true;
|
||||
}
|
||||
|
||||
// Abort redirect.
|
||||
if (!redirect.page) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Redirect to non-site path.
|
||||
const route = Router.instance.parseUrl(redirect.page);
|
||||
|
||||
route.queryParams = CoreObject.withoutEmpty({
|
||||
redirectPath: redirect.page,
|
||||
redirectParams: redirect.params,
|
||||
});
|
||||
|
||||
return route;
|
||||
} finally {
|
||||
CoreApp.instance.forgetRedirect();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -12,27 +12,8 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { IonicModule } from '@ionic/angular';
|
||||
import { CoreApp } from '@services/app';
|
||||
|
||||
import { CoreLoginInitPage } from './init';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: CoreLoginInitPage,
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
RouterModule.forChild(routes),
|
||||
IonicModule,
|
||||
],
|
||||
declarations: [
|
||||
CoreLoginInitPage,
|
||||
],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class CoreLoginInitPageModule {}
|
||||
export default function(): void {
|
||||
CoreApp.instance.consumeStorageRedirect();
|
||||
}
|
|
@ -25,6 +25,7 @@ import { makeSingleton, Keyboard, Network, StatusBar, Platform, Device } from '@
|
|||
import { CoreLogger } from '@singletons/logger';
|
||||
import { CoreColors } from '@singletons/colors';
|
||||
import { DBNAME, SCHEMA_VERSIONS_TABLE_NAME, SCHEMA_VERSIONS_TABLE_SCHEMA, SchemaVersionsDBEntry } from '@services/database/app';
|
||||
import { CoreObject } from '@singletons/object';
|
||||
|
||||
/**
|
||||
* Object responsible of managing schema versions.
|
||||
|
@ -58,6 +59,7 @@ export class CoreAppProvider {
|
|||
protected keyboardClosing = false;
|
||||
protected backActions: {callback: () => boolean; priority: number}[] = [];
|
||||
protected forceOffline = false;
|
||||
protected redirect?: CoreRedirectData;
|
||||
|
||||
// Variables for DB.
|
||||
protected schemaVersionsManager: Promise<SchemaVersionsManager>;
|
||||
|
@ -516,32 +518,50 @@ export class CoreAppProvider {
|
|||
await deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read redirect data from local storage and clear it if it existed.
|
||||
*/
|
||||
consumeStorageRedirect(): void {
|
||||
if (!localStorage?.getItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Read data from storage.
|
||||
const jsonData = localStorage.getItem('CoreRedirect');
|
||||
|
||||
if (!jsonData) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear storage.
|
||||
localStorage.removeItem('CoreRedirect');
|
||||
|
||||
// Remember redirect data.
|
||||
const data: CoreRedirectData = JSON.parse(jsonData);
|
||||
|
||||
if (!CoreObject.isEmpty(data)) {
|
||||
this.redirect = data;
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.error('Error loading redirect data:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Forget redirect data.
|
||||
*/
|
||||
forgetRedirect(): void {
|
||||
delete this.redirect;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve redirect data.
|
||||
*
|
||||
* @return Object with siteid, state, params and timemodified.
|
||||
*/
|
||||
getRedirect(): CoreRedirectData {
|
||||
if (localStorage?.getItem) {
|
||||
try {
|
||||
const paramsJson = localStorage.getItem('CoreRedirectParams');
|
||||
const data: CoreRedirectData = {
|
||||
siteId: localStorage.getItem('CoreRedirectSiteId') || undefined,
|
||||
page: localStorage.getItem('CoreRedirectState') || undefined,
|
||||
timemodified: parseInt(localStorage.getItem('CoreRedirectTime') || '0', 10),
|
||||
};
|
||||
|
||||
if (paramsJson) {
|
||||
data.params = JSON.parse(paramsJson);
|
||||
}
|
||||
|
||||
return data;
|
||||
} catch (ex) {
|
||||
this.logger.error('Error loading redirect data:', ex);
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
getRedirect(): CoreRedirectData | null {
|
||||
return this.redirect || null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -552,15 +572,17 @@ export class CoreAppProvider {
|
|||
* @param params Page params.
|
||||
*/
|
||||
storeRedirect(siteId: string, page: string, params: Params): void {
|
||||
if (localStorage && localStorage.setItem) {
|
||||
try {
|
||||
localStorage.setItem('CoreRedirectSiteId', siteId);
|
||||
localStorage.setItem('CoreRedirectState', page);
|
||||
localStorage.setItem('CoreRedirectParams', JSON.stringify(params));
|
||||
localStorage.setItem('CoreRedirectTime', String(Date.now()));
|
||||
} catch (ex) {
|
||||
// Ignore errors.
|
||||
}
|
||||
try {
|
||||
const redirect: CoreRedirectData = {
|
||||
siteId,
|
||||
page,
|
||||
params,
|
||||
timemodified: Date.now(),
|
||||
};
|
||||
|
||||
localStorage.setItem('CoreRedirect', JSON.stringify(redirect));
|
||||
} catch (ex) {
|
||||
// Ignore errors.
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1233,7 +1233,7 @@ export class CoreFileProvider {
|
|||
* @return Converted src.
|
||||
*/
|
||||
convertFileSrc(src: string): string {
|
||||
return CoreApp.instance.isIOS() ? WebView.instance.convertFileSrc(src) : src;
|
||||
return CoreApp.instance.isMobile() ? WebView.instance.convertFileSrc(src) : src;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1243,11 +1243,13 @@ export class CoreFileProvider {
|
|||
* @return Unconverted src.
|
||||
*/
|
||||
unconvertFileSrc(src: string): string {
|
||||
if (!CoreApp.instance.isIOS()) {
|
||||
if (!CoreApp.instance.isMobile()) {
|
||||
return src;
|
||||
}
|
||||
|
||||
return src.replace(CoreConstants.CONFIG.ioswebviewscheme + '://localhost/_app_file_', 'file://');
|
||||
const scheme = CoreApp.instance.isIOS() ? CoreConstants.CONFIG.ioswebviewscheme : 'http';
|
||||
|
||||
return src.replace(scheme + '://localhost/_app_file_', 'file://');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -19,7 +19,6 @@ import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
|
|||
import { CoreError } from '@classes/errors/error';
|
||||
import { makeSingleton, Translate } from '@singletons';
|
||||
import { CoreWSExternalWarning } from '@services/ws';
|
||||
import { CoreCourseBase } from '@/types/global';
|
||||
|
||||
const ROOT_CACHE_KEY = 'mmGroups:';
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ import { CoreConstants } from '@/core/constants';
|
|||
import { LangChangeEvent } from '@ngx-translate/core';
|
||||
import { CoreAppProvider } from '@services/app';
|
||||
import { CoreConfig } from '@services/config';
|
||||
import { CoreSubscriptions } from '@singletons/subscriptions';
|
||||
import { makeSingleton, Translate, Platform } from '@singletons';
|
||||
|
||||
import * as moment from 'moment';
|
||||
|
@ -128,44 +129,25 @@ export class CoreLangProvider {
|
|||
|
||||
// Change the language, resolving the promise when we receive the first value.
|
||||
promises.push(new Promise((resolve, reject) => {
|
||||
const subscription = Translate.instance.use(language).subscribe((data) => {
|
||||
CoreSubscriptions.once(Translate.instance.use(language), data => {
|
||||
// It's a language override, load the original one first.
|
||||
const fallbackLang = Translate.instance.instant('core.parentlanguage');
|
||||
|
||||
if (fallbackLang != '' && fallbackLang != 'core.parentlanguage' && fallbackLang != language) {
|
||||
const fallbackSubs = Translate.instance.use(fallbackLang).subscribe((fallbackData) => {
|
||||
data = Object.assign(fallbackData, data);
|
||||
resolve(data);
|
||||
CoreSubscriptions.once(
|
||||
Translate.instance.use(fallbackLang),
|
||||
fallbackData => {
|
||||
data = Object.assign(fallbackData, data);
|
||||
|
||||
// Data received, unsubscribe. Use a timeout because we can receive a value immediately.
|
||||
setTimeout(() => {
|
||||
fallbackSubs.unsubscribe();
|
||||
});
|
||||
}, () => {
|
||||
resolve(data);
|
||||
},
|
||||
// Resolve with the original language.
|
||||
resolve(data);
|
||||
|
||||
// Error received, unsubscribe. Use a timeout because we can receive a value immediately.
|
||||
setTimeout(() => {
|
||||
fallbackSubs.unsubscribe();
|
||||
});
|
||||
});
|
||||
() => resolve(data),
|
||||
);
|
||||
} else {
|
||||
resolve(data);
|
||||
}
|
||||
|
||||
// Data received, unsubscribe. Use a timeout because we can receive a value immediately.
|
||||
setTimeout(() => {
|
||||
subscription.unsubscribe();
|
||||
});
|
||||
}, (error) => {
|
||||
reject(error);
|
||||
|
||||
// Error received, unsubscribe. Use a timeout because we can receive a value immediately.
|
||||
setTimeout(() => {
|
||||
subscription.unsubscribe();
|
||||
});
|
||||
});
|
||||
}, reject);
|
||||
}));
|
||||
|
||||
// Change the config.
|
||||
|
|
|
@ -51,7 +51,7 @@ import {
|
|||
import { CoreArray } from '../singletons/array';
|
||||
import { CoreNetworkError } from '@classes/errors/network-error';
|
||||
|
||||
export const CORE_SITE_SCHEMAS = new InjectionToken('CORE_SITE_SCHEMAS');
|
||||
export const CORE_SITE_SCHEMAS = new InjectionToken<CoreSiteSchema[]>('CORE_SITE_SCHEMAS');
|
||||
|
||||
/*
|
||||
* Service to manage and interact with sites.
|
||||
|
@ -1576,7 +1576,7 @@ export class CoreSitesProvider {
|
|||
}
|
||||
|
||||
// Set installed version.
|
||||
await db.insertRecord(SCHEMA_VERSIONS_TABLE_NAME, { name, version: schema.version });
|
||||
await db.insertRecord(SCHEMA_VERSIONS_TABLE_NAME, { name: schema.name, version: schema.version });
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -390,7 +390,7 @@ export class CoreIframeUtilsProvider {
|
|||
return;
|
||||
}
|
||||
|
||||
if (urlParts.protocol && !CoreUrlUtils.instance.isLocalFileUrlScheme(urlParts.protocol)) {
|
||||
if (urlParts.protocol && !CoreUrlUtils.instance.isLocalFileUrlScheme(urlParts.protocol, urlParts.domain || '')) {
|
||||
// Scheme suggests it's an external resource.
|
||||
event && event.preventDefault();
|
||||
|
||||
|
|
|
@ -424,7 +424,7 @@ export class CoreUrlUtilsProvider {
|
|||
isLocalFileUrl(url: string): boolean {
|
||||
const urlParts = CoreUrl.parse(url);
|
||||
|
||||
return this.isLocalFileUrlScheme(urlParts?.protocol || '');
|
||||
return this.isLocalFileUrlScheme(urlParts?.protocol || '', urlParts?.domain || '');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -433,7 +433,7 @@ export class CoreUrlUtilsProvider {
|
|||
* @param scheme Scheme to check.
|
||||
* @return Whether the scheme belongs to a local file.
|
||||
*/
|
||||
isLocalFileUrlScheme(scheme: string): boolean {
|
||||
isLocalFileUrlScheme(scheme: string, domain: string): boolean {
|
||||
if (!scheme) {
|
||||
return false;
|
||||
}
|
||||
|
@ -442,7 +442,8 @@ export class CoreUrlUtilsProvider {
|
|||
return scheme == 'cdvfile' ||
|
||||
scheme == 'file' ||
|
||||
scheme == 'filesystem' ||
|
||||
scheme == CoreConstants.CONFIG.ioswebviewscheme;
|
||||
scheme == CoreConstants.CONFIG.ioswebviewscheme ||
|
||||
(scheme === 'http' && domain === 'localhost');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -927,7 +927,7 @@ export class CoreWSProvider {
|
|||
options.responseType = options.responseType || 'json';
|
||||
options.timeout = typeof options.timeout == 'undefined' ? this.getRequestTimeout() : options.timeout;
|
||||
|
||||
if (CoreApp.instance.isIOS()) {
|
||||
if (CoreApp.instance.isMobile()) {
|
||||
// Use the cordova plugin.
|
||||
if (url.indexOf('file://') === 0) {
|
||||
// We cannot load local files using the http native plugin. Use file provider instead.
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
// (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 { EventEmitter } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
/**
|
||||
* Subscribable object.
|
||||
*/
|
||||
type Subscribable<T> = EventEmitter<T> | Observable<T>;
|
||||
|
||||
/**
|
||||
* Singleton with helpers to work with subscriptions.
|
||||
*/
|
||||
export class CoreSubscriptions {
|
||||
|
||||
/**
|
||||
* Listen once to a subscribable object.
|
||||
*
|
||||
* @param subscribable Subscribable to listen to.
|
||||
* @param onSuccess Callback to run when the subscription is updated.
|
||||
* @param onError Callback to run when the an error happens.
|
||||
*/
|
||||
static once<T>(subscribable: Subscribable<T>, onSuccess: (value: T) => unknown, onError?: (error: unknown) => unknown): void {
|
||||
const subscription = subscribable.subscribe(
|
||||
value => {
|
||||
// Unsubscribe using a timeout because we can receive a value immediately.
|
||||
setTimeout(() => subscription.unsubscribe(), 0);
|
||||
|
||||
onSuccess(value);
|
||||
},
|
||||
error => {
|
||||
// Unsubscribe using a timeout because we can receive a value immediately.
|
||||
setTimeout(() => subscription.unsubscribe(), 0);
|
||||
|
||||
onError?.call(error);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -12,13 +12,9 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
|
||||
import 'jest-preset-angular';
|
||||
|
||||
import { getConfig, getBuild } from '../../config/utils';
|
||||
|
||||
(window as unknown as MoodleAppWindow).MoodleApp = {
|
||||
CONFIG: getConfig('testing'),
|
||||
BUILD: getBuild('testing'),
|
||||
// eslint-disable-next-line no-console
|
||||
console.debug = () => {
|
||||
// Silence.
|
||||
};
|
||||
|
|
|
@ -13,69 +13,13 @@
|
|||
// limitations under the License.
|
||||
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
|
||||
import { CoreColorScheme } from '@features/settings/services/settings-helper';
|
||||
import { CoreSitesDemoSiteData } from '@services/sites';
|
||||
|
||||
declare global {
|
||||
|
||||
interface Window {
|
||||
__Zone_disable_customElements: boolean;
|
||||
}
|
||||
|
||||
type MoodleAppWindow = {
|
||||
MoodleApp: {
|
||||
CONFIG: {
|
||||
app_id: string;
|
||||
appname: string;
|
||||
versioncode: number;
|
||||
versionname: string;
|
||||
cache_update_frequency_usually: number;
|
||||
cache_update_frequency_often: number;
|
||||
cache_update_frequency_sometimes: number;
|
||||
cache_update_frequency_rarely: number;
|
||||
default_lang: string;
|
||||
languages: Record<string, string>;
|
||||
wsservice: string;
|
||||
wsextservice: string;
|
||||
demo_sites: Record<string, CoreSitesDemoSiteData>;
|
||||
font_sizes: number[];
|
||||
customurlscheme: string;
|
||||
siteurl: string;
|
||||
sitename: string;
|
||||
multisitesdisplay: string;
|
||||
sitefindersettings: Record<string, unknown>;
|
||||
onlyallowlistedsites: boolean;
|
||||
skipssoconfirmation: boolean;
|
||||
forcedefaultlanguage: boolean;
|
||||
privacypolicy: string;
|
||||
notificoncolor: string;
|
||||
enableanalytics: boolean;
|
||||
enableonboarding: boolean;
|
||||
forceColorScheme: CoreColorScheme;
|
||||
forceLoginLogo: boolean;
|
||||
ioswebviewscheme: string;
|
||||
appstores: Record<string, string>;
|
||||
displayqroncredentialscreen?: boolean;
|
||||
displayqronsitescreen?: boolean;
|
||||
forceOpenLinksIn: 'app' | 'browser';
|
||||
};
|
||||
|
||||
BUILD: {
|
||||
isProduction: boolean;
|
||||
isTesting: boolean;
|
||||
isDevelopment: boolean;
|
||||
lastCommitHash: string;
|
||||
compilationTime: number;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
interface Window {
|
||||
__Zone_disable_customElements: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Course base definition.
|
||||
*/
|
||||
export type CoreCourseBase = {
|
||||
type CoreCourseBase = {
|
||||
id: number; // Course Id.
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue