commit
5dfccb1291
|
@ -1,4 +1,4 @@
|
||||||
sudo: required
|
os: linux
|
||||||
dist: bionic
|
dist: bionic
|
||||||
group: edge
|
group: edge
|
||||||
|
|
||||||
|
@ -23,4 +23,3 @@ script:
|
||||||
|
|
||||||
after_success:
|
after_success:
|
||||||
- scripts/ci.sh
|
- scripts/ci.sh
|
||||||
|
|
|
@ -2,39 +2,39 @@
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>AD_UNIT_ID_FOR_BANNER_TEST</key>
|
<key>AD_UNIT_ID_FOR_BANNER_TEST</key>
|
||||||
<string>ca-app-pub-3940256099942544/2934735716</string>
|
<string></string>
|
||||||
<key>AD_UNIT_ID_FOR_INTERSTITIAL_TEST</key>
|
<key>AD_UNIT_ID_FOR_INTERSTITIAL_TEST</key>
|
||||||
<string>ca-app-pub-3940256099942544/4411468910</string>
|
<string></string>
|
||||||
<key>CLIENT_ID</key>
|
<key>CLIENT_ID</key>
|
||||||
<string>694767596569-c2cjrca92k99f6nkp3363lsb7ljhdgdr.apps.googleusercontent.com</string>
|
<string></string>
|
||||||
<key>REVERSED_CLIENT_ID</key>
|
<key>REVERSED_CLIENT_ID</key>
|
||||||
<string>com.googleusercontent.apps.694767596569-c2cjrca92k99f6nkp3363lsb7ljhdgdr</string>
|
<string></string>
|
||||||
<key>API_KEY</key>
|
<key>API_KEY</key>
|
||||||
<string>AIzaSyA-77ZjkxII6GV97CC9rdUl83rzdEXu_rM</string>
|
<string></string>
|
||||||
<key>GCM_SENDER_ID</key>
|
<key>GCM_SENDER_ID</key>
|
||||||
<string>694767596569</string>
|
<string></string>
|
||||||
<key>PLIST_VERSION</key>
|
<key>PLIST_VERSION</key>
|
||||||
<string>1</string>
|
<string>1</string>
|
||||||
<key>BUNDLE_ID</key>
|
<key>BUNDLE_ID</key>
|
||||||
<string>com.moodle.moodlemobile</string>
|
<string>com.moodle.moodlemobile</string>
|
||||||
<key>PROJECT_ID</key>
|
<key>PROJECT_ID</key>
|
||||||
<string>moodlemobile-push</string>
|
<string>moodlemobile-push</string>
|
||||||
<key>STORAGE_BUCKET</key>
|
<key>STORAGE_BUCKET</key>
|
||||||
<string>moodlemobile-push.appspot.com</string>
|
<string></string>
|
||||||
<key>IS_ADS_ENABLED</key>
|
<key>IS_ADS_ENABLED</key>
|
||||||
<true></true>
|
<false></false>
|
||||||
<key>IS_ANALYTICS_ENABLED</key>
|
<key>IS_ANALYTICS_ENABLED</key>
|
||||||
<false></false>
|
<false></false>
|
||||||
<key>IS_APPINVITE_ENABLED</key>
|
<key>IS_APPINVITE_ENABLED</key>
|
||||||
<true></true>
|
<false></false>
|
||||||
<key>IS_GCM_ENABLED</key>
|
<key>IS_GCM_ENABLED</key>
|
||||||
<true></true>
|
<false></false>
|
||||||
<key>IS_SIGNIN_ENABLED</key>
|
<key>IS_SIGNIN_ENABLED</key>
|
||||||
<true></true>
|
<false></false>
|
||||||
<key>GOOGLE_APP_ID</key>
|
<key>GOOGLE_APP_ID</key>
|
||||||
<string>1:694767596569:ios:a4cdad4d168c9d1a</string>
|
<string></string>
|
||||||
<key>DATABASE_URL</key>
|
<key>DATABASE_URL</key>
|
||||||
<string>https://moodlemobile-push.firebaseio.com</string>
|
<string></string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
|
@ -0,0 +1,15 @@
|
||||||
|
Package updates known problems
|
||||||
|
=================
|
||||||
|
|
||||||
|
@ionic/app-scripts 3.2.3 shows error Cannot find type definition file for '@types'. on ngc build.
|
||||||
|
|
||||||
|
com-darryncampbell-cordova-plugin-intent 2.0.0 onwards needs Android X Support. Unsupported on PGB.
|
||||||
|
|
||||||
|
typescript is needed to be less than 2.7 for @angular/compiler-cli
|
||||||
|
|
||||||
|
cordova-plugin-ionic-keyboard has problems on greater versions than 2.1.3
|
||||||
|
|
||||||
|
jszip has problems with "lie" dependency on greater versions than 3.1
|
||||||
|
|
||||||
|
promise.prototype.finally has problems on greater versions than 3.1
|
||||||
|
|
10
config.xml
10
config.xml
|
@ -1,5 +1,5 @@
|
||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<widget id="com.moodle.moodlemobile" version="3.8.0" xmlns="http://www.w3.org/ns/widgets" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:cdv="http://cordova.apache.org/ns/1.0">
|
<widget id="com.moodle.moodlemobile" version="3.8.1" xmlns="http://www.w3.org/ns/widgets" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:cdv="http://cordova.apache.org/ns/1.0">
|
||||||
<name>Moodle</name>
|
<name>Moodle</name>
|
||||||
<description>Moodle official app</description>
|
<description>Moodle official app</description>
|
||||||
<author email="mobile@moodle.com" href="http://moodle.com">Moodle Mobile team</author>
|
<author email="mobile@moodle.com" href="http://moodle.com">Moodle Mobile team</author>
|
||||||
|
@ -23,7 +23,7 @@
|
||||||
<preference name="webviewbounce" value="false" />
|
<preference name="webviewbounce" value="false" />
|
||||||
<preference name="AppendUserAgent" value="MoodleMobile" />
|
<preference name="AppendUserAgent" value="MoodleMobile" />
|
||||||
<preference name="android-minSdkVersion" value="19" />
|
<preference name="android-minSdkVersion" value="19" />
|
||||||
<preference name="android-targetSdkVersion" value="28" />
|
<preference name="android-targetSdkVersion" value="29" />
|
||||||
<preference name="UIWebViewBounce" value="false" />
|
<preference name="UIWebViewBounce" value="false" />
|
||||||
<preference name="DisallowOverscroll" value="true" />
|
<preference name="DisallowOverscroll" value="true" />
|
||||||
<preference name="BackupWebStorage" value="none" />
|
<preference name="BackupWebStorage" value="none" />
|
||||||
|
@ -122,6 +122,9 @@
|
||||||
<edit-config file="*-Info.plist" mode="merge" target="NSLocationWhenInUseUsageDescription">
|
<edit-config file="*-Info.plist" mode="merge" target="NSLocationWhenInUseUsageDescription">
|
||||||
<string>We need your location so you can attach it as part of your submissions.</string>
|
<string>We need your location so you can attach it as part of your submissions.</string>
|
||||||
</edit-config>
|
</edit-config>
|
||||||
|
<edit-config file="*-Info.plist" mode="merge" target="NSLocationAlwaysUsageDescription">
|
||||||
|
<string>We need your location so you can attach it as part of your submissions.</string>
|
||||||
|
</edit-config>
|
||||||
<icon height="20" src="resources/ios/icon/icon-20.png" width="20" />
|
<icon height="20" src="resources/ios/icon/icon-20.png" width="20" />
|
||||||
<icon height="40" src="resources/ios/icon/icon-20@2x.png" width="40" />
|
<icon height="40" src="resources/ios/icon/icon-20@2x.png" width="40" />
|
||||||
<icon height="60" src="resources/ios/icon/icon-20@3x.png" width="60" />
|
<icon height="60" src="resources/ios/icon/icon-20@3x.png" width="60" />
|
||||||
|
@ -181,9 +184,6 @@
|
||||||
<edit-config file="AndroidManifest.xml" mode="merge" target="/manifest/application">
|
<edit-config file="AndroidManifest.xml" mode="merge" target="/manifest/application">
|
||||||
<application android:usesCleartextTraffic="true" />
|
<application android:usesCleartextTraffic="true" />
|
||||||
</edit-config>
|
</edit-config>
|
||||||
<edit-config file="AndroidManifest.xml" mode="overwrite" target="/manifest/uses-feature[@android:name='android.hardware.location.gps']">
|
|
||||||
<uses-feature android:name="android.hardware.location.gps" android:required="false" />
|
|
||||||
</edit-config>
|
|
||||||
<config-file parent="/manifest/application" target="AndroidManifest.xml">
|
<config-file parent="/manifest/application" target="AndroidManifest.xml">
|
||||||
<meta-data android:name="firebase_analytics_collection_deactivated" android:value="true" />
|
<meta-data android:name="firebase_analytics_collection_deactivated" android:value="true" />
|
||||||
</config-file>
|
</config-file>
|
||||||
|
|
|
@ -5,5 +5,11 @@ module.exports = {
|
||||||
'node_modules/ionicons/dist/scss',
|
'node_modules/ionicons/dist/scss',
|
||||||
'node_modules/ionic-angular/fonts',
|
'node_modules/ionic-angular/fonts',
|
||||||
'node_modules/font-awesome/scss'
|
'node_modules/font-awesome/scss'
|
||||||
|
],
|
||||||
|
includeFiles: [
|
||||||
|
/\.(s(c|a)ss)$/i
|
||||||
|
],
|
||||||
|
excludeFiles: [
|
||||||
|
/\.(wp).(scss)$/i
|
||||||
]
|
]
|
||||||
};
|
};
|
|
@ -0,0 +1,19 @@
|
||||||
|
// Check https://github.com/mishoo/UglifyJS2/tree/harmony#minify-options-structure
|
||||||
|
module.exports = {
|
||||||
|
/**
|
||||||
|
* mangle: uglify 2's mangle option
|
||||||
|
*/
|
||||||
|
mangle: {
|
||||||
|
keep_classnames: true,
|
||||||
|
keep_fnames: true
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* compress: uglify 2's compress option
|
||||||
|
*/
|
||||||
|
compress: {
|
||||||
|
toplevel: true,
|
||||||
|
pure_getters: true
|
||||||
|
},
|
||||||
|
keep_classnames: true,
|
||||||
|
keep_fnames: true
|
||||||
|
}
|
|
@ -6,7 +6,7 @@
|
||||||
<Identity Name="3312ADB7.MoodleDesktop"
|
<Identity Name="3312ADB7.MoodleDesktop"
|
||||||
ProcessorArchitecture="x64"
|
ProcessorArchitecture="x64"
|
||||||
Publisher="CN=33CDCDF6-1EB5-4827-9897-ED25C91A32F6"
|
Publisher="CN=33CDCDF6-1EB5-4827-9897-ED25C91A32F6"
|
||||||
Version="3.8.0.0" />
|
Version="3.8.1.0" />
|
||||||
<Properties>
|
<Properties>
|
||||||
<DisplayName>Moodle Desktop</DisplayName>
|
<DisplayName>Moodle Desktop</DisplayName>
|
||||||
<PublisherDisplayName>Moodle Pty Ltd.</PublisherDisplayName>
|
<PublisherDisplayName>Moodle Pty Ltd.</PublisherDisplayName>
|
||||||
|
|
|
@ -1,30 +1,30 @@
|
||||||
{
|
{
|
||||||
"project_info": {
|
"project_info": {
|
||||||
"project_number": "694767596569",
|
"project_number": "",
|
||||||
"firebase_url": "https://moodlemobile-push.firebaseio.com",
|
"firebase_url": "",
|
||||||
"project_id": "moodlemobile-push",
|
"project_id": "",
|
||||||
"storage_bucket": "moodlemobile-push.appspot.com"
|
"storage_bucket": ""
|
||||||
},
|
},
|
||||||
"client": [
|
"client": [
|
||||||
{
|
{
|
||||||
"client_info": {
|
"client_info": {
|
||||||
"mobilesdk_app_id": "1:694767596569:android:a4cdad4d168c9d1a",
|
"mobilesdk_app_id": "1:111111111111:android:1111111111111111",
|
||||||
"android_client_info": {
|
"android_client_info": {
|
||||||
"package_name": "com.moodle.moodlemobile"
|
"package_name": "com.moodle.moodlemobile"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"oauth_client": [
|
"oauth_client": [
|
||||||
{
|
{
|
||||||
"client_id": "694767596569-icveqqa2n56oh44l6ev1dr2oh67nh8il.apps.googleusercontent.com",
|
"client_id": "",
|
||||||
"client_type": 3
|
"client_type": 3
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"api_key": [
|
"api_key": [
|
||||||
{
|
{
|
||||||
"current_key": "AIzaSyCb2zogu0P_aZ2LNgdwzshWExITPKTXJyk"
|
"current_key": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"current_key": "AIzaSyDRT1HwT0gSsTty0whOVtoNKAh8SPrJXLE"
|
"current_key": ""
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"services": {
|
"services": {
|
||||||
|
|
17
gulpfile.js
17
gulpfile.js
|
@ -5,12 +5,12 @@ var gulp = require('gulp'),
|
||||||
path = require('path'),
|
path = require('path'),
|
||||||
slash = require('gulp-slash'),
|
slash = require('gulp-slash'),
|
||||||
clipEmptyFiles = require('gulp-clip-empty-files'),
|
clipEmptyFiles = require('gulp-clip-empty-files'),
|
||||||
gutil = require('gulp-util'),
|
File = require('vinyl'),
|
||||||
flatten = require('gulp-flatten'),
|
flatten = require('gulp-flatten'),
|
||||||
npmPath = require('path'),
|
npmPath = require('path'),
|
||||||
concat = require('gulp-concat'),
|
concat = require('gulp-concat'),
|
||||||
bufferFrom = require('buffer-from')
|
htmlmin = require('gulp-htmlmin'),
|
||||||
File = gutil.File,
|
bufferFrom = require('buffer-from'),
|
||||||
exec = require('child_process').exec,
|
exec = require('child_process').exec,
|
||||||
license = '' +
|
license = '' +
|
||||||
'// (C) Copyright 2015 Moodle Pty Ltd.\n' +
|
'// (C) Copyright 2015 Moodle Pty Ltd.\n' +
|
||||||
|
@ -294,6 +294,12 @@ gulp.task('copy-component-templates', function(done) {
|
||||||
|
|
||||||
gulp.src(templatesSrc, { allowEmpty: true })
|
gulp.src(templatesSrc, { allowEmpty: true })
|
||||||
.pipe(flatten())
|
.pipe(flatten())
|
||||||
|
// Check options here: https://github.com/kangax/html-minifier
|
||||||
|
.pipe(htmlmin({
|
||||||
|
collapseWhitespace: true,
|
||||||
|
removeComments: true,
|
||||||
|
caseSensitive: true
|
||||||
|
}))
|
||||||
.pipe(gulp.dest(templatesDest))
|
.pipe(gulp.dest(templatesDest))
|
||||||
.on('end', done);
|
.on('end', done);
|
||||||
});
|
});
|
||||||
|
@ -311,6 +317,11 @@ function getReplace(capture, baseDir, paths, parsedFiles) {
|
||||||
var parse = path.parse(path.resolve(baseDir, capture + '.scss'));
|
var parse = path.parse(path.resolve(baseDir, capture + '.scss'));
|
||||||
var file = parse.dir + '/' + parse.name;
|
var file = parse.dir + '/' + parse.name;
|
||||||
|
|
||||||
|
if (file.slice(-3) === '.wp') {
|
||||||
|
console.log('Windows Phone not supported "' + capture);
|
||||||
|
// File was already parsed, leave the import commented.
|
||||||
|
return '// @import "' + capture + '";';
|
||||||
|
}
|
||||||
|
|
||||||
if (!fs.existsSync(file + '.scss')) {
|
if (!fs.existsSync(file + '.scss')) {
|
||||||
// File not found, might be a partial file.
|
// File not found, might be a partial file.
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
119
package.json
119
package.json
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "moodlemobile",
|
"name": "moodlemobile",
|
||||||
"version": "3.8.0",
|
"version": "3.8.1",
|
||||||
"description": "The official app for Moodle.",
|
"description": "The official app for Moodle.",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Moodle Pty Ltd.",
|
"name": "Moodle Pty Ltd.",
|
||||||
|
@ -9,6 +9,7 @@
|
||||||
"config": {
|
"config": {
|
||||||
"ionic_webpack": "./config/webpack.config.js",
|
"ionic_webpack": "./config/webpack.config.js",
|
||||||
"ionic_copy": "./config/copy.config.js",
|
"ionic_copy": "./config/copy.config.js",
|
||||||
|
"ionic_uglifyjs": "./config/uglifyjs.config.js",
|
||||||
"ionic_sass": "./config/sass.config.js"
|
"ionic_sass": "./config/sass.config.js"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -40,63 +41,56 @@
|
||||||
"windows.store": "npx electron-windows-store --input-directory .\\desktop\\dist\\win-unpacked --output-directory .\\desktop\\store -a .\\resources\\desktop -m .\\desktop\\assets\\windows\\AppXManifest.xml --package-version 0.0.0.0 --package-name MoodleDesktop"
|
"windows.store": "npx electron-windows-store --input-directory .\\desktop\\dist\\win-unpacked --output-directory .\\desktop\\store -a .\\resources\\desktop -m .\\desktop\\assets\\windows\\AppXManifest.xml --package-version 0.0.0.0 --package-name MoodleDesktop"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "5.2.10",
|
"@angular/animations": "5.2.11",
|
||||||
"@angular/common": "5.2.10",
|
"@angular/common": "5.2.11",
|
||||||
"@angular/compiler": "5.2.10",
|
"@angular/compiler": "5.2.11",
|
||||||
"@angular/compiler-cli": "5.2.10",
|
"@angular/compiler-cli": "5.2.11",
|
||||||
"@angular/core": "5.2.10",
|
"@angular/core": "5.2.11",
|
||||||
"@angular/forms": "5.2.10",
|
"@angular/forms": "5.2.11",
|
||||||
"@angular/http": "5.2.10",
|
"@angular/platform-browser": "5.2.11",
|
||||||
"@angular/platform-browser": "5.2.10",
|
"@angular/platform-browser-dynamic": "5.2.11",
|
||||||
"@angular/platform-browser-dynamic": "5.2.10",
|
"@ionic-native/badge": "4.20.0",
|
||||||
"@ionic-native/badge": "4.17.0",
|
"@ionic-native/camera": "4.20.0",
|
||||||
"@ionic-native/camera": "4.17.0",
|
"@ionic-native/clipboard": "4.20.0",
|
||||||
"@ionic-native/clipboard": "4.17.0",
|
"@ionic-native/core": "4.20.0",
|
||||||
"@ionic-native/core": "4.11.0",
|
"@ionic-native/device": "4.20.0",
|
||||||
"@ionic-native/device": "4.17.0",
|
"@ionic-native/file": "4.20.0",
|
||||||
"@ionic-native/file": "4.17.0",
|
"@ionic-native/file-opener": "4.20.0",
|
||||||
"@ionic-native/file-opener": "4.17.0",
|
"@ionic-native/file-transfer": "4.20.0",
|
||||||
"@ionic-native/file-transfer": "4.17.0",
|
"@ionic-native/geolocation": "4.20.0",
|
||||||
"@ionic-native/geolocation": "4.17.0",
|
"@ionic-native/globalization": "4.20.0",
|
||||||
"@ionic-native/globalization": "4.17.0",
|
"@ionic-native/in-app-browser": "4.20.0",
|
||||||
"@ionic-native/in-app-browser": "4.17.0",
|
"@ionic-native/keyboard": "4.20.0",
|
||||||
"@ionic-native/keyboard": "4.17.0",
|
"@ionic-native/local-notifications": "4.20.0",
|
||||||
"@ionic-native/local-notifications": "4.17.0",
|
"@ionic-native/media-capture": "4.20.0",
|
||||||
"@ionic-native/media-capture": "4.17.0",
|
"@ionic-native/network": "4.20.0",
|
||||||
"@ionic-native/network": "4.17.0",
|
"@ionic-native/push": "4.20.0",
|
||||||
"@ionic-native/push": "4.17.0",
|
"@ionic-native/screen-orientation": "4.20.0",
|
||||||
"@ionic-native/screen-orientation": "4.17.0",
|
"@ionic-native/splash-screen": "4.20.0",
|
||||||
"@ionic-native/splash-screen": "4.17.0",
|
"@ionic-native/sqlite": "4.20.0",
|
||||||
"@ionic-native/sqlite": "4.17.0",
|
"@ionic-native/status-bar": "4.20.0",
|
||||||
"@ionic-native/status-bar": "4.17.0",
|
"@ionic-native/web-intent": "4.20.0",
|
||||||
"@ionic-native/web-intent": "4.17.0",
|
"@ionic-native/zip": "4.20.0",
|
||||||
"@ionic-native/zip": "4.17.0",
|
|
||||||
"@ngx-translate/core": "8.0.0",
|
"@ngx-translate/core": "8.0.0",
|
||||||
"@ngx-translate/http-loader": "2.0.1",
|
"@ngx-translate/http-loader": "2.0.1",
|
||||||
"@types/cordova": "0.0.34",
|
"ajv": "6.11.0",
|
||||||
"@types/cordova-plugin-file-transfer": "0.0.3",
|
"chart.js": "2.9.3",
|
||||||
"@types/cordova-plugin-globalization": "0.0.3",
|
|
||||||
"@types/cordova-plugin-network-information": "0.0.3",
|
|
||||||
"@types/node": "8.10.19",
|
|
||||||
"@types/promise.prototype.finally": "2.0.2",
|
|
||||||
"ajv": "6.10.2",
|
|
||||||
"chart.js": "2.7.2",
|
|
||||||
"com-darryncampbell-cordova-plugin-intent": "1.3.0",
|
"com-darryncampbell-cordova-plugin-intent": "1.3.0",
|
||||||
"cordova": "9.0.0",
|
"cordova": "9.0.0",
|
||||||
"cordova-android": "8.0.0",
|
"cordova-android": "8.1.0",
|
||||||
"cordova-android-support-gradle-release": "3.0.1",
|
"cordova-android-support-gradle-release": "3.0.1",
|
||||||
"cordova-clipboard": "1.3.0",
|
"cordova-clipboard": "1.3.0",
|
||||||
"cordova-ios": "5.0.1",
|
"cordova-ios": "5.1.1",
|
||||||
"cordova-plugin-badge": "0.8.8",
|
"cordova-plugin-badge": "0.8.8",
|
||||||
"cordova-plugin-camera": "4.1.0",
|
"cordova-plugin-camera": "4.1.0",
|
||||||
"cordova-plugin-customurlscheme": "4.4.0",
|
"cordova-plugin-customurlscheme": "5.0.0",
|
||||||
"cordova-plugin-device": "2.0.3",
|
"cordova-plugin-device": "2.0.3",
|
||||||
"cordova-plugin-file": "6.0.2",
|
"cordova-plugin-file": "6.0.2",
|
||||||
"cordova-plugin-file-opener2": "2.2.1",
|
"cordova-plugin-file-opener2": "3.0.0",
|
||||||
"cordova-plugin-file-transfer": "1.7.1",
|
"cordova-plugin-file-transfer": "1.7.1",
|
||||||
"cordova-plugin-geolocation": "4.0.2",
|
"cordova-plugin-geolocation": "4.0.2",
|
||||||
"cordova-plugin-globalization": "1.11.0",
|
"cordova-plugin-globalization": "1.11.0",
|
||||||
"cordova-plugin-inappbrowser": "3.1.0",
|
"cordova-plugin-inappbrowser": "3.2.0",
|
||||||
"cordova-plugin-ionic-keyboard": "2.1.3",
|
"cordova-plugin-ionic-keyboard": "2.1.3",
|
||||||
"cordova-plugin-local-notification": "git+https://github.com/moodlemobile/cordova-plugin-local-notification.git#moodle",
|
"cordova-plugin-local-notification": "git+https://github.com/moodlemobile/cordova-plugin-local-notification.git#moodle",
|
||||||
"cordova-plugin-media-capture": "3.0.3",
|
"cordova-plugin-media-capture": "3.0.3",
|
||||||
|
@ -106,41 +100,48 @@
|
||||||
"cordova-plugin-statusbar": "2.4.3",
|
"cordova-plugin-statusbar": "2.4.3",
|
||||||
"cordova-plugin-whitelist": "1.3.4",
|
"cordova-plugin-whitelist": "1.3.4",
|
||||||
"cordova-plugin-zip": "3.1.0",
|
"cordova-plugin-zip": "3.1.0",
|
||||||
"cordova-sqlite-storage": "3.4.0",
|
"cordova-sqlite-storage": "4.0.0",
|
||||||
"cordova-support-google-services": "1.2.1",
|
"cordova-support-google-services": "1.3.2",
|
||||||
"es6-promise-plugin": "4.2.2",
|
"es6-promise-plugin": "4.2.2",
|
||||||
"font-awesome": "4.7.0",
|
"font-awesome": "4.7.0",
|
||||||
"ionic-angular": "3.9.3",
|
"ionic-angular": "3.9.9",
|
||||||
"ionicons": "3.0.0",
|
"ionicons": "3.0.0",
|
||||||
"jszip": "3.1.5",
|
"jszip": "3.1.5",
|
||||||
"mathjax": "2.7.7",
|
"mathjax": "2.7.7",
|
||||||
"moment": "2.22.2",
|
"moment": "2.24.0",
|
||||||
"nl.kingsquare.cordova.background-audio": "1.0.1",
|
"nl.kingsquare.cordova.background-audio": "1.0.1",
|
||||||
"phonegap-plugin-multidex": "1.0.0",
|
"phonegap-plugin-multidex": "1.0.0",
|
||||||
"phonegap-plugin-push": "git+https://github.com/moodlemobile/phonegap-plugin-push.git#moodle-v3",
|
"phonegap-plugin-push": "git+https://github.com/moodlemobile/phonegap-plugin-push.git#moodle-v3",
|
||||||
"promise.prototype.finally": "3.1.0",
|
"promise.prototype.finally": "3.1.0",
|
||||||
"rxjs": "5.5.11",
|
"rxjs": "5.5.12",
|
||||||
"sw-toolbox": "3.6.0",
|
"sw-toolbox": "3.6.0",
|
||||||
"ts-md5": "1.2.4",
|
"ts-md5": "1.2.7",
|
||||||
"web-animations-js": "2.3.1",
|
"web-animations-js": "2.3.2",
|
||||||
"zone.js": "0.8.26"
|
"zone.js": "0.8.29"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@ionic/app-scripts": "3.2.2",
|
"@ionic/app-scripts": "3.2.3",
|
||||||
|
"@types/cordova": "0.0.34",
|
||||||
|
"@types/cordova-plugin-file-transfer": "0.0.3",
|
||||||
|
"@types/cordova-plugin-globalization": "0.0.3",
|
||||||
|
"@types/cordova-plugin-network-information": "0.0.3",
|
||||||
|
"@types/node": "8.10.59",
|
||||||
|
"@types/promise.prototype.finally": "2.0.4",
|
||||||
"electron-builder-lib": "20.23.1",
|
"electron-builder-lib": "20.23.1",
|
||||||
"electron-rebuild": "1.8.1",
|
"electron-rebuild": "1.10.0",
|
||||||
"gulp": "4.0.2",
|
"gulp": "4.0.2",
|
||||||
"gulp-clip-empty-files": "0.1.2",
|
"gulp-clip-empty-files": "0.1.2",
|
||||||
"gulp-concat": "2.6.1",
|
"gulp-concat": "2.6.1",
|
||||||
"gulp-flatten": "0.4.0",
|
"gulp-flatten": "0.4.0",
|
||||||
"gulp-rename": "1.3.0",
|
"gulp-htmlmin": "5.0.1",
|
||||||
|
"gulp-rename": "2.0.0",
|
||||||
"gulp-slash": "1.1.3",
|
"gulp-slash": "1.1.3",
|
||||||
"gulp-util": "3.0.8",
|
|
||||||
"lodash.template": "4.5.0",
|
"lodash.template": "4.5.0",
|
||||||
"node-loader": "0.6.0",
|
"node-loader": "0.6.0",
|
||||||
"through": "2.3.8",
|
"through": "2.3.8",
|
||||||
"typescript": "2.6.2",
|
"typescript": "2.6.2",
|
||||||
"webpack-merge": "4.1.2"
|
"vinyl": "2.2.0",
|
||||||
|
"webpack-merge": "4.2.2"
|
||||||
},
|
},
|
||||||
"browser": {
|
"browser": {
|
||||||
"electron": false
|
"electron": false
|
||||||
|
@ -224,7 +225,7 @@
|
||||||
"category": "public.app-category.education",
|
"category": "public.app-category.education",
|
||||||
"icon": "resources/desktop/icon.icns",
|
"icon": "resources/desktop/icon.icns",
|
||||||
"target": "mas",
|
"target": "mas",
|
||||||
"bundleVersion": "3.8.0",
|
"bundleVersion": "3.8.1",
|
||||||
"extendInfo": {
|
"extendInfo": {
|
||||||
"ElectronTeamID": "2NU57U5PAW"
|
"ElectronTeamID": "2NU57U5PAW"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,47 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
source "scripts/functions.sh"
|
|
||||||
|
|
||||||
print_title "NPM packages list"
|
|
||||||
|
|
||||||
# List first level of installed libraries so we can check the installed versions.
|
|
||||||
npm list --depth=0
|
|
||||||
|
|
||||||
if [ "$TRAVIS_BRANCH" == 'master' ] && [ ! -z $GIT_TOKEN ] && [ "$TRAVIS_REPO_SLUG" == 'moodlehq/moodleapp' ]; then
|
|
||||||
print_title "Update langpacks"
|
|
||||||
cd scripts
|
|
||||||
./update_lang.sh
|
|
||||||
cd ..
|
|
||||||
|
|
||||||
print_title "Update generated lang files"
|
|
||||||
git remote set-url origin https://$GIT_TOKEN@github.com/$TRAVIS_REPO_SLUG.git
|
|
||||||
git fetch -q origin
|
|
||||||
git add -A src/assets/lang
|
|
||||||
git add */en.json
|
|
||||||
git add src/config.json
|
|
||||||
git commit -m 'Update lang files [ci skip]'
|
|
||||||
git push origin HEAD:$TRAVIS_BRANCH
|
|
||||||
fi
|
|
||||||
|
|
||||||
print_title "AOT Compilation"
|
|
||||||
sed -ie $'s~throw new Error("No ResourceLoader.*~url = "templates/" + url;\\\nvar resolve;\\\nvar reject;\\\nvar promise = new Promise(function (res, rej) {\\\nresolve = res;\\\nreject = rej;\\\n});\\\nvar xhr = new XMLHttpRequest();\\\nxhr.open("GET", url, true);\\\nxhr.responseType = "text";\\\nxhr.onload = function () {\\\nvar response = xhr.response || xhr.responseText;\\\nvar status = xhr.status === 1223 ? 204 : xhr.status;\\\nif (status === 0) {\\\nstatus = response ? 200 : 0;\\\n}\\\nif (200 <= status \&\& status <= 300) {\\\nresolve(response);\\\n}\\\nelse {\\\nreject("Failed to load " + url);\\\n}\\\n};\\\nxhr.onerror = function () { reject("Failed to load " + url); };\\\nxhr.send();\\\nreturn promise;\\\n~g' node_modules/@angular/platform-browser-dynamic/esm5/platform-browser-dynamic.js
|
|
||||||
sed -ie "s/context\.isProd || hasArg('--minifyJs')/hasArg('--minifyJs')/g" node_modules/@ionic/app-scripts/dist/util/config.js
|
|
||||||
sed -ie "s/context\.isProd || hasArg('--optimizeJs')/hasArg('--optimizeJs')/g" node_modules/@ionic/app-scripts/dist/util/config.js
|
|
||||||
npm run ionic:build -- --prod
|
|
||||||
|
|
||||||
|
|
||||||
if [ $TRAVIS_BRANCH == 'integration' ] || [ $TRAVIS_BRANCH == 'master' ] || [ $TRAVIS_BRANCH == 'desktop' ] ; then
|
|
||||||
if [ ! -z $GIT_ORG_PRIVATE ] && [ ! -z $GIT_TOKEN ] ; then
|
|
||||||
if [ "$TRAVIS_REPO_SLUG" == 'moodlehq/moodleapp' ]; then
|
|
||||||
print_title "Mirror repository"
|
|
||||||
git remote add mirror https://$GIT_TOKEN@github.com/$GIT_ORG_PRIVATE/moodleapp.git
|
|
||||||
git fetch -q mirror
|
|
||||||
git push -f mirror HEAD:$TRAVIS_BRANCH
|
|
||||||
git push mirror --tags
|
|
||||||
else
|
|
||||||
print_title "Run scripts"
|
|
||||||
git clone --depth 1 https://$GIT_TOKEN@github.com/$GIT_ORG_PRIVATE/apps-scripts.git ../scripts
|
|
||||||
cp ../scripts/build.sh scripts/
|
|
||||||
./scripts/build.sh
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
|
@ -1,8 +1,81 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
source "scripts/functions.sh"
|
||||||
|
|
||||||
if [ $TRAVIS_EVENT_TYPE == 'cron' ] ; then
|
if [ "$TRAVIS_EVENT_TYPE" == 'cron' ] ; then
|
||||||
# Tests scripts.
|
# Tests scripts.
|
||||||
echo 'CRON NOT IMPLEMENTED YET'
|
print_error 'CRON NOT IMPLEMENTED YET'
|
||||||
else
|
else
|
||||||
./scripts/aot.sh
|
if [ -z $GIT_ORG_PRIVATE ] || [ -z $GIT_TOKEN ]; then
|
||||||
|
print_error "Env vars not correctly defined"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# List first level of installed libraries so we can check the installed versions.
|
||||||
|
print_title "NPM packages list"
|
||||||
|
npm list --depth=0
|
||||||
|
|
||||||
|
if [ "$TRAVIS_REPO_SLUG" == 'moodlehq/moodleapp' ]; then
|
||||||
|
if [ "$TRAVIS_BRANCH" == 'master' ]; then
|
||||||
|
print_title "Update langpacks"
|
||||||
|
cd scripts
|
||||||
|
./update_lang.sh
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
print_title "Update generated lang files"
|
||||||
|
git remote set-url origin https://$GIT_TOKEN@github.com/$TRAVIS_REPO_SLUG.git
|
||||||
|
git fetch -q origin
|
||||||
|
git add -A src/assets/lang
|
||||||
|
git add */en.json
|
||||||
|
git add src/config.json
|
||||||
|
git commit -m 'Update lang files [ci skip]'
|
||||||
|
|
||||||
|
print_title "Update Licenses"
|
||||||
|
npm install -g license-checker
|
||||||
|
|
||||||
|
jq --version
|
||||||
|
license-checker --json --production --relativeLicensePath > licenses.json
|
||||||
|
jq 'del(.[].path)' licenses.json > licenses_old.json
|
||||||
|
mv licenses_old.json licenses.json
|
||||||
|
licenses=`jq -r 'keys[]' licenses.json`
|
||||||
|
echo "{" > licensesurl.json
|
||||||
|
first=1
|
||||||
|
for license in $licenses; do
|
||||||
|
obj=`jq --arg lic $license '.[$lic]' licenses.json`
|
||||||
|
licensePath=`echo $obj | jq -r '.licenseFile'`
|
||||||
|
file=""
|
||||||
|
if [[ ! -z "$licensePath" ]] || [[ "$licensePath" != "null" ]]; then
|
||||||
|
file=$(basename $licensePath)
|
||||||
|
if [ $first -eq 1 ] ; then
|
||||||
|
first=0
|
||||||
|
echo "\"$license\" : { \"licenseFile\" : \"$file\"}" >> licensesurl.json
|
||||||
|
else
|
||||||
|
echo ",\"$license\" : { \"licenseFile\" : \"$file\"}" >> licensesurl.json
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo "}" >> licensesurl.json
|
||||||
|
|
||||||
|
jq -s '.[0] * .[1]' licenses.json licensesurl.json > licenses_old.json
|
||||||
|
mv licenses_old.json licenses.json
|
||||||
|
rm licensesurl.json
|
||||||
|
|
||||||
|
git add licenses.json
|
||||||
|
git commit -m 'Update licenses [ci skip]'
|
||||||
|
|
||||||
|
git push origin HEAD:$TRAVIS_BRANCH
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$TRAVIS_BRANCH" == 'integration' ] || [ "$TRAVIS_BRANCH" == 'master' ] || [ "$TRAVIS_BRANCH" == 'desktop' ] ; then
|
||||||
|
print_title "Mirror repository"
|
||||||
|
git remote add mirror https://$GIT_TOKEN@github.com/$GIT_ORG_PRIVATE/moodleapp.git
|
||||||
|
git fetch -q --unshallow mirror
|
||||||
|
git push -f mirror HEAD:$TRAVIS_BRANCH
|
||||||
|
git push mirror --tags
|
||||||
|
fi
|
||||||
|
elif [ "$TRAVIS_REPO_SLUG" == "$GIT_ORG_PRIVATE/moodleapp" ]; then
|
||||||
|
print_title "Run scripts"
|
||||||
|
git clone --depth 1 https://$GIT_TOKEN@github.com/$GIT_ORG_PRIVATE/apps-scripts.git ../scripts
|
||||||
|
cp ../scripts/build.sh scripts/
|
||||||
|
./scripts/build.sh
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -3,8 +3,8 @@ source "functions.sh"
|
||||||
|
|
||||||
#Saves or updates a key on langindex_old.json
|
#Saves or updates a key on langindex_old.json
|
||||||
function save_key {
|
function save_key {
|
||||||
key=$1
|
local key=$1
|
||||||
found=$2
|
local found=$2
|
||||||
|
|
||||||
print_ok "$key=$found"
|
print_ok "$key=$found"
|
||||||
echo "{\"$key\": \"$found\"}" > langindex_old.json
|
echo "{\"$key\": \"$found\"}" > langindex_old.json
|
||||||
|
@ -14,19 +14,17 @@ function save_key {
|
||||||
|
|
||||||
#Removes a key on langindex_old.json
|
#Removes a key on langindex_old.json
|
||||||
function remove_key {
|
function remove_key {
|
||||||
key=$1
|
local key=$1
|
||||||
found=$2
|
|
||||||
|
|
||||||
print_ok "$key=$found"
|
cat langindex.json | jq 'del(."'$key'")' > langindex_new.json
|
||||||
echo "{\"$key\": \"$found\"}" > langindex_old.json
|
|
||||||
jq -s '.[0] - .[1]' langindex.json langindex_old.json > langindex_new.json
|
|
||||||
mv langindex_new.json langindex.json
|
mv langindex_new.json langindex.json
|
||||||
|
print_ok "Deleted unused key $key"
|
||||||
}
|
}
|
||||||
|
|
||||||
#Check if and i exists in php file
|
#Check if and i exists in php file
|
||||||
function exists_in_file {
|
function exists_in_file {
|
||||||
file=$1
|
local file=$1
|
||||||
id=$2
|
local id=$2
|
||||||
|
|
||||||
file=`echo $file | sed s/^mod_workshop_assessment/workshopform/1`
|
file=`echo $file | sed s/^mod_workshop_assessment/workshopform/1`
|
||||||
file=`echo $file | sed s/^mod_assign_/assign/1`
|
file=`echo $file | sed s/^mod_assign_/assign/1`
|
||||||
|
@ -45,7 +43,7 @@ function exists_in_file {
|
||||||
|
|
||||||
#Checks if a key exists on the original local_moodlemobileapp.php
|
#Checks if a key exists on the original local_moodlemobileapp.php
|
||||||
function exists_in_mobile {
|
function exists_in_mobile {
|
||||||
file='local_moodlemobileapp'
|
local file='local_moodlemobileapp'
|
||||||
exists_in_file $file $key
|
exists_in_file $file $key
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,12 +112,12 @@ function find_single_matches {
|
||||||
|
|
||||||
#Tries to gues the file where the id will be found.
|
#Tries to gues the file where the id will be found.
|
||||||
function guess_file {
|
function guess_file {
|
||||||
key=$1
|
local key=$1
|
||||||
value=$2
|
local value=$2
|
||||||
|
|
||||||
type=`echo $key | cut -d'.' -f1`
|
local type=`echo $key | cut -d'.' -f1`
|
||||||
component=`echo $key | cut -d'.' -f2`
|
local component=`echo $key | cut -d'.' -f2`
|
||||||
plainid=`echo $key | cut -d'.' -f3-`
|
local plainid=`echo $key | cut -d'.' -f3-`
|
||||||
|
|
||||||
if [ -z "$plainid" ]; then
|
if [ -z "$plainid" ]; then
|
||||||
plainid=$component
|
plainid=$component
|
||||||
|
@ -161,23 +159,52 @@ function guess_file {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function current_translation_exists {
|
||||||
|
local key=$1
|
||||||
|
local current=$2
|
||||||
|
local file=$3
|
||||||
|
|
||||||
|
plainid=`echo $key | cut -d'.' -f3-`
|
||||||
|
|
||||||
|
if [ -z "$plainid" ]; then
|
||||||
|
plainid=`echo $key | cut -d'.' -f2`
|
||||||
|
fi
|
||||||
|
|
||||||
|
local currentFile=`echo $current | cut -d'/' -f1`
|
||||||
|
local currentStr=`echo $current | cut -d'/' -f2-`
|
||||||
|
if [ $currentFile == $current ]; then
|
||||||
|
currentStr=$plainid
|
||||||
|
fi
|
||||||
|
|
||||||
|
exists_in_file $currentFile $currentStr
|
||||||
|
if [ $found == 0 ]; then
|
||||||
|
# Translation not found.
|
||||||
|
exec="jq -r .\"$key\" $file"
|
||||||
|
value=`$exec`
|
||||||
|
|
||||||
|
print_error "Translation of '$currentStr' not found in '$currentFile'"
|
||||||
|
|
||||||
|
guess_file $key "$value"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
#Finds if there's a better file where to get the id from.
|
#Finds if there's a better file where to get the id from.
|
||||||
function find_better_file {
|
function find_better_file {
|
||||||
key=$1
|
local key=$1
|
||||||
value=$2
|
local value=$2
|
||||||
current=$3
|
local current=$3
|
||||||
|
|
||||||
type=`echo $key | cut -d'.' -f1`
|
local type=`echo $key | cut -d'.' -f1`
|
||||||
component=`echo $key | cut -d'.' -f2`
|
local component=`echo $key | cut -d'.' -f2`
|
||||||
plainid=`echo $key | cut -d'.' -f3-`
|
local plainid=`echo $key | cut -d'.' -f3-`
|
||||||
|
|
||||||
if [ -z "$plainid" ]; then
|
if [ -z "$plainid" ]; then
|
||||||
plainid=$component
|
plainid=$component
|
||||||
component='moodle'
|
component='moodle'
|
||||||
fi
|
fi
|
||||||
|
|
||||||
currentFile=`echo $current | cut -d'/' -f1`
|
local currentFile=`echo $current | cut -d'/' -f1`
|
||||||
currentStr=`echo $current | cut -d'/' -f2-`
|
local currentStr=`echo $current | cut -d'/' -f2-`
|
||||||
if [ $currentFile == $current ]; then
|
if [ $currentFile == $current ]; then
|
||||||
currentStr=$plainid
|
currentStr=$plainid
|
||||||
fi
|
fi
|
||||||
|
@ -217,7 +244,7 @@ function find_better_file {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
#Parses the file.
|
# Parses the file.
|
||||||
function parse_file {
|
function parse_file {
|
||||||
findbetter=$2
|
findbetter=$2
|
||||||
keys=`jq -r 'keys[]' $1`
|
keys=`jq -r 'keys[]' $1`
|
||||||
|
@ -226,12 +253,43 @@ function parse_file {
|
||||||
exec="jq -r .\"$key\" langindex.json"
|
exec="jq -r .\"$key\" langindex.json"
|
||||||
found=`$exec`
|
found=`$exec`
|
||||||
|
|
||||||
exec="jq -r .\"$key\" $1"
|
|
||||||
value=`$exec`
|
|
||||||
if [ -z "$found" ] || [ "$found" == 'null' ]; then
|
if [ -z "$found" ] || [ "$found" == 'null' ]; then
|
||||||
|
exec="jq -r .\"$key\" $1"
|
||||||
|
value=`$exec`
|
||||||
guess_file $key "$value"
|
guess_file $key "$value"
|
||||||
elif [ ! -z "$findbetter" ]; then
|
else
|
||||||
find_better_file "$key" "$value" "$found"
|
if [ ! -z "$findbetter" ]; then
|
||||||
|
exec="jq -r .\"$key\" $1"
|
||||||
|
value=`$exec`
|
||||||
|
find_better_file "$key" "$value" "$found"
|
||||||
|
elif [ "$found" != 'local_moodlemobileapp' ]; then
|
||||||
|
current_translation_exists "$key" "$found" "$1"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Do some cleanup
|
||||||
|
langkeys=`jq -r 'keys[]' langindex.json`
|
||||||
|
findkeys="${keys[@]}"
|
||||||
|
for key in $langkeys; do
|
||||||
|
# Check if already used.
|
||||||
|
array_contains "$key" "$findkeys"
|
||||||
|
|
||||||
|
if [ -z "$found" ] || [ "$found" == 'null' ]; then
|
||||||
|
remove_key $key
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Checks if an array contains an string.
|
||||||
|
function array_contains {
|
||||||
|
local hayjack=$2
|
||||||
|
local needle=$1
|
||||||
|
found=''
|
||||||
|
for i in $hayjack; do
|
||||||
|
if [ "$i" == "$needle" ] ; then
|
||||||
|
found=$i
|
||||||
|
return
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
|
@ -208,7 +208,7 @@ function build_lang($lang, $keys) {
|
||||||
// Apply translations.
|
// Apply translations.
|
||||||
if (!$string) {
|
if (!$string) {
|
||||||
if (TOTRANSLATE) {
|
if (TOTRANSLATE) {
|
||||||
echo "\n\t\To translate $value->string on $value->file";
|
echo "\n\t\tTo translate $value->string on $value->file";
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -217,9 +217,12 @@ function build_lang($lang, $keys) {
|
||||||
// Not yet translated. Do not override.
|
// Not yet translated. Do not override.
|
||||||
if ($langFile && is_array($langFile) && isset($langFile[$key])) {
|
if ($langFile && is_array($langFile) && isset($langFile[$key])) {
|
||||||
$translations[$key] = $langFile[$key];
|
$translations[$key] = $langFile[$key];
|
||||||
$local++;
|
|
||||||
|
if ($value->file == 'local_moodlemobileapp') {
|
||||||
|
$local++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (TOTRANSLATE) {
|
if (TOTRANSLATE && !isset($string[$value->string])) {
|
||||||
echo "\n\t\tTo translate $value->string on $value->file";
|
echo "\n\t\tTo translate $value->string on $value->file";
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
|
@ -265,7 +268,7 @@ function build_lang($lang, $keys) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function progressbar($percentage) {
|
function progressbar($percentage) {
|
||||||
$done = $percentage/10;
|
$done = floor($percentage/10);
|
||||||
return "\t".str_repeat('=', $done) . str_repeat('-', 10-$done);
|
return "\t".str_repeat('=', $done) . str_repeat('-', 10-$done);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,6 @@
|
||||||
"addon.block_activitymodules.pluginname": "block_activity_modules",
|
"addon.block_activitymodules.pluginname": "block_activity_modules",
|
||||||
"addon.block_badges.pluginname": "block_badges",
|
"addon.block_badges.pluginname": "block_badges",
|
||||||
"addon.block_blogmenu.pluginname": "block_blog_menu",
|
"addon.block_blogmenu.pluginname": "block_blog_menu",
|
||||||
"addon.block_blogrecent.nocourses": "block_blog_recent",
|
|
||||||
"addon.block_blogrecent.pluginname": "block_blog_recent",
|
"addon.block_blogrecent.pluginname": "block_blog_recent",
|
||||||
"addon.block_blogtags.pluginname": "block_blog_tags",
|
"addon.block_blogtags.pluginname": "block_blog_tags",
|
||||||
"addon.block_calendarmonth.pluginname": "block_calendar_month",
|
"addon.block_calendarmonth.pluginname": "block_calendar_month",
|
||||||
|
@ -427,6 +426,8 @@
|
||||||
"addon.mod_assign_submission_onlinetext.wordlimitexceeded": "assignsubmission_onlinetext",
|
"addon.mod_assign_submission_onlinetext.wordlimitexceeded": "assignsubmission_onlinetext",
|
||||||
"addon.mod_book.errorchapter": "book",
|
"addon.mod_book.errorchapter": "book",
|
||||||
"addon.mod_book.modulenameplural": "book",
|
"addon.mod_book.modulenameplural": "book",
|
||||||
|
"addon.mod_book.navnexttitle": "book",
|
||||||
|
"addon.mod_book.navprevtitle": "book",
|
||||||
"addon.mod_book.tagarea_book_chapters": "book",
|
"addon.mod_book.tagarea_book_chapters": "book",
|
||||||
"addon.mod_book.toc": "book",
|
"addon.mod_book.toc": "book",
|
||||||
"addon.mod_chat.beep": "chat",
|
"addon.mod_chat.beep": "chat",
|
||||||
|
@ -1323,12 +1324,12 @@
|
||||||
"core.all": "moodle",
|
"core.all": "moodle",
|
||||||
"core.allgroups": "moodle",
|
"core.allgroups": "moodle",
|
||||||
"core.allparticipants": "moodle",
|
"core.allparticipants": "moodle",
|
||||||
"core.android": "local_moodlemobileapp",
|
|
||||||
"core.answer": "moodle",
|
"core.answer": "moodle",
|
||||||
"core.answered": "quiz",
|
"core.answered": "quiz",
|
||||||
"core.areyousure": "moodle",
|
"core.areyousure": "moodle",
|
||||||
"core.back": "moodle",
|
"core.back": "moodle",
|
||||||
"core.block.blocks": "moodle",
|
"core.block.blocks": "moodle",
|
||||||
|
"core.browser": "local_moodlemobileapp",
|
||||||
"core.cancel": "moodle",
|
"core.cancel": "moodle",
|
||||||
"core.cannotconnect": "local_moodlemobileapp",
|
"core.cannotconnect": "local_moodlemobileapp",
|
||||||
"core.cannotdownloadfiles": "local_moodlemobileapp",
|
"core.cannotdownloadfiles": "local_moodlemobileapp",
|
||||||
|
@ -1355,7 +1356,6 @@
|
||||||
"core.comments.savecomment": "moodle",
|
"core.comments.savecomment": "moodle",
|
||||||
"core.comments.warningcommentsnotsent": "local_moodlemobileapp",
|
"core.comments.warningcommentsnotsent": "local_moodlemobileapp",
|
||||||
"core.commentscount": "moodle",
|
"core.commentscount": "moodle",
|
||||||
"core.commentsnotworking": "local_moodlemobileapp",
|
|
||||||
"core.completion-alt-auto-fail": "completion",
|
"core.completion-alt-auto-fail": "completion",
|
||||||
"core.completion-alt-auto-n": "completion",
|
"core.completion-alt-auto-n": "completion",
|
||||||
"core.completion-alt-auto-n-override": "completion",
|
"core.completion-alt-auto-n-override": "completion",
|
||||||
|
@ -1467,6 +1467,7 @@
|
||||||
"core.deleteduser": "bulkusers",
|
"core.deleteduser": "bulkusers",
|
||||||
"core.deleting": "local_moodlemobileapp",
|
"core.deleting": "local_moodlemobileapp",
|
||||||
"core.description": "moodle",
|
"core.description": "moodle",
|
||||||
|
"core.desktop": "local_moodlemobileapp",
|
||||||
"core.dfdaymonthyear": "local_moodlemobileapp",
|
"core.dfdaymonthyear": "local_moodlemobileapp",
|
||||||
"core.dfdayweekmonth": "local_moodlemobileapp",
|
"core.dfdayweekmonth": "local_moodlemobileapp",
|
||||||
"core.dffulldate": "local_moodlemobileapp",
|
"core.dffulldate": "local_moodlemobileapp",
|
||||||
|
@ -1477,11 +1478,27 @@
|
||||||
"core.digitalminor_desc": "moodle",
|
"core.digitalminor_desc": "moodle",
|
||||||
"core.discard": "local_moodlemobileapp",
|
"core.discard": "local_moodlemobileapp",
|
||||||
"core.dismiss": "local_moodlemobileapp",
|
"core.dismiss": "local_moodlemobileapp",
|
||||||
|
"core.displayoptions": "atto_media",
|
||||||
"core.done": "survey",
|
"core.done": "survey",
|
||||||
"core.download": "moodle",
|
"core.download": "moodle",
|
||||||
"core.downloaded": "local_moodlemobileapp",
|
"core.downloaded": "local_moodlemobileapp",
|
||||||
"core.downloading": "local_moodlemobileapp",
|
"core.downloading": "local_moodlemobileapp",
|
||||||
"core.edit": "moodle",
|
"core.edit": "moodle",
|
||||||
|
"core.editor.autosavesucceeded": "editor_atto",
|
||||||
|
"core.editor.bold": "atto_bold/pluginname",
|
||||||
|
"core.editor.clear": "atto_clear/pluginname",
|
||||||
|
"core.editor.h3": "atto_title",
|
||||||
|
"core.editor.h4": "atto_title",
|
||||||
|
"core.editor.h5": "atto_title",
|
||||||
|
"core.editor.hidetoolbar": "local_moodlemobileapp",
|
||||||
|
"core.editor.italic": "atto_italic/pluginname",
|
||||||
|
"core.editor.orderedlist": "atto_orderedlist/pluginname",
|
||||||
|
"core.editor.p": "atto_title",
|
||||||
|
"core.editor.strike": "atto_strike/pluginname",
|
||||||
|
"core.editor.textrecovered": "editor_atto",
|
||||||
|
"core.editor.toggle": "local_moodlemobileapp",
|
||||||
|
"core.editor.underline": "atto_underline/pluginname",
|
||||||
|
"core.editor.unorderedlist": "atto_unorderedlist/pluginname",
|
||||||
"core.emptysplit": "local_moodlemobileapp",
|
"core.emptysplit": "local_moodlemobileapp",
|
||||||
"core.error": "moodle",
|
"core.error": "moodle",
|
||||||
"core.errorchangecompletion": "local_moodlemobileapp",
|
"core.errorchangecompletion": "local_moodlemobileapp",
|
||||||
|
@ -1619,6 +1636,7 @@
|
||||||
"core.h5p.offlineDialogRetryButtonLabel": "h5p",
|
"core.h5p.offlineDialogRetryButtonLabel": "h5p",
|
||||||
"core.h5p.offlineDialogRetryMessage": "h5p",
|
"core.h5p.offlineDialogRetryMessage": "h5p",
|
||||||
"core.h5p.offlineSuccessfulSubmit": "h5p",
|
"core.h5p.offlineSuccessfulSubmit": "h5p",
|
||||||
|
"core.h5p.offlinedisabled": "local_moodlemobileapp",
|
||||||
"core.h5p.originator": "h5p",
|
"core.h5p.originator": "h5p",
|
||||||
"core.h5p.pd": "h5p",
|
"core.h5p.pd": "h5p",
|
||||||
"core.h5p.pddl": "h5p",
|
"core.h5p.pddl": "h5p",
|
||||||
|
@ -1653,7 +1671,6 @@
|
||||||
"core.imageviewer": "local_moodlemobileapp",
|
"core.imageviewer": "local_moodlemobileapp",
|
||||||
"core.info": "moodle",
|
"core.info": "moodle",
|
||||||
"core.invalidformdata": "error",
|
"core.invalidformdata": "error",
|
||||||
"core.ios": "local_moodlemobileapp",
|
|
||||||
"core.labelsep": "langconfig",
|
"core.labelsep": "langconfig",
|
||||||
"core.lastaccess": "moodle",
|
"core.lastaccess": "moodle",
|
||||||
"core.lastdownloaded": "local_moodlemobileapp",
|
"core.lastdownloaded": "local_moodlemobileapp",
|
||||||
|
@ -1674,7 +1691,6 @@
|
||||||
"core.login.changepasswordinstructions": "local_moodlemobileapp",
|
"core.login.changepasswordinstructions": "local_moodlemobileapp",
|
||||||
"core.login.changepasswordlogoutinstructions": "local_moodlemobileapp",
|
"core.login.changepasswordlogoutinstructions": "local_moodlemobileapp",
|
||||||
"core.login.changepasswordreconnectinstructions": "local_moodlemobileapp",
|
"core.login.changepasswordreconnectinstructions": "local_moodlemobileapp",
|
||||||
"core.login.checksiteversion": "local_moodlemobileapp",
|
|
||||||
"core.login.confirmdeletesite": "local_moodlemobileapp",
|
"core.login.confirmdeletesite": "local_moodlemobileapp",
|
||||||
"core.login.connect": "local_moodlemobileapp",
|
"core.login.connect": "local_moodlemobileapp",
|
||||||
"core.login.connecttomoodle": "local_moodlemobileapp",
|
"core.login.connecttomoodle": "local_moodlemobileapp",
|
||||||
|
@ -1689,7 +1705,6 @@
|
||||||
"core.login.emailconfirmsentnoemail": "local_moodlemobileapp",
|
"core.login.emailconfirmsentnoemail": "local_moodlemobileapp",
|
||||||
"core.login.emailconfirmsentsuccess": "moodle",
|
"core.login.emailconfirmsentsuccess": "moodle",
|
||||||
"core.login.emailnotmatch": "local_moodlemobileapp",
|
"core.login.emailnotmatch": "local_moodlemobileapp",
|
||||||
"core.login.enterthewordsabove": "auth",
|
|
||||||
"core.login.erroraccesscontrolalloworigin": "local_moodlemobileapp",
|
"core.login.erroraccesscontrolalloworigin": "local_moodlemobileapp",
|
||||||
"core.login.errordeletesite": "local_moodlemobileapp",
|
"core.login.errordeletesite": "local_moodlemobileapp",
|
||||||
"core.login.errorupdatesite": "local_moodlemobileapp",
|
"core.login.errorupdatesite": "local_moodlemobileapp",
|
||||||
|
@ -1697,7 +1712,6 @@
|
||||||
"core.login.firsttime": "moodle",
|
"core.login.firsttime": "moodle",
|
||||||
"core.login.forcepasswordchangenotice": "moodle",
|
"core.login.forcepasswordchangenotice": "moodle",
|
||||||
"core.login.forgotten": "moodle",
|
"core.login.forgotten": "moodle",
|
||||||
"core.login.getanothercaptcha": "auth",
|
|
||||||
"core.login.help": "moodle",
|
"core.login.help": "moodle",
|
||||||
"core.login.helpmelogin": "local_moodlemobileapp",
|
"core.login.helpmelogin": "local_moodlemobileapp",
|
||||||
"core.login.instructions": "auth",
|
"core.login.instructions": "auth",
|
||||||
|
@ -1710,9 +1724,6 @@
|
||||||
"core.login.invalidurl": "scorm",
|
"core.login.invalidurl": "scorm",
|
||||||
"core.login.invalidvaluemax": "local_moodlemobileapp",
|
"core.login.invalidvaluemax": "local_moodlemobileapp",
|
||||||
"core.login.invalidvaluemin": "local_moodlemobileapp",
|
"core.login.invalidvaluemin": "local_moodlemobileapp",
|
||||||
"core.login.legacymoodleversion": "local_moodlemobileapp",
|
|
||||||
"core.login.legacymoodleversiondesktop": "local_moodlemobileapp",
|
|
||||||
"core.login.legacymoodleversiondesktopdownloadold": "local_moodlemobileapp",
|
|
||||||
"core.login.localmobileunexpectedresponse": "local_moodlemobileapp",
|
"core.login.localmobileunexpectedresponse": "local_moodlemobileapp",
|
||||||
"core.login.loggedoutssodescription": "local_moodlemobileapp",
|
"core.login.loggedoutssodescription": "local_moodlemobileapp",
|
||||||
"core.login.login": "moodle",
|
"core.login.login": "moodle",
|
||||||
|
@ -1767,7 +1778,6 @@
|
||||||
"core.login.visitchangepassword": "local_moodlemobileapp",
|
"core.login.visitchangepassword": "local_moodlemobileapp",
|
||||||
"core.login.webservicesnotenabled": "local_moodlemobileapp",
|
"core.login.webservicesnotenabled": "local_moodlemobileapp",
|
||||||
"core.lostconnection": "local_moodlemobileapp",
|
"core.lostconnection": "local_moodlemobileapp",
|
||||||
"core.mainmenu.appsettings": "local_moodlemobileapp",
|
|
||||||
"core.mainmenu.changesite": "local_moodlemobileapp",
|
"core.mainmenu.changesite": "local_moodlemobileapp",
|
||||||
"core.mainmenu.help": "moodle",
|
"core.mainmenu.help": "moodle",
|
||||||
"core.mainmenu.logout": "moodle",
|
"core.mainmenu.logout": "moodle",
|
||||||
|
@ -1898,7 +1908,8 @@
|
||||||
"core.sending": "chat",
|
"core.sending": "chat",
|
||||||
"core.serverconnection": "error",
|
"core.serverconnection": "error",
|
||||||
"core.settings.about": "local_moodlemobileapp",
|
"core.settings.about": "local_moodlemobileapp",
|
||||||
"core.settings.appready": "local_moodlemobileapp",
|
"core.settings.appsettings": "local_moodlemobileapp",
|
||||||
|
"core.settings.appversion": "local_moodlemobileapp",
|
||||||
"core.settings.cannotsyncoffline": "local_moodlemobileapp",
|
"core.settings.cannotsyncoffline": "local_moodlemobileapp",
|
||||||
"core.settings.cannotsyncwithoutwifi": "local_moodlemobileapp",
|
"core.settings.cannotsyncwithoutwifi": "local_moodlemobileapp",
|
||||||
"core.settings.colorscheme": "local_moodlemobileapp",
|
"core.settings.colorscheme": "local_moodlemobileapp",
|
||||||
|
@ -1906,6 +1917,7 @@
|
||||||
"core.settings.colorscheme-dark": "local_moodlemobileapp",
|
"core.settings.colorscheme-dark": "local_moodlemobileapp",
|
||||||
"core.settings.colorscheme-light": "local_moodlemobileapp",
|
"core.settings.colorscheme-light": "local_moodlemobileapp",
|
||||||
"core.settings.compilationinfo": "local_moodlemobileapp",
|
"core.settings.compilationinfo": "local_moodlemobileapp",
|
||||||
|
"core.settings.copyinfo": "local_moodlemobileapp",
|
||||||
"core.settings.cordovadevicemodel": "local_moodlemobileapp",
|
"core.settings.cordovadevicemodel": "local_moodlemobileapp",
|
||||||
"core.settings.cordovadeviceosversion": "local_moodlemobileapp",
|
"core.settings.cordovadeviceosversion": "local_moodlemobileapp",
|
||||||
"core.settings.cordovadeviceplatform": "local_moodlemobileapp",
|
"core.settings.cordovadeviceplatform": "local_moodlemobileapp",
|
||||||
|
@ -1918,7 +1930,6 @@
|
||||||
"core.settings.deletesitefilestitle": "local_moodlemobileapp",
|
"core.settings.deletesitefilestitle": "local_moodlemobileapp",
|
||||||
"core.settings.deviceinfo": "local_moodlemobileapp",
|
"core.settings.deviceinfo": "local_moodlemobileapp",
|
||||||
"core.settings.deviceos": "local_moodlemobileapp",
|
"core.settings.deviceos": "local_moodlemobileapp",
|
||||||
"core.settings.devicewebworkers": "local_moodlemobileapp",
|
|
||||||
"core.settings.disableall": "message",
|
"core.settings.disableall": "message",
|
||||||
"core.settings.disabled": "lesson",
|
"core.settings.disabled": "lesson",
|
||||||
"core.settings.displayformat": "local_moodlemobileapp",
|
"core.settings.displayformat": "local_moodlemobileapp",
|
||||||
|
@ -1935,6 +1946,7 @@
|
||||||
"core.settings.filesystemroot": "local_moodlemobileapp",
|
"core.settings.filesystemroot": "local_moodlemobileapp",
|
||||||
"core.settings.fontsize": "local_moodlemobileapp",
|
"core.settings.fontsize": "local_moodlemobileapp",
|
||||||
"core.settings.fontsizecharacter": "block_accessibility/char",
|
"core.settings.fontsizecharacter": "block_accessibility/char",
|
||||||
|
"core.settings.forcedsetting": "local_moodlemobileapp",
|
||||||
"core.settings.general": "moodle",
|
"core.settings.general": "moodle",
|
||||||
"core.settings.language": "moodle",
|
"core.settings.language": "moodle",
|
||||||
"core.settings.license": "moodle",
|
"core.settings.license": "moodle",
|
||||||
|
@ -1946,19 +1958,24 @@
|
||||||
"core.settings.navigatorlanguage": "local_moodlemobileapp",
|
"core.settings.navigatorlanguage": "local_moodlemobileapp",
|
||||||
"core.settings.navigatoruseragent": "local_moodlemobileapp",
|
"core.settings.navigatoruseragent": "local_moodlemobileapp",
|
||||||
"core.settings.networkstatus": "local_moodlemobileapp",
|
"core.settings.networkstatus": "local_moodlemobileapp",
|
||||||
|
"core.settings.opensourcelicenses": "local_moodlemobileapp",
|
||||||
|
"core.settings.preferences": "moodle",
|
||||||
"core.settings.privacypolicy": "local_moodlemobileapp",
|
"core.settings.privacypolicy": "local_moodlemobileapp",
|
||||||
|
"core.settings.publisher": "local_moodlemobileapp",
|
||||||
"core.settings.pushid": "local_moodlemobileapp",
|
"core.settings.pushid": "local_moodlemobileapp",
|
||||||
"core.settings.reportinbackground": "local_moodlemobileapp",
|
"core.settings.reportinbackground": "local_moodlemobileapp",
|
||||||
|
"core.settings.screen": "local_moodlemobileapp",
|
||||||
"core.settings.settings": "moodle",
|
"core.settings.settings": "moodle",
|
||||||
"core.settings.showdownloadoptions": "local_moodlemobileapp",
|
"core.settings.showdownloadoptions": "local_moodlemobileapp",
|
||||||
|
"core.settings.siteinfo": "local_moodlemobileapp",
|
||||||
"core.settings.sites": "moodle",
|
"core.settings.sites": "moodle",
|
||||||
"core.settings.spaceusage": "local_moodlemobileapp",
|
"core.settings.spaceusage": "local_moodlemobileapp",
|
||||||
|
"core.settings.spaceusagehelp": "local_moodlemobileapp",
|
||||||
"core.settings.synchronization": "local_moodlemobileapp",
|
"core.settings.synchronization": "local_moodlemobileapp",
|
||||||
"core.settings.synchronizenow": "local_moodlemobileapp",
|
"core.settings.synchronizenow": "local_moodlemobileapp",
|
||||||
|
"core.settings.synchronizenowhelp": "local_moodlemobileapp",
|
||||||
"core.settings.syncsettings": "local_moodlemobileapp",
|
"core.settings.syncsettings": "local_moodlemobileapp",
|
||||||
"core.settings.total": "moodle",
|
"core.settings.total": "moodle",
|
||||||
"core.settings.versioncode": "local_moodlemobileapp",
|
|
||||||
"core.settings.versionname": "local_moodlemobileapp",
|
|
||||||
"core.settings.wificonnection": "local_moodlemobileapp",
|
"core.settings.wificonnection": "local_moodlemobileapp",
|
||||||
"core.sharedfiles.chooseaccountstorefile": "local_moodlemobileapp",
|
"core.sharedfiles.chooseaccountstorefile": "local_moodlemobileapp",
|
||||||
"core.sharedfiles.chooseactionrepeatedfile": "local_moodlemobileapp",
|
"core.sharedfiles.chooseactionrepeatedfile": "local_moodlemobileapp",
|
||||||
|
@ -2003,18 +2020,18 @@
|
||||||
"core.submit": "moodle",
|
"core.submit": "moodle",
|
||||||
"core.success": "moodle",
|
"core.success": "moodle",
|
||||||
"core.tablet": "local_moodlemobileapp",
|
"core.tablet": "local_moodlemobileapp",
|
||||||
"core.tag.defautltagcoll": "moodle",
|
"core.tag.defautltagcoll": "tag",
|
||||||
"core.tag.errorareanotsupported": "local_moodlemobileapp",
|
"core.tag.errorareanotsupported": "local_moodlemobileapp",
|
||||||
"core.tag.inalltagcoll": "moodle",
|
"core.tag.inalltagcoll": "tag",
|
||||||
"core.tag.itemstaggedwith": "moodle",
|
"core.tag.itemstaggedwith": "tag",
|
||||||
"core.tag.notagsfound": "moodle",
|
"core.tag.notagsfound": "tag",
|
||||||
"core.tag.searchtags": "moodle",
|
"core.tag.searchtags": "tag",
|
||||||
"core.tag.showingfirsttags": "moodle",
|
"core.tag.showingfirsttags": "tag",
|
||||||
"core.tag.tag": "moodle",
|
"core.tag.tag": "moodle",
|
||||||
"core.tag.tagarea_course": "moodle",
|
"core.tag.tagarea_course": "tag",
|
||||||
"core.tag.tagarea_course_modules": "moodle",
|
"core.tag.tagarea_course_modules": "tag",
|
||||||
"core.tag.tagarea_post": "moodle",
|
"core.tag.tagarea_post": "tag",
|
||||||
"core.tag.tagarea_user": "moodle",
|
"core.tag.tagarea_user": "tag",
|
||||||
"core.tag.tags": "moodle",
|
"core.tag.tags": "moodle",
|
||||||
"core.tag.warningareasnotsupported": "local_moodlemobileapp",
|
"core.tag.warningareasnotsupported": "local_moodlemobileapp",
|
||||||
"core.teachers": "moodle",
|
"core.teachers": "moodle",
|
||||||
|
@ -2076,7 +2093,6 @@
|
||||||
"core.whoops": "local_moodlemobileapp",
|
"core.whoops": "local_moodlemobileapp",
|
||||||
"core.whyisthishappening": "local_moodlemobileapp",
|
"core.whyisthishappening": "local_moodlemobileapp",
|
||||||
"core.whyisthisrequired": "moodle",
|
"core.whyisthisrequired": "moodle",
|
||||||
"core.windowsphone": "local_moodlemobileapp",
|
|
||||||
"core.wsfunctionnotavailable": "local_moodlemobileapp",
|
"core.wsfunctionnotavailable": "local_moodlemobileapp",
|
||||||
"core.year": "moodle",
|
"core.year": "moodle",
|
||||||
"core.years": "moodle",
|
"core.years": "moodle",
|
||||||
|
|
|
@ -15,12 +15,3 @@ ion-app.app-root.ios addon-block-activitymodules {
|
||||||
height: 24px;
|
height: 24px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ion-app.app-root.wp addon-block-activitymodules {
|
|
||||||
.core-module-icon {
|
|
||||||
margin-top: $item-wp-padding-top;
|
|
||||||
margin-bottom: $item-wp-padding-bottom;
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<h2>{{ 'addon.block_activitymodules.pluginname' | translate }}</h2>
|
<h2>{{ 'addon.block_activitymodules.pluginname' | translate }}</h2>
|
||||||
</ion-item-divider>
|
</ion-item-divider>
|
||||||
<core-loading [hideUntil]="loaded" class="core-loading-center">
|
<core-loading [hideUntil]="loaded" class="core-loading-center">
|
||||||
<a ion-item text-wrap *ngFor="let entry of entries" class="item-media" detail-none [navPush]="'CoreCourseListModTypePage'" [navParams]="{title: entry.name, courseId: instanceId, modName: entry.modName}">
|
<a ion-item text-wrap *ngFor="let entry of entries" class="item-media" detail-none navPush="CoreCourseListModTypePage" [navParams]="{title: entry.name, courseId: instanceId, modName: entry.modName}">
|
||||||
<img item-start [src]="entry.icon" alt="" role="presentation" class="core-module-icon">
|
<img item-start [src]="entry.icon" alt="" role="presentation" class="core-module-icon">
|
||||||
{{ entry.name }}
|
{{ entry.name }}
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -218,7 +218,8 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
|
||||||
|
|
||||||
this.showFilters.favourite = this.getShowFilterValue(
|
this.showFilters.favourite = this.getShowFilterValue(
|
||||||
this.showSelectorFilter && typeof courses[0].isfavourite != 'undefined' &&
|
this.showSelectorFilter && typeof courses[0].isfavourite != 'undefined' &&
|
||||||
(!config || config.displaygroupingstarred.value == '1'),
|
(!config || (config.displaygroupingstarred && config.displaygroupingstarred.value == '1') ||
|
||||||
|
(config.displaygroupingfavourites && config.displaygroupingfavourites.value == '1')),
|
||||||
this.courses.favourite.length === 0);
|
this.courses.favourite.length === 0);
|
||||||
|
|
||||||
this.showFilters.custom = this.getShowFilterValue(this.showSelectorFilter && config &&
|
this.showFilters.custom = this.getShowFilterValue(this.showSelectorFilter && config &&
|
||||||
|
|
|
@ -25,7 +25,6 @@ import { CoreCronDelegate } from '@providers/cron';
|
||||||
import { CoreInitDelegate } from '@providers/init';
|
import { CoreInitDelegate } from '@providers/init';
|
||||||
import { CoreLocalNotificationsProvider } from '@providers/local-notifications';
|
import { CoreLocalNotificationsProvider } from '@providers/local-notifications';
|
||||||
import { CoreLoginHelperProvider } from '@core/login/providers/helper';
|
import { CoreLoginHelperProvider } from '@core/login/providers/helper';
|
||||||
import { CoreUpdateManagerProvider } from '@providers/update-manager';
|
|
||||||
import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate';
|
import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate';
|
||||||
import { AddonCalendarComponentsModule } from './components/components.module';
|
import { AddonCalendarComponentsModule } from './components/components.module';
|
||||||
|
|
||||||
|
@ -56,7 +55,7 @@ export const ADDON_CALENDAR_PROVIDERS: any[] = [
|
||||||
export class AddonCalendarModule {
|
export class AddonCalendarModule {
|
||||||
constructor(mainMenuDelegate: CoreMainMenuDelegate, calendarHandler: AddonCalendarMainMenuHandler,
|
constructor(mainMenuDelegate: CoreMainMenuDelegate, calendarHandler: AddonCalendarMainMenuHandler,
|
||||||
initDelegate: CoreInitDelegate, calendarProvider: AddonCalendarProvider, loginHelper: CoreLoginHelperProvider,
|
initDelegate: CoreInitDelegate, calendarProvider: AddonCalendarProvider, loginHelper: CoreLoginHelperProvider,
|
||||||
localNotificationsProvider: CoreLocalNotificationsProvider, updateManager: CoreUpdateManagerProvider,
|
localNotificationsProvider: CoreLocalNotificationsProvider,
|
||||||
cronDelegate: CoreCronDelegate, syncHandler: AddonCalendarSyncCronHandler,
|
cronDelegate: CoreCronDelegate, syncHandler: AddonCalendarSyncCronHandler,
|
||||||
contentLinksDelegate: CoreContentLinksDelegate, viewLinkHandler: AddonCalendarViewLinkHandler) {
|
contentLinksDelegate: CoreContentLinksDelegate, viewLinkHandler: AddonCalendarViewLinkHandler) {
|
||||||
|
|
||||||
|
@ -88,18 +87,5 @@ export class AddonCalendarModule {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Allow migrating the table from the old app to the new schema.
|
|
||||||
// In the old app some calculated properties were stored when it shouldn't. Filter only the fields we want.
|
|
||||||
updateManager.registerSiteTableMigration({
|
|
||||||
name: 'calendar_events',
|
|
||||||
newName: AddonCalendarProvider.EVENTS_TABLE,
|
|
||||||
filterFields: ['id', 'name', 'description', 'format', 'eventtype', 'courseid', 'timestart', 'timeduration',
|
|
||||||
'categoryid', 'groupid', 'userid', 'instance', 'modulename', 'timemodified', 'repeatid', 'visible', 'uuid',
|
|
||||||
'sequence', 'subscriptionid']
|
|
||||||
});
|
|
||||||
|
|
||||||
// Migrate the component name.
|
|
||||||
updateManager.registerLocalNotifComponentMigration('mmaCalendarComponent', AddonCalendarProvider.COMPONENT);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,6 +89,7 @@ ion-app.app-root addon-calendar-calendar {
|
||||||
@include border-end(1px, solid, $calendar-border-color);
|
@include border-end(1px, solid, $calendar-border-color);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
min-height: 60px;
|
min-height: 60px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
&:first-child {
|
&:first-child {
|
||||||
@include padding(null, null, null, 10px);
|
@include padding(null, null, null, 10px);
|
||||||
|
@ -131,6 +132,7 @@ ion-app.app-root addon-calendar-calendar {
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
&.dayblank {
|
&.dayblank {
|
||||||
|
cursor: auto;
|
||||||
background-color: $gray-lighter;
|
background-color: $gray-lighter;
|
||||||
@include darkmode() {
|
@include darkmode() {
|
||||||
background-color: $black;
|
background-color: $black;
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
ion-app.app-root addon-calendar-upcoming-events {
|
||||||
|
.addon-calendar-event {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
|
@ -48,7 +48,7 @@
|
||||||
|
|
||||||
<ion-list *ngIf="filteredEvents && filteredEvents.length" no-margin>
|
<ion-list *ngIf="filteredEvents && filteredEvents.length" no-margin>
|
||||||
<ng-container *ngFor="let event of filteredEvents">
|
<ng-container *ngFor="let event of filteredEvents">
|
||||||
<ion-item text-wrap [title]="event.name" (click)="gotoEvent(event.id)" [class.item-dimmed]="event.ispast" class="addon-calendar-event" [ngClass]="['addon-calendar-eventtype-'+event.eventtype]">
|
<a ion-item text-wrap [title]="event.name" (click)="gotoEvent(event.id)" [class.item-dimmed]="event.ispast" class="addon-calendar-event" [ngClass]="['addon-calendar-eventtype-'+event.eventtype]">
|
||||||
<img *ngIf="event.moduleIcon" src="{{event.moduleIcon}}" item-start class="core-module-icon">
|
<img *ngIf="event.moduleIcon" src="{{event.moduleIcon}}" item-start class="core-module-icon">
|
||||||
<core-icon *ngIf="event.eventIcon && !event.moduleIcon" [name]="event.eventIcon" item-start></core-icon>
|
<core-icon *ngIf="event.eventIcon && !event.moduleIcon" [name]="event.eventIcon" item-start></core-icon>
|
||||||
<h2><core-format-text [text]="event.name" [contextLevel]="event.contextLevel" [contextInstanceId]="event.contextInstanceId"></core-format-text></h2>
|
<h2><core-format-text [text]="event.name" [contextLevel]="event.contextLevel" [contextInstanceId]="event.contextInstanceId"></core-format-text></h2>
|
||||||
|
@ -61,7 +61,7 @@
|
||||||
<ion-icon name="trash"></ion-icon>
|
<ion-icon name="trash"></ion-icon>
|
||||||
<span text-wrap>{{ 'core.deletedoffline' | translate }}</span>
|
<span text-wrap>{{ 'core.deletedoffline' | translate }}</span>
|
||||||
</ion-note>
|
</ion-note>
|
||||||
</ion-item>
|
</a>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</ion-list>
|
</ion-list>
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
</ion-refresher>
|
</ion-refresher>
|
||||||
|
|
||||||
<core-loading [hideUntil]="loaded">
|
<core-loading [hideUntil]="loaded">
|
||||||
<form ion-list [formGroup]="eventForm" *ngIf="!error">
|
<form ion-list [formGroup]="eventForm" *ngIf="!error" #editEventForm>
|
||||||
<!-- Event name. -->
|
<!-- Event name. -->
|
||||||
<ion-item text-wrap>
|
<ion-item text-wrap>
|
||||||
<ion-label stacked><h2 [core-mark-required]="true">{{ 'addon.calendar.eventname' | translate }}</h2></ion-label>
|
<ion-label stacked><h2 [core-mark-required]="true">{{ 'addon.calendar.eventname' | translate }}</h2></ion-label>
|
||||||
|
@ -86,7 +86,7 @@
|
||||||
<!-- Description. -->
|
<!-- Description. -->
|
||||||
<ion-item text-wrap>
|
<ion-item text-wrap>
|
||||||
<ion-label stacked><h2>{{ 'core.description' | translate }}</h2></ion-label>
|
<ion-label stacked><h2>{{ 'core.description' | translate }}</h2></ion-label>
|
||||||
<core-rich-text-editor item-content [control]="descriptionControl" [placeholder]="'core.description' | translate" name="description" [component]="component" [componentId]="eventId"></core-rich-text-editor>
|
<core-rich-text-editor item-content [control]="descriptionControl" [placeholder]="'core.description' | translate" name="description" [component]="component" [componentId]="eventId" [autoSave]="false"></core-rich-text-editor>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
|
|
||||||
<!-- Location. -->
|
<!-- Location. -->
|
||||||
|
|
|
@ -17,6 +17,7 @@ import { IonicPageModule } from 'ionic-angular';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { CoreComponentsModule } from '@components/components.module';
|
import { CoreComponentsModule } from '@components/components.module';
|
||||||
import { CoreDirectivesModule } from '@directives/directives.module';
|
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||||
|
import { CoreEditorComponentsModule } from '@core/editor/components/components.module';
|
||||||
import { AddonCalendarEditEventPage } from './edit-event';
|
import { AddonCalendarEditEventPage } from './edit-event';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
@ -26,6 +27,7 @@ import { AddonCalendarEditEventPage } from './edit-event';
|
||||||
imports: [
|
imports: [
|
||||||
CoreComponentsModule,
|
CoreComponentsModule,
|
||||||
CoreDirectivesModule,
|
CoreDirectivesModule,
|
||||||
|
CoreEditorComponentsModule,
|
||||||
IonicPageModule.forChild(AddonCalendarEditEventPage),
|
IonicPageModule.forChild(AddonCalendarEditEventPage),
|
||||||
TranslateModule.forChild()
|
TranslateModule.forChild()
|
||||||
],
|
],
|
||||||
|
|
|
@ -14,13 +14,6 @@ ion-app.app-root page-addon-calendar-edit-event {
|
||||||
@include padding-horizontal($datetime-md-padding-start - $text-input-md-margin-start, null);
|
@include padding-horizontal($datetime-md-padding-start - $text-input-md-margin-start, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&.item-wp {
|
|
||||||
@include padding-horizontal($item-wp-padding-start * 2, null);
|
|
||||||
|
|
||||||
ion-input {
|
|
||||||
@include padding-horizontal($datetime-wp-padding-start - $text-input-wp-margin-start, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.addon-calendar-eventtype-container.item-select-disabled {
|
.addon-calendar-eventtype-container.item-select-disabled {
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { Component, OnInit, OnDestroy, Optional, ViewChild } from '@angular/core';
|
import { Component, OnInit, OnDestroy, Optional, ViewChild, ElementRef } from '@angular/core';
|
||||||
import { FormControl, FormGroup, FormBuilder, Validators } from '@angular/forms';
|
import { FormControl, FormGroup, FormBuilder, Validators } from '@angular/forms';
|
||||||
import { IonicPage, NavController, NavParams } from 'ionic-angular';
|
import { IonicPage, NavController, NavParams } from 'ionic-angular';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
@ -25,7 +25,7 @@ import { CoreTimeUtilsProvider } from '@providers/utils/time';
|
||||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||||
import { CoreCoursesProvider } from '@core/courses/providers/courses';
|
import { CoreCoursesProvider } from '@core/courses/providers/courses';
|
||||||
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
||||||
import { CoreRichTextEditorComponent } from '@components/rich-text-editor/rich-text-editor.ts';
|
import { CoreEditorRichTextEditorComponent } from '@core/editor/components/rich-text-editor/rich-text-editor.ts';
|
||||||
import { AddonCalendarProvider, AddonCalendarGetAccessInfoResult, AddonCalendarEvent } from '../../providers/calendar';
|
import { AddonCalendarProvider, AddonCalendarGetAccessInfoResult, AddonCalendarEvent } from '../../providers/calendar';
|
||||||
import { AddonCalendarOfflineProvider } from '../../providers/calendar-offline';
|
import { AddonCalendarOfflineProvider } from '../../providers/calendar-offline';
|
||||||
import { AddonCalendarHelperProvider } from '../../providers/helper';
|
import { AddonCalendarHelperProvider } from '../../providers/helper';
|
||||||
|
@ -43,7 +43,8 @@ import { CoreFilterHelperProvider } from '@core/filter/providers/helper';
|
||||||
})
|
})
|
||||||
export class AddonCalendarEditEventPage implements OnInit, OnDestroy {
|
export class AddonCalendarEditEventPage implements OnInit, OnDestroy {
|
||||||
|
|
||||||
@ViewChild(CoreRichTextEditorComponent) descriptionEditor: CoreRichTextEditorComponent;
|
@ViewChild(CoreEditorRichTextEditorComponent) descriptionEditor: CoreEditorRichTextEditorComponent;
|
||||||
|
@ViewChild('editEventForm') formElement: ElementRef;
|
||||||
|
|
||||||
title: string;
|
title: string;
|
||||||
dateFormat: string;
|
dateFormat: string;
|
||||||
|
@ -496,6 +497,8 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy {
|
||||||
this.calendarProvider.submitEvent(this.eventId, data).then((result) => {
|
this.calendarProvider.submitEvent(this.eventId, data).then((result) => {
|
||||||
event = result.event;
|
event = result.event;
|
||||||
|
|
||||||
|
this.domUtils.triggerFormSubmittedEvent(this.formElement, result.sent, this.currentSite.getId());
|
||||||
|
|
||||||
if (result.sent) {
|
if (result.sent) {
|
||||||
// Event created or edited, invalidate right days & months.
|
// Event created or edited, invalidate right days & months.
|
||||||
const numberOfRepetitions = formData.repeat ? formData.repeats :
|
const numberOfRepetitions = formData.repeat ? formData.repeats :
|
||||||
|
@ -557,6 +560,9 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy {
|
||||||
discard(): void {
|
discard(): void {
|
||||||
this.domUtils.showConfirm(this.translate.instant('core.areyousure')).then(() => {
|
this.domUtils.showConfirm(this.translate.instant('core.areyousure')).then(() => {
|
||||||
this.calendarOffline.deleteEvent(this.eventId).then(() => {
|
this.calendarOffline.deleteEvent(this.eventId).then(() => {
|
||||||
|
|
||||||
|
this.domUtils.triggerFormCancelledEvent(this.formElement, this.currentSite.getId());
|
||||||
|
|
||||||
this.returnToList();
|
this.returnToList();
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
// Shouldn't happen.
|
// Shouldn't happen.
|
||||||
|
@ -572,16 +578,18 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy {
|
||||||
*
|
*
|
||||||
* @return Resolved if we can leave it, rejected if not.
|
* @return Resolved if we can leave it, rejected if not.
|
||||||
*/
|
*/
|
||||||
ionViewCanLeave(): boolean | Promise<void> {
|
async ionViewCanLeave(): Promise<void> {
|
||||||
|
|
||||||
if (this.calendarHelper.hasEventDataChanged(this.eventForm.value, this.originalData)) {
|
if (this.calendarHelper.hasEventDataChanged(this.eventForm.value, this.originalData)) {
|
||||||
// Show confirmation if some data has been modified.
|
// Show confirmation if some data has been modified.
|
||||||
return this.domUtils.showConfirm(this.translate.instant('core.confirmcanceledit'));
|
await this.domUtils.showConfirm(this.translate.instant('core.confirmcanceledit'));
|
||||||
} else {
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.domUtils.triggerFormCancelledEvent(this.formElement, this.currentSite.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unblock sync.
|
||||||
|
*/
|
||||||
protected unblockSync(): void {
|
protected unblockSync(): void {
|
||||||
if (this.eventId) {
|
if (this.eventId) {
|
||||||
this.syncProvider.unblockOperation(AddonCalendarProvider.COMPONENT, this.eventId);
|
this.syncProvider.unblockOperation(AddonCalendarProvider.COMPONENT, this.eventId);
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
<ng-container *ngIf="!competency.competency.comppath.showlinks">{{ competency.competency.comppath.framework.name }}</ng-container>
|
<ng-container *ngIf="!competency.competency.comppath.showlinks">{{ competency.competency.comppath.framework.name }}</ng-container>
|
||||||
/
|
/
|
||||||
<span *ngFor="let ancestor of competency.competency.comppath.ancestors">
|
<span *ngFor="let ancestor of competency.competency.comppath.ancestors">
|
||||||
<a *ngIf="competency.competency.comppath.showlinks" (click)="openCompetencySummary(ancestor.id)">{{ ancestor.name }}</a>
|
<a *ngIf="competency.competency.comppath.showlinks" (click)="openCompetencySummary(ancestor.id)" class="core-clickable">{{ ancestor.name }}</a>
|
||||||
<ng-container *ngIf="!competency.competency.comppath.showlinks">{{ ancestor.name }}</ng-container>
|
<ng-container *ngIf="!competency.competency.comppath.showlinks">{{ ancestor.name }}</ng-container>
|
||||||
<ng-container *ngIf="!ancestor.last"> / </ng-container>
|
<ng-container *ngIf="!ancestor.last"> / </ng-container>
|
||||||
</span>
|
</span>
|
||||||
|
@ -35,7 +35,7 @@
|
||||||
<div *ngIf="!competency.competency.hasrelatedcompetencies">{{ 'addon.competency.nocrossreferencedcompetencies' | translate }}</div>
|
<div *ngIf="!competency.competency.hasrelatedcompetencies">{{ 'addon.competency.nocrossreferencedcompetencies' | translate }}</div>
|
||||||
<div *ngIf="competency.competency.hasrelatedcompetencies">
|
<div *ngIf="competency.competency.hasrelatedcompetencies">
|
||||||
<p *ngFor="let relatedcomp of competency.competency.relatedcompetencies">
|
<p *ngFor="let relatedcomp of competency.competency.relatedcompetencies">
|
||||||
<a (click)="openCompetencySummary(relatedcomp.id)">
|
<a (click)="openCompetencySummary(relatedcomp.id)" class="core-clickable">
|
||||||
{{ relatedcomp.shortname }} - {{ relatedcomp.idnumber }}
|
{{ relatedcomp.shortname }} - {{ relatedcomp.idnumber }}
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
<!-- List of files. -->
|
<!-- List of files. -->
|
||||||
<ion-list *ngIf="files && files.length > 0">
|
<ion-list *ngIf="files && files.length > 0">
|
||||||
<ng-container *ngFor="let file of files">
|
<ng-container *ngFor="let file of files">
|
||||||
<a *ngIf="file.isdir" ion-item class="item-media" [navPush]="'AddonFilesListPage'" [navParams]="{path: file.link, title: file.filename}">
|
<a *ngIf="file.isdir" ion-item class="item-media" navPush="AddonFilesListPage" [navParams]="{path: file.link, title: file.filename}">
|
||||||
<img [src]="file.imgPath" alt="" role="presentation" item-start>
|
<img [src]="file.imgPath" alt="" role="presentation" item-start>
|
||||||
<p>{{file.filename}}</p>
|
<p>{{file.filename}}</p>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -19,6 +19,7 @@ import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { CoreComponentsModule } from '@components/components.module';
|
import { CoreComponentsModule } from '@components/components.module';
|
||||||
import { CoreDirectivesModule } from '@directives/directives.module';
|
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||||
import { CorePipesModule } from '@pipes/pipes.module';
|
import { CorePipesModule } from '@pipes/pipes.module';
|
||||||
|
import { CoreSearchComponentsModule } from '@core/search/components/components.module';
|
||||||
import { AddonMessagesDiscussionsComponent } from '../components/discussions/discussions';
|
import { AddonMessagesDiscussionsComponent } from '../components/discussions/discussions';
|
||||||
import { AddonMessagesConfirmedContactsComponent } from '../components/confirmed-contacts/confirmed-contacts';
|
import { AddonMessagesConfirmedContactsComponent } from '../components/confirmed-contacts/confirmed-contacts';
|
||||||
import { AddonMessagesContactRequestsComponent } from '../components/contact-requests/contact-requests';
|
import { AddonMessagesContactRequestsComponent } from '../components/contact-requests/contact-requests';
|
||||||
|
@ -37,7 +38,8 @@ import { AddonMessagesContactsComponent } from '../components/contacts/contacts'
|
||||||
TranslateModule.forChild(),
|
TranslateModule.forChild(),
|
||||||
CoreComponentsModule,
|
CoreComponentsModule,
|
||||||
CoreDirectivesModule,
|
CoreDirectivesModule,
|
||||||
CorePipesModule
|
CorePipesModule,
|
||||||
|
CoreSearchComponentsModule,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
],
|
],
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||||
</ion-refresher>
|
</ion-refresher>
|
||||||
|
|
||||||
<core-search-box (onSubmit)="search($event)" (onClear)="clearSearch($event)" [placeholder]=" 'addon.messages.contactname' | translate" autocorrect="off" spellcheck="false" lengthCheck="2" [disabled]="!loaded"></core-search-box>
|
<core-search-box (onSubmit)="search($event)" (onClear)="clearSearch($event)" [placeholder]=" 'addon.messages.contactname' | translate" autocorrect="off" spellcheck="false" lengthCheck="2" [disabled]="!loaded" searchArea="AddonMessagesContacts"></core-search-box>
|
||||||
|
|
||||||
<core-loading [hideUntil]="loaded" [message]="loadingMessage">
|
<core-loading [hideUntil]="loaded" [message]="loadingMessage">
|
||||||
<core-empty-box *ngIf="!hasContacts && searchString == ''" icon="person" [message]="'addon.messages.contactlistempty' | translate"></core-empty-box>
|
<core-empty-box *ngIf="!hasContacts && searchString == ''" icon="person" [message]="'addon.messages.contactlistempty' | translate"></core-empty-box>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||||
</ion-refresher>
|
</ion-refresher>
|
||||||
|
|
||||||
<core-search-box *ngIf="search.enabled" (onSubmit)="searchMessage($event)" (onClear)="clearSearch($event)" [placeholder]=" 'addon.messages.message' | translate" autocorrect="off" spellcheck="false" lengthCheck="2" [disabled]="!loaded"></core-search-box>
|
<core-search-box *ngIf="search.enabled" (onSubmit)="searchMessage($event)" (onClear)="clearSearch($event)" [placeholder]=" 'addon.messages.message' | translate" autocorrect="off" spellcheck="false" lengthCheck="2" [disabled]="!loaded" searchArea="AddonMessagesDiscussions"></core-search-box>
|
||||||
|
|
||||||
<core-loading [hideUntil]="loaded" [message]="loadingMessage">
|
<core-loading [hideUntil]="loaded" [message]="loadingMessage">
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,6 @@ import { CoreSettingsDelegate } from '@core/settings/providers/delegate';
|
||||||
import { AddonMessagesSettingsHandler } from './providers/settings-handler';
|
import { AddonMessagesSettingsHandler } from './providers/settings-handler';
|
||||||
import { CorePushNotificationsDelegate } from '@core/pushnotifications/providers/delegate';
|
import { CorePushNotificationsDelegate } from '@core/pushnotifications/providers/delegate';
|
||||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||||
import { CoreUpdateManagerProvider } from '@providers/update-manager';
|
|
||||||
|
|
||||||
// List of providers (without handlers).
|
// List of providers (without handlers).
|
||||||
export const ADDON_MESSAGES_PROVIDERS: any[] = [
|
export const ADDON_MESSAGES_PROVIDERS: any[] = [
|
||||||
|
@ -75,7 +74,7 @@ export class AddonMessagesModule {
|
||||||
userDelegate: CoreUserDelegate, cronDelegate: CoreCronDelegate, syncHandler: AddonMessagesSyncCronHandler,
|
userDelegate: CoreUserDelegate, cronDelegate: CoreCronDelegate, syncHandler: AddonMessagesSyncCronHandler,
|
||||||
network: Network, zone: NgZone, messagesSync: AddonMessagesSyncProvider, appProvider: CoreAppProvider,
|
network: Network, zone: NgZone, messagesSync: AddonMessagesSyncProvider, appProvider: CoreAppProvider,
|
||||||
localNotifications: CoreLocalNotificationsProvider, messagesProvider: AddonMessagesProvider,
|
localNotifications: CoreLocalNotificationsProvider, messagesProvider: AddonMessagesProvider,
|
||||||
sitesProvider: CoreSitesProvider, linkHelper: CoreContentLinksHelperProvider, updateManager: CoreUpdateManagerProvider,
|
sitesProvider: CoreSitesProvider, linkHelper: CoreContentLinksHelperProvider,
|
||||||
settingsHandler: AddonMessagesSettingsHandler, settingsDelegate: CoreSettingsDelegate,
|
settingsHandler: AddonMessagesSettingsHandler, settingsDelegate: CoreSettingsDelegate,
|
||||||
pushNotificationsDelegate: CorePushNotificationsDelegate, utils: CoreUtilsProvider,
|
pushNotificationsDelegate: CorePushNotificationsDelegate, utils: CoreUtilsProvider,
|
||||||
addContactHandler: AddonMessagesAddContactUserHandler, blockContactHandler: AddonMessagesBlockContactUserHandler,
|
addContactHandler: AddonMessagesAddContactUserHandler, blockContactHandler: AddonMessagesBlockContactUserHandler,
|
||||||
|
@ -136,21 +135,5 @@ export class AddonMessagesModule {
|
||||||
// Listen for clicks in simulated push notifications.
|
// Listen for clicks in simulated push notifications.
|
||||||
localNotifications.registerClick(AddonMessagesProvider.PUSH_SIMULATION_COMPONENT, notificationClicked);
|
localNotifications.registerClick(AddonMessagesProvider.PUSH_SIMULATION_COMPONENT, notificationClicked);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow migrating the table from the old app to the new schema.
|
|
||||||
updateManager.registerSiteTableMigration({
|
|
||||||
name: 'mma_messages_offline_messages',
|
|
||||||
newName: AddonMessagesOfflineProvider.MESSAGES_TABLE,
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
name: 'textformat',
|
|
||||||
delete: true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
// Migrate the component name.
|
|
||||||
updateManager.registerLocalNotifComponentMigration('mmaMessagesPushSimulation',
|
|
||||||
AddonMessagesProvider.PUSH_SIMULATION_COMPONENT);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -366,9 +366,12 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Don't use domUtils.getScrollHeight because it gives an outdated value after receiving a new message.
|
||||||
|
const scrollHeight = this.content && this.content.getScrollElement() ? this.content.getScrollElement().scrollHeight : 0;
|
||||||
|
|
||||||
// Check if we are at the bottom to scroll it after render.
|
// Check if we are at the bottom to scroll it after render.
|
||||||
// Use a 5px error margin because in iOS there is 1px difference for some reason.
|
// Use a 5px error margin because in iOS there is 1px difference for some reason.
|
||||||
this.scrollBottom = Math.abs(this.domUtils.getScrollHeight(this.content) - this.domUtils.getScrollTop(this.content) -
|
this.scrollBottom = Math.abs(scrollHeight - this.domUtils.getScrollTop(this.content) -
|
||||||
this.domUtils.getContentHeight(this.content)) < 5;
|
this.domUtils.getContentHeight(this.content)) < 5;
|
||||||
|
|
||||||
if (this.messagesBeingSent > 0) {
|
if (this.messagesBeingSent > 0) {
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
</ion-header>
|
</ion-header>
|
||||||
<core-split-view>
|
<core-split-view>
|
||||||
<ion-content>
|
<ion-content>
|
||||||
<core-search-box (onSubmit)="search($event)" (onClear)="clearSearch($event)" [disabled]="disableSearch" autocorrect="off" [spellcheck]="false" [autoFocus]="true" [lengthCheck]="1"></core-search-box>
|
<core-search-box (onSubmit)="search($event)" (onClear)="clearSearch($event)" [disabled]="disableSearch" autocorrect="off" [spellcheck]="false" [autoFocus]="true" [lengthCheck]="1" searchArea="AddonMessagesSearch"></core-search-box>
|
||||||
<core-loading [hideUntil]="!displaySearching" [message]="'core.searching' | translate">
|
<core-loading [hideUntil]="!displaySearching" [message]="'core.searching' | translate">
|
||||||
<ion-list *ngIf="displayResults">
|
<ion-list *ngIf="displayResults">
|
||||||
<ng-container *ngTemplateOutlet="resultsTemplate; context: {item: contacts}"></ng-container>
|
<ng-container *ngTemplateOutlet="resultsTemplate; context: {item: contacts}"></ng-container>
|
||||||
|
|
|
@ -19,6 +19,7 @@ import { AddonMessagesSearchPage } from './search';
|
||||||
import { CoreComponentsModule } from '@components/components.module';
|
import { CoreComponentsModule } from '@components/components.module';
|
||||||
import { CoreDirectivesModule } from '@directives/directives.module';
|
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||||
import { CorePipesModule } from '@pipes/pipes.module';
|
import { CorePipesModule } from '@pipes/pipes.module';
|
||||||
|
import { CoreSearchComponentsModule } from '@core/search/components/components.module';
|
||||||
import { AddonMessagesComponentsModule } from '../../components/components.module';
|
import { AddonMessagesComponentsModule } from '../../components/components.module';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
@ -29,9 +30,10 @@ import { AddonMessagesComponentsModule } from '../../components/components.modul
|
||||||
CoreComponentsModule,
|
CoreComponentsModule,
|
||||||
CoreDirectivesModule,
|
CoreDirectivesModule,
|
||||||
CorePipesModule,
|
CorePipesModule,
|
||||||
|
CoreSearchComponentsModule,
|
||||||
AddonMessagesComponentsModule,
|
AddonMessagesComponentsModule,
|
||||||
IonicPageModule.forChild(AddonMessagesSearchPage),
|
IonicPageModule.forChild(AddonMessagesSearchPage),
|
||||||
TranslateModule.forChild()
|
TranslateModule.forChild(),
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class AddonMessagesSearchPageModule {}
|
export class AddonMessagesSearchPageModule {}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<ion-header>
|
<ion-header>
|
||||||
<ion-navbar core-back-button>
|
<ion-navbar core-back-button>
|
||||||
<ion-title>{{ 'addon.messages.messagepreferences' | translate }}</ion-title>
|
<ion-title>{{ 'addon.messages.messages' | translate }}</ion-title>
|
||||||
</ion-navbar>
|
</ion-navbar>
|
||||||
</ion-header>
|
</ion-header>
|
||||||
<ion-content>
|
<ion-content>
|
||||||
|
|
|
@ -44,9 +44,9 @@ export class AddonMessagesSettingsHandler implements CoreSettingsHandler {
|
||||||
getDisplayData(): CoreSettingsHandlerData {
|
getDisplayData(): CoreSettingsHandlerData {
|
||||||
return {
|
return {
|
||||||
icon: 'chatbubbles',
|
icon: 'chatbubbles',
|
||||||
title: 'addon.messages.messagepreferences',
|
title: 'addon.messages.messages',
|
||||||
page: 'AddonMessagesSettingsPage',
|
page: 'AddonMessagesSettingsPage',
|
||||||
class: 'addon-messages-settings-handler'
|
class: 'addon-messages-settings-handler',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,6 @@ import { AddonModAssignListLinkHandler } from './providers/list-link-handler';
|
||||||
import { AddonModAssignPushClickHandler } from './providers/push-click-handler';
|
import { AddonModAssignPushClickHandler } from './providers/push-click-handler';
|
||||||
import { AddonModAssignSubmissionModule } from './submission/submission.module';
|
import { AddonModAssignSubmissionModule } from './submission/submission.module';
|
||||||
import { AddonModAssignFeedbackModule } from './feedback/feedback.module';
|
import { AddonModAssignFeedbackModule } from './feedback/feedback.module';
|
||||||
import { CoreUpdateManagerProvider } from '@providers/update-manager';
|
|
||||||
|
|
||||||
// List of providers (without handlers).
|
// List of providers (without handlers).
|
||||||
export const ADDON_MOD_ASSIGN_PROVIDERS: any[] = [
|
export const ADDON_MOD_ASSIGN_PROVIDERS: any[] = [
|
||||||
|
@ -73,7 +72,7 @@ export const ADDON_MOD_ASSIGN_PROVIDERS: any[] = [
|
||||||
export class AddonModAssignModule {
|
export class AddonModAssignModule {
|
||||||
constructor(moduleDelegate: CoreCourseModuleDelegate, moduleHandler: AddonModAssignModuleHandler,
|
constructor(moduleDelegate: CoreCourseModuleDelegate, moduleHandler: AddonModAssignModuleHandler,
|
||||||
prefetchDelegate: CoreCourseModulePrefetchDelegate, prefetchHandler: AddonModAssignPrefetchHandler,
|
prefetchDelegate: CoreCourseModulePrefetchDelegate, prefetchHandler: AddonModAssignPrefetchHandler,
|
||||||
cronDelegate: CoreCronDelegate, syncHandler: AddonModAssignSyncCronHandler, updateManager: CoreUpdateManagerProvider,
|
cronDelegate: CoreCronDelegate, syncHandler: AddonModAssignSyncCronHandler,
|
||||||
contentLinksDelegate: CoreContentLinksDelegate, linkHandler: AddonModAssignIndexLinkHandler,
|
contentLinksDelegate: CoreContentLinksDelegate, linkHandler: AddonModAssignIndexLinkHandler,
|
||||||
listLinkHandler: AddonModAssignListLinkHandler, pushNotificationsDelegate: CorePushNotificationsDelegate,
|
listLinkHandler: AddonModAssignListLinkHandler, pushNotificationsDelegate: CorePushNotificationsDelegate,
|
||||||
pushClickHandler: AddonModAssignPushClickHandler) {
|
pushClickHandler: AddonModAssignPushClickHandler) {
|
||||||
|
@ -84,57 +83,5 @@ export class AddonModAssignModule {
|
||||||
contentLinksDelegate.registerHandler(linkHandler);
|
contentLinksDelegate.registerHandler(linkHandler);
|
||||||
contentLinksDelegate.registerHandler(listLinkHandler);
|
contentLinksDelegate.registerHandler(listLinkHandler);
|
||||||
pushNotificationsDelegate.registerClickHandler(pushClickHandler);
|
pushNotificationsDelegate.registerClickHandler(pushClickHandler);
|
||||||
|
|
||||||
// Allow migrating the tables from the old app to the new schema.
|
|
||||||
updateManager.registerSiteTablesMigration([
|
|
||||||
{
|
|
||||||
name: 'mma_mod_assign_submissions',
|
|
||||||
newName: AddonModAssignOfflineProvider.SUBMISSIONS_TABLE,
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
name: 'assignmentid',
|
|
||||||
newName: 'assignid'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'submitted',
|
|
||||||
type: 'boolean'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'submissionstatement',
|
|
||||||
type: 'boolean'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'plugindata',
|
|
||||||
type: 'object'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'mma_mod_assign_submissions_grading',
|
|
||||||
newName: AddonModAssignOfflineProvider.SUBMISSIONS_GRADES_TABLE,
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
name: 'assignmentid',
|
|
||||||
newName: 'assignid'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'addattempt',
|
|
||||||
type: 'boolean'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'applytoall',
|
|
||||||
type: 'boolean'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'outcomes',
|
|
||||||
type: 'object'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'plugindata',
|
|
||||||
type: 'object'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
<core-loading [hideUntil]="loaded" class="core-loading-center">
|
<core-loading [hideUntil]="loaded" class="core-loading-center">
|
||||||
|
|
||||||
<!-- Description and intro attachments. -->
|
<!-- Description and intro attachments. -->
|
||||||
<ion-card *ngIf="description" (click)="expandDescription($event)">
|
<ion-card *ngIf="description" (click)="expandDescription($event)" class="core-clickable">
|
||||||
<ion-item text-wrap>
|
<ion-item text-wrap>
|
||||||
<core-format-text [text]="description" [component]="component" [componentId]="componentId" maxHeight="120" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId" (click)="expandDescription($event)"></core-format-text>
|
<core-format-text [text]="description" [component]="component" [componentId]="componentId" maxHeight="120" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId" (click)="expandDescription($event)"></core-format-text>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
|
|
|
@ -21,6 +21,7 @@ import { AddonModAssignFeedbackCommentsComponent } from './component/comments';
|
||||||
import { AddonModAssignFeedbackDelegate } from '../../providers/feedback-delegate';
|
import { AddonModAssignFeedbackDelegate } from '../../providers/feedback-delegate';
|
||||||
import { CoreComponentsModule } from '@components/components.module';
|
import { CoreComponentsModule } from '@components/components.module';
|
||||||
import { CoreDirectivesModule } from '@directives/directives.module';
|
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||||
|
import { CoreEditorComponentsModule } from '@core/editor/components/components.module';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
|
@ -31,7 +32,8 @@ import { CoreDirectivesModule } from '@directives/directives.module';
|
||||||
IonicModule,
|
IonicModule,
|
||||||
TranslateModule.forChild(),
|
TranslateModule.forChild(),
|
||||||
CoreComponentsModule,
|
CoreComponentsModule,
|
||||||
CoreDirectivesModule
|
CoreDirectivesModule,
|
||||||
|
CoreEditorComponentsModule,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
AddonModAssignFeedbackCommentsHandler
|
AddonModAssignFeedbackCommentsHandler
|
||||||
|
|
|
@ -19,5 +19,6 @@
|
||||||
|
|
||||||
<!-- Edit -->
|
<!-- Edit -->
|
||||||
<ion-item text-wrap *ngIf="edit && loaded">
|
<ion-item text-wrap *ngIf="edit && loaded">
|
||||||
<core-rich-text-editor item-content [control]="control" [placeholder]="plugin.name" name="assignfeedbackcomments_editor" [component]="component" [componentId]="assign.cmid"></core-rich-text-editor>
|
<core-rich-text-editor item-content [control]="control" [placeholder]="plugin.name" name="assignfeedbackcomments_editor" [component]="component" [componentId]="assign.cmid" [autoSave]="true" contextLevel="module" [contextInstanceId]="assign.cmid" elementId="assignfeedbackcomments_editor" [draftExtraParams]="{userid: userId, action: 'grade'}">
|
||||||
|
</core-rich-text-editor>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
</ion-navbar>
|
</ion-navbar>
|
||||||
</ion-header>
|
</ion-header>
|
||||||
<ion-content>
|
<ion-content>
|
||||||
<form name="addon-mod_assign-edit-feedback-form" *ngIf="userId && plugin">
|
<form name="addon-mod_assign-edit-feedback-form" *ngIf="userId && plugin" #editFeedbackForm>
|
||||||
<addon-mod-assign-feedback-plugin [assign]="assign" [submission]="submission" [userId]="userId" [plugin]="plugin" [edit]="true"></addon-mod-assign-feedback-plugin>
|
<addon-mod-assign-feedback-plugin [assign]="assign" [submission]="submission" [userId]="userId" [plugin]="plugin" [edit]="true"></addon-mod-assign-feedback-plugin>
|
||||||
<button ion-button block (click)="done($event)">{{ 'core.done' | translate }}</button>
|
<button ion-button block (click)="done($event)">{{ 'core.done' | translate }}</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -12,9 +12,11 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { Component, Input } from '@angular/core';
|
import { Component, Input, ViewChild, ElementRef } from '@angular/core';
|
||||||
import { IonicPage, ViewController, NavParams } from 'ionic-angular';
|
import { IonicPage, ViewController, NavParams } from 'ionic-angular';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { CoreEventsProvider } from '@providers/events';
|
||||||
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
import { AddonModAssignFeedbackDelegate } from '../../providers/feedback-delegate';
|
import { AddonModAssignFeedbackDelegate } from '../../providers/feedback-delegate';
|
||||||
import {
|
import {
|
||||||
|
@ -36,10 +38,17 @@ export class AddonModAssignEditFeedbackModalPage {
|
||||||
@Input() plugin: AddonModAssignPlugin; // The plugin object.
|
@Input() plugin: AddonModAssignPlugin; // The plugin object.
|
||||||
@Input() userId: number; // The user ID of the submission.
|
@Input() userId: number; // The user ID of the submission.
|
||||||
|
|
||||||
|
@ViewChild('editFeedbackForm') formElement: ElementRef;
|
||||||
|
|
||||||
protected forceLeave = false; // To allow leaving the page without checking for changes.
|
protected forceLeave = false; // To allow leaving the page without checking for changes.
|
||||||
|
|
||||||
constructor(params: NavParams, protected viewCtrl: ViewController, protected domUtils: CoreDomUtilsProvider,
|
constructor(params: NavParams,
|
||||||
protected translate: TranslateService, protected feedbackDelegate: AddonModAssignFeedbackDelegate) {
|
protected viewCtrl: ViewController,
|
||||||
|
protected domUtils: CoreDomUtilsProvider,
|
||||||
|
protected translate: TranslateService,
|
||||||
|
protected feedbackDelegate: AddonModAssignFeedbackDelegate,
|
||||||
|
protected eventsProvider: CoreEventsProvider,
|
||||||
|
protected sitesProvider: CoreSitesProvider) {
|
||||||
|
|
||||||
this.assign = params.get('assign');
|
this.assign = params.get('assign');
|
||||||
this.submission = params.get('submission');
|
this.submission = params.get('submission');
|
||||||
|
@ -52,16 +61,17 @@ export class AddonModAssignEditFeedbackModalPage {
|
||||||
*
|
*
|
||||||
* @return Resolved if we can leave it, rejected if not.
|
* @return Resolved if we can leave it, rejected if not.
|
||||||
*/
|
*/
|
||||||
ionViewCanLeave(): boolean | Promise<void> {
|
async ionViewCanLeave(): Promise<void> {
|
||||||
if (this.forceLeave) {
|
if (this.forceLeave) {
|
||||||
return true;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.hasDataChanged().then((changed) => {
|
const changed = await this.hasDataChanged();
|
||||||
if (changed) {
|
if (changed) {
|
||||||
return this.domUtils.showConfirm(this.translate.instant('core.confirmcanceledit'));
|
await this.domUtils.showConfirm(this.translate.instant('core.confirmcanceledit'));
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
this.domUtils.triggerFormCancelledEvent(this.formElement, this.sitesProvider.getCurrentSiteId());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -82,6 +92,8 @@ export class AddonModAssignEditFeedbackModalPage {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
|
this.domUtils.triggerFormSubmittedEvent(this.formElement, false, this.sitesProvider.getCurrentSiteId());
|
||||||
|
|
||||||
// Close the modal, sending the input data.
|
// Close the modal, sending the input data.
|
||||||
this.forceLeave = true;
|
this.forceLeave = true;
|
||||||
this.closeModal(this.getInputData());
|
this.closeModal(this.getInputData());
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
<core-loading [hideUntil]="loaded">
|
<core-loading [hideUntil]="loaded">
|
||||||
<ion-list>
|
<ion-list>
|
||||||
<!-- @todo: plagiarism_print_disclosure -->
|
<!-- @todo: plagiarism_print_disclosure -->
|
||||||
<form name="addon-mod_assign-edit-form" *ngIf="userSubmission && userSubmission.plugins && userSubmission.plugins.length">
|
<form name="addon-mod_assign-edit-form" *ngIf="userSubmission && userSubmission.plugins && userSubmission.plugins.length" #editSubmissionForm>
|
||||||
<!-- Submission statement. -->
|
<!-- Submission statement. -->
|
||||||
<ion-item text-wrap *ngIf="submissionStatement">
|
<ion-item text-wrap *ngIf="submissionStatement">
|
||||||
<ion-label><core-format-text [text]="submissionStatement" [filter]="false"></core-format-text></ion-label>
|
<ion-label><core-format-text [text]="submissionStatement" [filter]="false"></core-format-text></ion-label>
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { Component, OnInit, OnDestroy } from '@angular/core';
|
import { Component, OnInit, OnDestroy, ViewChild, ElementRef } from '@angular/core';
|
||||||
import { IonicPage, NavController, NavParams } from 'ionic-angular';
|
import { IonicPage, NavController, NavParams } from 'ionic-angular';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { CoreEventsProvider } from '@providers/events';
|
import { CoreEventsProvider } from '@providers/events';
|
||||||
|
@ -34,6 +34,9 @@ import { AddonModAssignHelperProvider } from '../../providers/helper';
|
||||||
templateUrl: 'edit.html',
|
templateUrl: 'edit.html',
|
||||||
})
|
})
|
||||||
export class AddonModAssignEditPage implements OnInit, OnDestroy {
|
export class AddonModAssignEditPage implements OnInit, OnDestroy {
|
||||||
|
|
||||||
|
@ViewChild('editSubmissionForm') formElement: ElementRef;
|
||||||
|
|
||||||
title: string; // Title to display.
|
title: string; // Title to display.
|
||||||
assign: AddonModAssignAssign; // Assignment.
|
assign: AddonModAssignAssign; // Assignment.
|
||||||
courseId: number; // Course ID the assignment belongs to.
|
courseId: number; // Course ID the assignment belongs to.
|
||||||
|
@ -82,20 +85,21 @@ export class AddonModAssignEditPage implements OnInit, OnDestroy {
|
||||||
*
|
*
|
||||||
* @return Resolved if we can leave it, rejected if not.
|
* @return Resolved if we can leave it, rejected if not.
|
||||||
*/
|
*/
|
||||||
ionViewCanLeave(): boolean | Promise<void> {
|
async ionViewCanLeave(): Promise<void> {
|
||||||
if (this.forceLeave) {
|
if (this.forceLeave) {
|
||||||
return true;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if data has changed.
|
// Check if data has changed.
|
||||||
return this.hasDataChanged().then((changed) => {
|
const changed = await this.hasDataChanged();
|
||||||
if (changed) {
|
if (changed) {
|
||||||
return this.domUtils.showConfirm(this.translate.instant('core.confirmcanceledit'));
|
await this.domUtils.showConfirm(this.translate.instant('core.confirmcanceledit'));
|
||||||
}
|
}
|
||||||
}).then(() => {
|
|
||||||
// Nothing has changed or user confirmed to leave. Clear temporary data from plugins.
|
// Nothing has changed or user confirmed to leave. Clear temporary data from plugins.
|
||||||
this.assignHelper.clearSubmissionPluginTmpData(this.assign, this.userSubmission, this.getInputData());
|
this.assignHelper.clearSubmissionPluginTmpData(this.assign, this.userSubmission, this.getInputData());
|
||||||
});
|
|
||||||
|
this.domUtils.triggerFormCancelledEvent(this.formElement, this.sitesProvider.getCurrentSiteId());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -265,69 +269,74 @@ export class AddonModAssignEditPage implements OnInit, OnDestroy {
|
||||||
*
|
*
|
||||||
* @return Promise resolved when done.
|
* @return Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
protected saveSubmission(): Promise<any> {
|
protected async saveSubmission(): Promise<void> {
|
||||||
const inputData = this.getInputData();
|
const inputData = this.getInputData();
|
||||||
|
|
||||||
if (this.submissionStatement && (!inputData.submissionstatement || inputData.submissionstatement === 'false')) {
|
if (this.submissionStatement && (!inputData.submissionstatement || inputData.submissionstatement === 'false')) {
|
||||||
return Promise.reject(this.translate.instant('addon.mod_assign.acceptsubmissionstatement'));
|
throw this.translate.instant('addon.mod_assign.acceptsubmissionstatement');
|
||||||
}
|
}
|
||||||
|
|
||||||
let modal = this.domUtils.showModalLoading();
|
let modal = this.domUtils.showModalLoading();
|
||||||
|
let size;
|
||||||
|
|
||||||
// Get size to ask for confirmation.
|
// Get size to ask for confirmation.
|
||||||
return this.assignHelper.getSubmissionSizeForEdit(this.assign, this.userSubmission, inputData).catch(() => {
|
try {
|
||||||
|
size = await this.assignHelper.getSubmissionSizeForEdit(this.assign, this.userSubmission, inputData);
|
||||||
|
} catch (error) {
|
||||||
// Error calculating size, return -1.
|
// Error calculating size, return -1.
|
||||||
return -1;
|
size = -1;
|
||||||
}).then((size) => {
|
}
|
||||||
modal.dismiss();
|
|
||||||
|
|
||||||
|
modal.dismiss();
|
||||||
|
|
||||||
|
try {
|
||||||
// Confirm action.
|
// Confirm action.
|
||||||
return this.fileUploaderHelper.confirmUploadFile(size, true, this.allowOffline);
|
await this.fileUploaderHelper.confirmUploadFile(size, true, this.allowOffline);
|
||||||
}).then(() => {
|
|
||||||
modal = this.domUtils.showModalLoading('core.sending', true);
|
modal = this.domUtils.showModalLoading('core.sending', true);
|
||||||
|
|
||||||
return this.prepareSubmissionData(inputData).then((pluginData) => {
|
const pluginData = await this.prepareSubmissionData(inputData);
|
||||||
if (!Object.keys(pluginData).length) {
|
if (!Object.keys(pluginData).length) {
|
||||||
// Nothing to save.
|
// Nothing to save.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let promise;
|
let sent: boolean;
|
||||||
|
|
||||||
if (this.saveOffline) {
|
if (this.saveOffline) {
|
||||||
// Save submission in offline.
|
// Save submission in offline.
|
||||||
promise = this.assignOfflineProvider.saveSubmission(this.assign.id, this.courseId, pluginData,
|
sent = false;
|
||||||
this.userSubmission.timemodified, !this.assign.submissiondrafts, this.userId);
|
await this.assignOfflineProvider.saveSubmission(this.assign.id, this.courseId, pluginData,
|
||||||
} else {
|
this.userSubmission.timemodified, !this.assign.submissiondrafts, this.userId);
|
||||||
// Try to send it to server.
|
} else {
|
||||||
promise = this.assignProvider.saveSubmission(this.assign.id, this.courseId, pluginData, this.allowOffline,
|
// Try to send it to server.
|
||||||
this.userSubmission.timemodified, !!this.assign.submissiondrafts, this.userId);
|
sent = await this.assignProvider.saveSubmission(this.assign.id, this.courseId, pluginData, this.allowOffline,
|
||||||
}
|
this.userSubmission.timemodified, !!this.assign.submissiondrafts, this.userId);
|
||||||
|
}
|
||||||
|
|
||||||
return promise.then(() => {
|
// Clear temporary data from plugins.
|
||||||
// Clear temporary data from plugins.
|
await this.assignHelper.clearSubmissionPluginTmpData(this.assign, this.userSubmission, inputData);
|
||||||
return this.assignHelper.clearSubmissionPluginTmpData(this.assign, this.userSubmission, inputData);
|
|
||||||
}).then(() => {
|
|
||||||
// Submission saved, trigger event.
|
|
||||||
const params = {
|
|
||||||
assignmentId: this.assign.id,
|
|
||||||
submissionId: this.userSubmission.id,
|
|
||||||
userId: this.userId,
|
|
||||||
};
|
|
||||||
|
|
||||||
this.eventsProvider.trigger(AddonModAssignProvider.SUBMISSION_SAVED_EVENT, params,
|
// Submission saved, trigger events.
|
||||||
this.sitesProvider.getCurrentSiteId());
|
this.domUtils.triggerFormSubmittedEvent(this.formElement, sent, this.sitesProvider.getCurrentSiteId());
|
||||||
|
|
||||||
if (!this.assign.submissiondrafts) {
|
const params = {
|
||||||
// No drafts allowed, so it was submitted. Trigger event.
|
assignmentId: this.assign.id,
|
||||||
this.eventsProvider.trigger(AddonModAssignProvider.SUBMITTED_FOR_GRADING_EVENT, params,
|
submissionId: this.userSubmission.id,
|
||||||
this.sitesProvider.getCurrentSiteId());
|
userId: this.userId,
|
||||||
}
|
};
|
||||||
});
|
|
||||||
});
|
this.eventsProvider.trigger(AddonModAssignProvider.SUBMISSION_SAVED_EVENT, params,
|
||||||
}).finally(() => {
|
this.sitesProvider.getCurrentSiteId());
|
||||||
|
|
||||||
|
if (!this.assign.submissiondrafts) {
|
||||||
|
// No drafts allowed, so it was submitted. Trigger event.
|
||||||
|
this.eventsProvider.trigger(AddonModAssignProvider.SUBMITTED_FOR_GRADING_EVENT, params,
|
||||||
|
this.sitesProvider.getCurrentSiteId());
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
modal.dismiss();
|
modal.dismiss();
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -354,15 +354,7 @@ export class AddonModAssignPrefetchHandler extends CoreCourseActivityPrefetchHan
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
// Participiants already fetched, we don't need to ignore cache now.
|
// Participiants already fetched, we don't need to ignore cache now.
|
||||||
return this.assignHelper.getParticipants(assign, group.id, false, siteId).then((participants) => {
|
return this.assignHelper.getParticipants(assign, group.id, false, siteId).then((participants) => {
|
||||||
const promises = [];
|
return this.userProvider.prefetchUserAvatars(participants, 'profileimageurl', siteId);
|
||||||
|
|
||||||
participants.forEach((participant) => {
|
|
||||||
if (participant.profileimageurl) {
|
|
||||||
promises.push(this.filepoolProvider.addToQueueByUrl(siteId, participant.profileimageurl));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return Promise.all(promises);
|
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
// Fail silently (Moodle < 3.2).
|
// Fail silently (Moodle < 3.2).
|
||||||
});
|
});
|
||||||
|
@ -443,7 +435,11 @@ export class AddonModAssignPrefetchHandler extends CoreCourseActivityPrefetchHan
|
||||||
|
|
||||||
// Prefetch grade items.
|
// Prefetch grade items.
|
||||||
if (userId) {
|
if (userId) {
|
||||||
promises.push(this.gradesHelper.getGradeModuleItems(courseId, moduleId, userId, undefined, siteId, true));
|
promises.push(this.courseProvider.getModuleBasicGradeInfo(moduleId, siteId).then((gradeInfo) => {
|
||||||
|
if (gradeInfo) {
|
||||||
|
promises.push(this.gradesHelper.getGradeModuleItems(courseId, moduleId, userId, undefined, siteId, true));
|
||||||
|
}
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefetch feedback.
|
// Prefetch feedback.
|
||||||
|
|
|
@ -15,6 +15,6 @@
|
||||||
<p>{{ 'core.numwords' | translate: {'$a': words + ' / ' + configs.wordlimit} }}</p>
|
<p>{{ 'core.numwords' | translate: {'$a': words + ' / ' + configs.wordlimit} }}</p>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item text-wrap>
|
<ion-item text-wrap>
|
||||||
<core-rich-text-editor item-content [control]="control" [placeholder]="plugin.name" name="onlinetext_editor_text" (contentChanged)="onChange($event)" [component]="component" [componentId]="assign.cmid"></core-rich-text-editor>
|
<core-rich-text-editor item-content [control]="control" [placeholder]="plugin.name" name="onlinetext_editor_text" (contentChanged)="onChange($event)" [component]="component" [componentId]="assign.cmid" [autoSave]="true" contextLevel="module" [contextInstanceId]="assign.cmid" elementId="onlinetext_editor" [draftExtraParams]="{userid: currentUserId, action: 'editsubmission'}"></core-rich-text-editor>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
import { Component, OnInit, ElementRef } from '@angular/core';
|
import { Component, OnInit, ElementRef } from '@angular/core';
|
||||||
import { FormBuilder, FormControl } from '@angular/forms';
|
import { FormBuilder, FormControl } from '@angular/forms';
|
||||||
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||||
import { AddonModAssignProvider } from '../../../providers/assign';
|
import { AddonModAssignProvider } from '../../../providers/assign';
|
||||||
|
@ -35,16 +36,23 @@ export class AddonModAssignSubmissionOnlineTextComponent extends AddonModAssignS
|
||||||
text: string;
|
text: string;
|
||||||
loaded: boolean;
|
loaded: boolean;
|
||||||
wordLimitEnabled: boolean;
|
wordLimitEnabled: boolean;
|
||||||
|
currentUserId: number;
|
||||||
|
|
||||||
protected wordCountTimeout: any;
|
protected wordCountTimeout: any;
|
||||||
protected element: HTMLElement;
|
protected element: HTMLElement;
|
||||||
|
|
||||||
constructor(protected fb: FormBuilder, protected domUtils: CoreDomUtilsProvider, protected textUtils: CoreTextUtilsProvider,
|
constructor(
|
||||||
protected assignProvider: AddonModAssignProvider, protected assignOfflineProvider: AddonModAssignOfflineProvider,
|
protected fb: FormBuilder,
|
||||||
element: ElementRef) {
|
protected domUtils: CoreDomUtilsProvider,
|
||||||
|
protected textUtils: CoreTextUtilsProvider,
|
||||||
|
protected assignProvider: AddonModAssignProvider,
|
||||||
|
protected assignOfflineProvider: AddonModAssignOfflineProvider,
|
||||||
|
element: ElementRef,
|
||||||
|
sitesProvider: CoreSitesProvider) {
|
||||||
|
|
||||||
super();
|
super();
|
||||||
this.element = element.nativeElement;
|
this.element = element.nativeElement;
|
||||||
|
this.currentUserId = sitesProvider.getCurrentSiteUserId();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -21,6 +21,7 @@ import { AddonModAssignSubmissionOnlineTextComponent } from './component/onlinet
|
||||||
import { AddonModAssignSubmissionDelegate } from '../../providers/submission-delegate';
|
import { AddonModAssignSubmissionDelegate } from '../../providers/submission-delegate';
|
||||||
import { CoreComponentsModule } from '@components/components.module';
|
import { CoreComponentsModule } from '@components/components.module';
|
||||||
import { CoreDirectivesModule } from '@directives/directives.module';
|
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||||
|
import { CoreEditorComponentsModule } from '@core/editor/components/components.module';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
|
@ -31,7 +32,8 @@ import { CoreDirectivesModule } from '@directives/directives.module';
|
||||||
IonicModule,
|
IonicModule,
|
||||||
TranslateModule.forChild(),
|
TranslateModule.forChild(),
|
||||||
CoreComponentsModule,
|
CoreComponentsModule,
|
||||||
CoreDirectivesModule
|
CoreDirectivesModule,
|
||||||
|
CoreEditorComponentsModule,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
AddonModAssignSubmissionOnlineTextHandler
|
AddonModAssignSubmissionOnlineTextHandler
|
||||||
|
|
|
@ -19,13 +19,13 @@
|
||||||
<core-course-module-description [description]="description" [component]="component" [componentId]="componentId" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId"></core-course-module-description>
|
<core-course-module-description [description]="description" [component]="component" [componentId]="componentId" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId"></core-course-module-description>
|
||||||
|
|
||||||
<div padding class="safe-padding-horizontal">
|
<div padding class="safe-padding-horizontal">
|
||||||
<core-navigation-bar [previous]="previousChapter > 0 && previousChapter" [next]="nextChapter > 0 && nextChapter" (action)="changeChapter($event)"></core-navigation-bar>
|
<core-navigation-bar *ngIf="displayNavBar" [previous]="previousChapter && previousChapter.id" [previousTitle]="previousNavBarTitle" [next]="nextChapter && nextChapter.id" [nextTitle]="nextNavBarTitle" (action)="changeChapter($event)"></core-navigation-bar>
|
||||||
<core-format-text [component]="component" [componentId]="componentId" [text]="chapterContent" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId"></core-format-text>
|
<core-format-text [component]="component" [componentId]="componentId" [text]="chapterContent" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId"></core-format-text>
|
||||||
<div margin-top *ngIf="tagsEnabled && contentsMap && contentsMap[currentChapter] && contentsMap[currentChapter].tags && contentsMap[currentChapter].tags.length > 0">
|
<div margin-top *ngIf="tagsEnabled && contentsMap && contentsMap[currentChapter] && contentsMap[currentChapter].tags && contentsMap[currentChapter].tags.length > 0">
|
||||||
<b>{{ 'core.tag.tags' | translate }}:</b>
|
<b>{{ 'core.tag.tags' | translate }}:</b>
|
||||||
<core-tag-list [tags]="contentsMap[currentChapter].tags"></core-tag-list>
|
<core-tag-list [tags]="contentsMap[currentChapter].tags"></core-tag-list>
|
||||||
</div>
|
</div>
|
||||||
<core-navigation-bar [previous]="previousChapter > 0 && previousChapter" [next]="nextChapter > 0 && nextChapter" (action)="changeChapter($event)"></core-navigation-bar>
|
<core-navigation-bar *ngIf="displayNavBar" [previous]="previousChapter && previousChapter.id" [previousTitle]="previousNavBarTitle" [next]="nextChapter && nextChapter.id" [nextTitle]="nextNavBarTitle" (action)="changeChapter($event)"></core-navigation-bar>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</core-loading>
|
</core-loading>
|
||||||
|
|
|
@ -17,7 +17,9 @@ import { Content, ModalController } from 'ionic-angular';
|
||||||
import { CoreAppProvider } from '@providers/app';
|
import { CoreAppProvider } from '@providers/app';
|
||||||
import { CoreCourseProvider } from '@core/course/providers/course';
|
import { CoreCourseProvider } from '@core/course/providers/course';
|
||||||
import { CoreCourseModuleMainResourceComponent } from '@core/course/classes/main-resource-component';
|
import { CoreCourseModuleMainResourceComponent } from '@core/course/classes/main-resource-component';
|
||||||
import { AddonModBookProvider, AddonModBookContentsMap, AddonModBookTocChapter } from '../../providers/book';
|
import {
|
||||||
|
AddonModBookProvider, AddonModBookContentsMap, AddonModBookTocChapter, AddonModBookBook, AddonModBookNavStyle
|
||||||
|
} from '../../providers/book';
|
||||||
import { AddonModBookPrefetchHandler } from '../../providers/prefetch-handler';
|
import { AddonModBookPrefetchHandler } from '../../providers/prefetch-handler';
|
||||||
import { CoreTagProvider } from '@core/tag/providers/tag';
|
import { CoreTagProvider } from '@core/tag/providers/tag';
|
||||||
|
|
||||||
|
@ -33,13 +35,18 @@ export class AddonModBookIndexComponent extends CoreCourseModuleMainResourceComp
|
||||||
|
|
||||||
component = AddonModBookProvider.COMPONENT;
|
component = AddonModBookProvider.COMPONENT;
|
||||||
chapterContent: string;
|
chapterContent: string;
|
||||||
previousChapter: string;
|
previousChapter: AddonModBookTocChapter;
|
||||||
nextChapter: string;
|
nextChapter: AddonModBookTocChapter;
|
||||||
tagsEnabled: boolean;
|
tagsEnabled: boolean;
|
||||||
|
displayNavBar = true;
|
||||||
|
previousNavBarTitle: string;
|
||||||
|
nextNavBarTitle: string;
|
||||||
|
|
||||||
protected chapters: AddonModBookTocChapter[];
|
protected chapters: AddonModBookTocChapter[];
|
||||||
protected currentChapter: string;
|
protected currentChapter: string;
|
||||||
protected contentsMap: AddonModBookContentsMap;
|
protected contentsMap: AddonModBookContentsMap;
|
||||||
|
protected book: AddonModBookBook;
|
||||||
|
protected displayTitlesInNavBar = false;
|
||||||
|
|
||||||
constructor(injector: Injector, private bookProvider: AddonModBookProvider, private courseProvider: CoreCourseProvider,
|
constructor(injector: Injector, private bookProvider: AddonModBookProvider, private courseProvider: CoreCourseProvider,
|
||||||
private appProvider: CoreAppProvider, private prefetchDelegate: AddonModBookPrefetchHandler,
|
private appProvider: CoreAppProvider, private prefetchDelegate: AddonModBookPrefetchHandler,
|
||||||
|
@ -69,7 +76,8 @@ export class AddonModBookIndexComponent extends CoreCourseModuleMainResourceComp
|
||||||
moduleId: this.module.id,
|
moduleId: this.module.id,
|
||||||
chapters: this.chapters,
|
chapters: this.chapters,
|
||||||
selected: this.currentChapter,
|
selected: this.currentChapter,
|
||||||
courseId: this.courseId
|
courseId: this.courseId,
|
||||||
|
book: this.book,
|
||||||
}, { cssClass: 'core-modal-lateral',
|
}, { cssClass: 'core-modal-lateral',
|
||||||
showBackdrop: true,
|
showBackdrop: true,
|
||||||
enableBackdropDismiss: true,
|
enableBackdropDismiss: true,
|
||||||
|
@ -97,7 +105,7 @@ export class AddonModBookIndexComponent extends CoreCourseModuleMainResourceComp
|
||||||
if (chapterId && chapterId != this.currentChapter) {
|
if (chapterId && chapterId != this.currentChapter) {
|
||||||
this.loaded = false;
|
this.loaded = false;
|
||||||
this.refreshIcon = 'spinner';
|
this.refreshIcon = 'spinner';
|
||||||
this.loadChapter(chapterId);
|
this.loadChapter(chapterId, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,19 +127,24 @@ export class AddonModBookIndexComponent extends CoreCourseModuleMainResourceComp
|
||||||
protected fetchContent(refresh?: boolean): Promise<any> {
|
protected fetchContent(refresh?: boolean): Promise<any> {
|
||||||
const promises = [];
|
const promises = [];
|
||||||
let downloadFailed = false;
|
let downloadFailed = false;
|
||||||
|
let downloadFailError;
|
||||||
|
|
||||||
// Try to get the book data.
|
// Try to get the book data.
|
||||||
promises.push(this.bookProvider.getBook(this.courseId, this.module.id).then((book) => {
|
promises.push(this.bookProvider.getBook(this.courseId, this.module.id).then((book) => {
|
||||||
|
this.book = book;
|
||||||
this.dataRetrieved.emit(book);
|
this.dataRetrieved.emit(book);
|
||||||
this.description = book.intro;
|
this.description = book.intro;
|
||||||
|
this.displayNavBar = book.navstyle != AddonModBookNavStyle.TOC_ONLY;
|
||||||
|
this.displayTitlesInNavBar = book.navstyle == AddonModBookNavStyle.TEXT;
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
// Ignore errors since this WS isn't available in some Moodle versions.
|
// Ignore errors since this WS isn't available in some Moodle versions.
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Download content. This function also loads module contents if needed.
|
// Download content. This function also loads module contents if needed.
|
||||||
promises.push(this.prefetchDelegate.download(this.module, this.courseId).catch(() => {
|
promises.push(this.prefetchDelegate.download(this.module, this.courseId).catch((error) => {
|
||||||
// Mark download as failed but go on since the main files could have been downloaded.
|
// Mark download as failed but go on since the main files could have been downloaded.
|
||||||
downloadFailed = true;
|
downloadFailed = true;
|
||||||
|
downloadFailError = error;
|
||||||
|
|
||||||
if (!this.module.contents.length) {
|
if (!this.module.contents.length) {
|
||||||
// Try to load module contents for offline usage.
|
// Try to load module contents for offline usage.
|
||||||
|
@ -160,10 +173,10 @@ export class AddonModBookIndexComponent extends CoreCourseModuleMainResourceComp
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show chapter.
|
// Show chapter.
|
||||||
return this.loadChapter(this.currentChapter).then(() => {
|
return this.loadChapter(this.currentChapter, refresh).then(() => {
|
||||||
if (downloadFailed && this.appProvider.isOnline()) {
|
if (downloadFailed && this.appProvider.isOnline()) {
|
||||||
// We could load the main file but the download failed. Show error message.
|
// We could load the main file but the download failed. Show error message.
|
||||||
this.domUtils.showErrorModal('core.errordownloadingsomefiles', true);
|
this.showErrorDownloadingSomeFiles(downloadFailError);
|
||||||
}
|
}
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
// Ignore errors, they're handled inside the loadChapter function.
|
// Ignore errors, they're handled inside the loadChapter function.
|
||||||
|
@ -177,9 +190,10 @@ export class AddonModBookIndexComponent extends CoreCourseModuleMainResourceComp
|
||||||
* Load a book chapter.
|
* Load a book chapter.
|
||||||
*
|
*
|
||||||
* @param chapterId Chapter to load.
|
* @param chapterId Chapter to load.
|
||||||
|
* @param logChapterId Whether chapter ID should be passed to the log view function.
|
||||||
* @return Promise resolved when done.
|
* @return Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
protected loadChapter(chapterId: string): Promise<void> {
|
protected loadChapter(chapterId: string, logChapterId: boolean): Promise<void> {
|
||||||
this.currentChapter = chapterId;
|
this.currentChapter = chapterId;
|
||||||
this.domUtils.scrollToTop(this.content);
|
this.domUtils.scrollToTop(this.content);
|
||||||
|
|
||||||
|
@ -188,10 +202,15 @@ export class AddonModBookIndexComponent extends CoreCourseModuleMainResourceComp
|
||||||
this.previousChapter = this.bookProvider.getPreviousChapter(this.chapters, chapterId);
|
this.previousChapter = this.bookProvider.getPreviousChapter(this.chapters, chapterId);
|
||||||
this.nextChapter = this.bookProvider.getNextChapter(this.chapters, chapterId);
|
this.nextChapter = this.bookProvider.getNextChapter(this.chapters, chapterId);
|
||||||
|
|
||||||
|
this.previousNavBarTitle = this.previousChapter && this.displayTitlesInNavBar ?
|
||||||
|
this.translate.instant('addon.mod_book.navprevtitle', {$a: this.previousChapter.title}) : '';
|
||||||
|
this.nextNavBarTitle = this.nextChapter && this.displayTitlesInNavBar ?
|
||||||
|
this.translate.instant('addon.mod_book.navnexttitle', {$a: this.nextChapter.title}) : '';
|
||||||
|
|
||||||
// Chapter loaded, log view. We don't return the promise because we don't want to block the user for this.
|
// Chapter loaded, log view. We don't return the promise because we don't want to block the user for this.
|
||||||
this.bookProvider.logView(this.module.instance, chapterId, this.module.name).then(() => {
|
this.bookProvider.logView(this.module.instance, logChapterId ? chapterId : undefined, this.module.name).then(() => {
|
||||||
// Module is completed when last chapter is viewed, so we only check completion if the last is reached.
|
// Module is completed when last chapter is viewed, so we only check completion if the last is reached.
|
||||||
if (this.nextChapter == '0') {
|
if (!this.nextChapter) {
|
||||||
this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata);
|
this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata);
|
||||||
}
|
}
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
{
|
{
|
||||||
"errorchapter": "Error reading chapter of book.",
|
"errorchapter": "Error reading chapter of book.",
|
||||||
"modulenameplural": "Books",
|
"modulenameplural": "Books",
|
||||||
|
"navnexttitle": "Next: {{$a}}",
|
||||||
|
"navprevtitle": "Previous: {{$a}}",
|
||||||
"tagarea_book_chapters": "Book chapters",
|
"tagarea_book_chapters": "Book chapters",
|
||||||
"toc": "Table of contents"
|
"toc": "Table of contents"
|
||||||
}
|
}
|
|
@ -12,7 +12,11 @@
|
||||||
<nav>
|
<nav>
|
||||||
<ion-list>
|
<ion-list>
|
||||||
<a ion-item text-wrap *ngFor="let chapter of chapters" (click)="loadChapter(chapter.id)" [class.core-nav-item-selected]="selected == chapter.id" [class.item-dimmed]="chapter.hidden">
|
<a ion-item text-wrap *ngFor="let chapter of chapters" (click)="loadChapter(chapter.id)" [class.core-nav-item-selected]="selected == chapter.id" [class.item-dimmed]="chapter.hidden">
|
||||||
<p [attr.padding-left]="chapter.level == 1 ? true : null">{{chapter.number}} <core-format-text [text]="chapter.title" contextLevel="module" [contextInstanceId]="moduleId" [courseId]="courseId"></core-format-text></p>
|
<p [attr.padding-left]="addPadding && chapter.level == 1 ? true : null">
|
||||||
|
<span *ngIf="showNumbers" class="addon-mod-book-number">{{chapter.number}}</span>
|
||||||
|
<span *ngIf="showBullets" class="addon-mod-book-bullet">•</span>
|
||||||
|
<core-format-text [text]="chapter.title" contextLevel="module" [contextInstanceId]="moduleId" [courseId]="courseId"></core-format-text>
|
||||||
|
</p>
|
||||||
</a>
|
</a>
|
||||||
</ion-list>
|
</ion-list>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
ion-app.app-root page-addon-mod-book-toc {
|
||||||
|
.addon-mod-book-bullet {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.5em;
|
||||||
|
margin-right: 3px;
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { IonicPage, NavParams, ViewController } from 'ionic-angular';
|
import { IonicPage, NavParams, ViewController } from 'ionic-angular';
|
||||||
import { AddonModBookTocChapter } from '../../providers/book';
|
import { AddonModBookTocChapter, AddonModBookBook, AddonModBookNumbering } from '../../providers/book';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Modal to display the TOC of a book.
|
* Modal to display the TOC of a book.
|
||||||
|
@ -29,12 +29,24 @@ export class AddonModBookTocPage {
|
||||||
chapters: AddonModBookTocChapter[];
|
chapters: AddonModBookTocChapter[];
|
||||||
selected: number;
|
selected: number;
|
||||||
courseId: number;
|
courseId: number;
|
||||||
|
showNumbers = true;
|
||||||
|
addPadding = true;
|
||||||
|
showBullets = false;
|
||||||
|
|
||||||
|
protected book: AddonModBookBook;
|
||||||
|
|
||||||
constructor(navParams: NavParams, private viewCtrl: ViewController) {
|
constructor(navParams: NavParams, private viewCtrl: ViewController) {
|
||||||
this.moduleId = navParams.get('moduleId');
|
this.moduleId = navParams.get('moduleId');
|
||||||
this.chapters = navParams.get('chapters') || [];
|
this.chapters = navParams.get('chapters') || [];
|
||||||
this.selected = navParams.get('selected');
|
this.selected = navParams.get('selected');
|
||||||
this.courseId = navParams.get('courseId');
|
this.courseId = navParams.get('courseId');
|
||||||
|
this.book = navParams.get('book');
|
||||||
|
|
||||||
|
if (this.book) {
|
||||||
|
this.showNumbers = this.book.numbering == AddonModBookNumbering.NUMBERS;
|
||||||
|
this.showBullets = this.book.numbering == AddonModBookNumbering.BULLETS;
|
||||||
|
this.addPadding = this.book.numbering != AddonModBookNumbering.NONE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Http, Response } from '@angular/http';
|
|
||||||
import { CoreFileProvider } from '@providers/file';
|
import { CoreFileProvider } from '@providers/file';
|
||||||
import { CoreFilepoolProvider } from '@providers/filepool';
|
import { CoreFilepoolProvider } from '@providers/filepool';
|
||||||
import { CoreLoggerProvider } from '@providers/logger';
|
import { CoreLoggerProvider } from '@providers/logger';
|
||||||
|
@ -25,7 +24,26 @@ import { CoreCourseProvider } from '@core/course/providers/course';
|
||||||
import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper';
|
import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper';
|
||||||
import { CoreSite } from '@classes/site';
|
import { CoreSite } from '@classes/site';
|
||||||
import { CoreTagItem } from '@core/tag/providers/tag';
|
import { CoreTagItem } from '@core/tag/providers/tag';
|
||||||
import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws';
|
import { CoreWSProvider, CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constants to define how the chapters and subchapters of a book should be displayed in that table of contents.
|
||||||
|
*/
|
||||||
|
export const enum AddonModBookNumbering {
|
||||||
|
NONE = 0,
|
||||||
|
NUMBERS = 1,
|
||||||
|
BULLETS = 2,
|
||||||
|
INDENTED = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constants to define the navigation style used within a book.
|
||||||
|
*/
|
||||||
|
export const enum AddonModBookNavStyle {
|
||||||
|
TOC_ONLY = 0,
|
||||||
|
IMAGE = 1,
|
||||||
|
TEXT = 2,
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service that provides some features for books.
|
* Service that provides some features for books.
|
||||||
|
@ -37,10 +55,16 @@ export class AddonModBookProvider {
|
||||||
protected ROOT_CACHE_KEY = 'mmaModBook:';
|
protected ROOT_CACHE_KEY = 'mmaModBook:';
|
||||||
protected logger;
|
protected logger;
|
||||||
|
|
||||||
constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private textUtils: CoreTextUtilsProvider,
|
constructor(logger: CoreLoggerProvider,
|
||||||
private fileProvider: CoreFileProvider, private filepoolProvider: CoreFilepoolProvider, private http: Http,
|
protected sitesProvider: CoreSitesProvider,
|
||||||
private utils: CoreUtilsProvider, private courseProvider: CoreCourseProvider, private domUtils: CoreDomUtilsProvider,
|
protected textUtils: CoreTextUtilsProvider,
|
||||||
private logHelper: CoreCourseLogHelperProvider) {
|
protected fileProvider: CoreFileProvider,
|
||||||
|
protected filepoolProvider: CoreFilepoolProvider,
|
||||||
|
protected wsProvider: CoreWSProvider,
|
||||||
|
protected utils: CoreUtilsProvider,
|
||||||
|
protected courseProvider: CoreCourseProvider,
|
||||||
|
protected domUtils: CoreDomUtilsProvider,
|
||||||
|
protected logHelper: CoreCourseLogHelperProvider) {
|
||||||
this.logger = logger.getInstance('AddonModBookProvider');
|
this.logger = logger.getInstance('AddonModBookProvider');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,19 +152,11 @@ export class AddonModBookProvider {
|
||||||
return this.sitesProvider.getCurrentSite().checkAndFixPluginfileURL(indexUrl);
|
return this.sitesProvider.getCurrentSite().checkAndFixPluginfileURL(indexUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
return promise.then((url) => {
|
return promise.then(async (url) => {
|
||||||
// Fetch the URL content.
|
const content = await this.wsProvider.getText(url);
|
||||||
const promise = this.http.get(url).toPromise();
|
|
||||||
|
|
||||||
return promise.then((response: Response): any => {
|
// Now that we have the content, we update the SRC to point back to the external resource.
|
||||||
const content = response.text();
|
return this.domUtils.restoreSourcesInHtml(content, contentsMap[chapterId].paths);
|
||||||
if (typeof content !== 'string') {
|
|
||||||
return Promise.reject(null);
|
|
||||||
} else {
|
|
||||||
// Now that we have the content, we update the SRC to point back to the external resource.
|
|
||||||
return this.domUtils.restoreSourcesInHtml(content, contentsMap[chapterId].paths);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,15 +234,15 @@ export class AddonModBookProvider {
|
||||||
*
|
*
|
||||||
* @param chapters The chapters list.
|
* @param chapters The chapters list.
|
||||||
* @param chapterId The current chapter.
|
* @param chapterId The current chapter.
|
||||||
* @return The next chapter id.
|
* @return The next chapter.
|
||||||
*/
|
*/
|
||||||
getNextChapter(chapters: AddonModBookTocChapter[], chapterId: string): string {
|
getNextChapter(chapters: AddonModBookTocChapter[], chapterId: string): AddonModBookTocChapter {
|
||||||
let next = '0';
|
let next: AddonModBookTocChapter;
|
||||||
|
|
||||||
for (let i = 0; i < chapters.length; i++) {
|
for (let i = 0; i < chapters.length; i++) {
|
||||||
if (chapters[i].id == chapterId) {
|
if (chapters[i].id == chapterId) {
|
||||||
if (typeof chapters[i + 1] != 'undefined') {
|
if (typeof chapters[i + 1] != 'undefined') {
|
||||||
next = chapters[i + 1].id;
|
next = chapters[i + 1];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -240,16 +256,16 @@ export class AddonModBookProvider {
|
||||||
*
|
*
|
||||||
* @param chapters The chapters list.
|
* @param chapters The chapters list.
|
||||||
* @param chapterId The current chapter.
|
* @param chapterId The current chapter.
|
||||||
* @return The next chapter id.
|
* @return The next chapter.
|
||||||
*/
|
*/
|
||||||
getPreviousChapter(chapters: AddonModBookTocChapter[], chapterId: string): string {
|
getPreviousChapter(chapters: AddonModBookTocChapter[], chapterId: string): AddonModBookTocChapter {
|
||||||
let previous = '0';
|
let previous: AddonModBookTocChapter;
|
||||||
|
|
||||||
for (let i = 0; i < chapters.length; i++) {
|
for (let i = 0; i < chapters.length; i++) {
|
||||||
if (chapters[i].id == chapterId) {
|
if (chapters[i].id == chapterId) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
previous = chapters[i].id;
|
previous = chapters[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
return previous;
|
return previous;
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
<ion-label id="addon-chat-showalllabel">{{ 'addon.mod_chat.showincompletesessions' | translate }}</ion-label>
|
<ion-label id="addon-chat-showalllabel">{{ 'addon.mod_chat.showincompletesessions' | translate }}</ion-label>
|
||||||
<ion-toggle [(ngModel)]="showAll" (ionChange)="fetchSessions(true)" aria-labelledby="addon-chat-showalllabel"></ion-toggle>
|
<ion-toggle [(ngModel)]="showAll" (ionChange)="fetchSessions(true)" aria-labelledby="addon-chat-showalllabel"></ion-toggle>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-card *ngFor="let session of sessions" (click)="openSession(session)"
|
<ion-card *ngFor="let session of sessions" (click)="openSession(session)" class="core-clickable"
|
||||||
[class.addon-mod-chat-session-selected]="session.sessionstart == selectedSessionStart && groupId == selectedSessionGroupId"
|
[class.addon-mod-chat-session-selected]="session.sessionstart == selectedSessionStart && groupId == selectedSessionGroupId"
|
||||||
[class.addon-mod-chat-session-show-more]="session.sessionusers.length < session.allsessionusers.length">
|
[class.addon-mod-chat-session-show-more]="session.sessionusers.length < session.allsessionusers.length">
|
||||||
<ion-item text-wrap>
|
<ion-item text-wrap>
|
||||||
|
|
|
@ -182,9 +182,7 @@ export class AddonModChatPrefetchHandler extends CoreCourseActivityPrefetchHandl
|
||||||
});
|
});
|
||||||
const userIds = Object.keys(users).map(Number);
|
const userIds = Object.keys(users).map(Number);
|
||||||
|
|
||||||
return this.userProvider.prefetchProfiles(userIds, courseId, siteId).catch(() => {
|
return this.userProvider.prefetchProfiles(userIds, courseId, siteId);
|
||||||
// Ignore errors, some users might not exist.
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,6 @@ import { AddonModChoicePrefetchHandler } from './providers/prefetch-handler';
|
||||||
import { AddonModChoiceSyncProvider } from './providers/sync';
|
import { AddonModChoiceSyncProvider } from './providers/sync';
|
||||||
import { AddonModChoiceSyncCronHandler } from './providers/sync-cron-handler';
|
import { AddonModChoiceSyncCronHandler } from './providers/sync-cron-handler';
|
||||||
import { AddonModChoiceOfflineProvider } from './providers/offline';
|
import { AddonModChoiceOfflineProvider } from './providers/offline';
|
||||||
import { CoreUpdateManagerProvider } from '@providers/update-manager';
|
|
||||||
|
|
||||||
// List of providers (without handlers).
|
// List of providers (without handlers).
|
||||||
export const ADDON_MOD_CHOICE_PROVIDERS: any[] = [
|
export const ADDON_MOD_CHOICE_PROVIDERS: any[] = [
|
||||||
|
@ -56,7 +55,7 @@ export class AddonModChoiceModule {
|
||||||
constructor(moduleDelegate: CoreCourseModuleDelegate, moduleHandler: AddonModChoiceModuleHandler,
|
constructor(moduleDelegate: CoreCourseModuleDelegate, moduleHandler: AddonModChoiceModuleHandler,
|
||||||
prefetchDelegate: CoreCourseModulePrefetchDelegate, prefetchHandler: AddonModChoicePrefetchHandler,
|
prefetchDelegate: CoreCourseModulePrefetchDelegate, prefetchHandler: AddonModChoicePrefetchHandler,
|
||||||
contentLinksDelegate: CoreContentLinksDelegate, linkHandler: AddonModChoiceLinkHandler,
|
contentLinksDelegate: CoreContentLinksDelegate, linkHandler: AddonModChoiceLinkHandler,
|
||||||
cronDelegate: CoreCronDelegate, syncHandler: AddonModChoiceSyncCronHandler, updateManager: CoreUpdateManagerProvider,
|
cronDelegate: CoreCronDelegate, syncHandler: AddonModChoiceSyncCronHandler,
|
||||||
listLinkHandler: AddonModChoiceListLinkHandler) {
|
listLinkHandler: AddonModChoiceListLinkHandler) {
|
||||||
|
|
||||||
moduleDelegate.registerHandler(moduleHandler);
|
moduleDelegate.registerHandler(moduleHandler);
|
||||||
|
@ -64,21 +63,5 @@ export class AddonModChoiceModule {
|
||||||
contentLinksDelegate.registerHandler(linkHandler);
|
contentLinksDelegate.registerHandler(linkHandler);
|
||||||
contentLinksDelegate.registerHandler(listLinkHandler);
|
contentLinksDelegate.registerHandler(listLinkHandler);
|
||||||
cronDelegate.register(syncHandler);
|
cronDelegate.register(syncHandler);
|
||||||
|
|
||||||
// Allow migrating the tables from the old app to the new schema.
|
|
||||||
updateManager.registerSiteTableMigration({
|
|
||||||
name: 'mma_mod_choice_offline_responses',
|
|
||||||
newName: AddonModChoiceOfflineProvider.CHOICE_TABLE,
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
name: 'responses',
|
|
||||||
type: 'object'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'deleting',
|
|
||||||
type: 'boolean'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,6 @@ import { AddonModDataDefaultFieldHandler } from './providers/default-field-handl
|
||||||
import { CoreTagAreaDelegate } from '@core/tag/providers/area-delegate';
|
import { CoreTagAreaDelegate } from '@core/tag/providers/area-delegate';
|
||||||
import { AddonModDataTagAreaHandler } from './providers/tag-area-handler';
|
import { AddonModDataTagAreaHandler } from './providers/tag-area-handler';
|
||||||
import { AddonModDataFieldModule } from './fields/field.module';
|
import { AddonModDataFieldModule } from './fields/field.module';
|
||||||
import { CoreUpdateManagerProvider } from '@providers/update-manager';
|
|
||||||
|
|
||||||
// List of providers (without handlers).
|
// List of providers (without handlers).
|
||||||
export const ADDON_MOD_DATA_PROVIDERS: any[] = [
|
export const ADDON_MOD_DATA_PROVIDERS: any[] = [
|
||||||
|
@ -77,7 +76,7 @@ export class AddonModDataModule {
|
||||||
constructor(moduleDelegate: CoreCourseModuleDelegate, moduleHandler: AddonModDataModuleHandler,
|
constructor(moduleDelegate: CoreCourseModuleDelegate, moduleHandler: AddonModDataModuleHandler,
|
||||||
prefetchDelegate: CoreCourseModulePrefetchDelegate, prefetchHandler: AddonModDataPrefetchHandler,
|
prefetchDelegate: CoreCourseModulePrefetchDelegate, prefetchHandler: AddonModDataPrefetchHandler,
|
||||||
contentLinksDelegate: CoreContentLinksDelegate, linkHandler: AddonModDataLinkHandler,
|
contentLinksDelegate: CoreContentLinksDelegate, linkHandler: AddonModDataLinkHandler,
|
||||||
cronDelegate: CoreCronDelegate, syncHandler: AddonModDataSyncCronHandler, updateManager: CoreUpdateManagerProvider,
|
cronDelegate: CoreCronDelegate, syncHandler: AddonModDataSyncCronHandler,
|
||||||
approveLinkHandler: AddonModDataApproveLinkHandler, deleteLinkHandler: AddonModDataDeleteLinkHandler,
|
approveLinkHandler: AddonModDataApproveLinkHandler, deleteLinkHandler: AddonModDataDeleteLinkHandler,
|
||||||
showLinkHandler: AddonModDataShowLinkHandler, editLinkHandler: AddonModDataEditLinkHandler,
|
showLinkHandler: AddonModDataShowLinkHandler, editLinkHandler: AddonModDataEditLinkHandler,
|
||||||
listLinkHandler: AddonModDataListLinkHandler, tagAreaDelegate: CoreTagAreaDelegate,
|
listLinkHandler: AddonModDataListLinkHandler, tagAreaDelegate: CoreTagAreaDelegate,
|
||||||
|
@ -93,21 +92,5 @@ export class AddonModDataModule {
|
||||||
contentLinksDelegate.registerHandler(listLinkHandler);
|
contentLinksDelegate.registerHandler(listLinkHandler);
|
||||||
cronDelegate.register(syncHandler);
|
cronDelegate.register(syncHandler);
|
||||||
tagAreaDelegate.registerHandler(tagAreaHandler);
|
tagAreaDelegate.registerHandler(tagAreaHandler);
|
||||||
|
|
||||||
// Allow migrating the tables from the old app to the new schema.
|
|
||||||
updateManager.registerSiteTableMigration({
|
|
||||||
name: 'mma_mod_data_entry',
|
|
||||||
newName: AddonModDataOfflineProvider.DATA_ENTRY_TABLE,
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
name: 'fields',
|
|
||||||
type: 'object'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'dataAndEntry',
|
|
||||||
delete: true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,14 +107,6 @@ page-addon-mod-data-edit {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-wp input {
|
|
||||||
@include padding-horizontal(null, ($item-wp-padding-end / 2));
|
|
||||||
border-bottom: 1px solid $list-wp-border-color;
|
|
||||||
&:focus {
|
|
||||||
border-color: $text-input-wp-highlight-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ion-select {
|
ion-select {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@include position(null, null, null, 0);
|
@include position(null, null, null, 0);
|
||||||
|
|
|
@ -40,7 +40,7 @@ import { AddonModDataFieldUrlModule } from './url/url.module';
|
||||||
AddonModDataFieldRadiobuttonModule,
|
AddonModDataFieldRadiobuttonModule,
|
||||||
AddonModDataFieldTextModule,
|
AddonModDataFieldTextModule,
|
||||||
AddonModDataFieldTextareaModule,
|
AddonModDataFieldTextareaModule,
|
||||||
AddonModDataFieldUrlModule
|
AddonModDataFieldUrlModule,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
],
|
],
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
<ion-input type="text" [formControlName]="'f_'+field.id+'_1'" maxlength="10"></ion-input>
|
<ion-input type="text" [formControlName]="'f_'+field.id+'_1'" maxlength="10"></ion-input>
|
||||||
<span class="placeholder-icon" item-right>°E</span>
|
<span class="placeholder-icon" item-right>°E</span>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="mode == 'edit'" class="addon-data-lantlong">
|
<div *ngIf="mode == 'edit' && showGeolocation" class="addon-data-lantlong">
|
||||||
<button ion-button icon-left (click)="getLocation($event)">
|
<button ion-button icon-left (click)="getLocation($event)">
|
||||||
<ion-icon name="locate"></ion-icon>
|
<ion-icon name="locate"></ion-icon>
|
||||||
{{ 'addon.mod_data.mylocation' | translate }}
|
{{ 'addon.mod_data.mylocation' | translate }}
|
||||||
|
|
|
@ -17,6 +17,7 @@ import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
|
||||||
import { Platform } from 'ionic-angular';
|
import { Platform } from 'ionic-angular';
|
||||||
import { Geolocation, GeolocationOptions } from '@ionic-native/geolocation';
|
import { Geolocation, GeolocationOptions } from '@ionic-native/geolocation';
|
||||||
import { AddonModDataFieldPluginComponent } from '../../../classes/field-plugin-component';
|
import { AddonModDataFieldPluginComponent } from '../../../classes/field-plugin-component';
|
||||||
|
import { CoreAppProvider } from '@providers/app';
|
||||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -30,13 +31,17 @@ export class AddonModDataFieldLatlongComponent extends AddonModDataFieldPluginCo
|
||||||
|
|
||||||
north: number;
|
north: number;
|
||||||
east: number;
|
east: number;
|
||||||
|
showGeolocation: boolean;
|
||||||
|
|
||||||
constructor(protected fb: FormBuilder,
|
constructor(protected fb: FormBuilder,
|
||||||
protected platform: Platform,
|
protected platform: Platform,
|
||||||
protected geolocation: Geolocation,
|
protected geolocation: Geolocation,
|
||||||
protected domUtils: CoreDomUtilsProvider,
|
protected domUtils: CoreDomUtilsProvider,
|
||||||
protected sanitizer: DomSanitizer) {
|
protected sanitizer: DomSanitizer,
|
||||||
|
protected appProvider: CoreAppProvider) {
|
||||||
super(fb);
|
super(fb);
|
||||||
|
|
||||||
|
this.showGeolocation = !this.appProvider.isDesktop();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<ion-input *ngIf="mode == 'search'" type="text" [placeholder]="field.name" [formControlName]="'f_'+field.id"></ion-input>
|
<ion-input *ngIf="mode == 'search'" type="text" [placeholder]="field.name" [formControlName]="'f_'+field.id"></ion-input>
|
||||||
|
|
||||||
<span *ngIf="mode == 'edit'" [core-mark-required]="field.required" class="core-mark-required"></span>
|
<span *ngIf="mode == 'edit'" [core-mark-required]="field.required" class="core-mark-required"></span>
|
||||||
<core-rich-text-editor *ngIf="mode == 'edit'" item-content [control]="form.controls['f_'+field.id]" [placeholder]="field.name" [formControlName]="'f_'+field.id" [component]="component" [componentId]="componentId"></core-rich-text-editor>
|
<core-rich-text-editor *ngIf="mode == 'edit'" item-content [control]="form.controls['f_'+field.id]" [placeholder]="field.name" [formControlName]="'f_'+field.id" [component]="component" [componentId]="componentId" [autoSave]="true" contextLevel="module" [contextInstanceId]="componentId" [elementId]="'field_'+field.id"></core-rich-text-editor>
|
||||||
<core-input-errors *ngIf="error && mode == 'edit'" [control]="form.controls['f_'+field.id]" [errorText]="error"></core-input-errors>
|
<core-input-errors *ngIf="error && mode == 'edit'" [control]="form.controls['f_'+field.id]" [errorText]="error"></core-input-errors>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
|
|
@ -49,10 +49,10 @@ export class AddonModDataFieldTextareaComponent extends AddonModDataFieldPluginC
|
||||||
* Initialize field.
|
* Initialize field.
|
||||||
*/
|
*/
|
||||||
protected init(): void {
|
protected init(): void {
|
||||||
if (this.isShowOrListMode()) {
|
this.component = AddonModDataProvider.COMPONENT;
|
||||||
this.component = AddonModDataProvider.COMPONENT;
|
this.componentId = this.database.coursemodule;
|
||||||
this.componentId = this.database.coursemodule;
|
|
||||||
|
|
||||||
|
if (this.isShowOrListMode()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ import { AddonModDataFieldsDelegate } from '../../providers/fields-delegate';
|
||||||
import { AddonModDataFieldTextareaComponent } from './component/textarea';
|
import { AddonModDataFieldTextareaComponent } from './component/textarea';
|
||||||
import { CoreComponentsModule } from '@components/components.module';
|
import { CoreComponentsModule } from '@components/components.module';
|
||||||
import { CoreDirectivesModule } from '@directives/directives.module';
|
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||||
|
import { CoreEditorComponentsModule } from '@core/editor/components/components.module';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
|
@ -30,7 +31,8 @@ import { CoreDirectivesModule } from '@directives/directives.module';
|
||||||
IonicModule,
|
IonicModule,
|
||||||
TranslateModule.forChild(),
|
TranslateModule.forChild(),
|
||||||
CoreComponentsModule,
|
CoreComponentsModule,
|
||||||
CoreDirectivesModule
|
CoreDirectivesModule,
|
||||||
|
CoreEditorComponentsModule,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
AddonModDataFieldTextareaHandler
|
AddonModDataFieldTextareaHandler
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<div class="addon-data-contents addon-data-entries-{{data.id}}" *ngIf="data">
|
<div class="addon-data-contents addon-data-entries-{{data.id}}" *ngIf="data">
|
||||||
<core-style [css]="data.csstemplate" prefix=".addon-data-entries-{{data.id}}"></core-style>
|
<core-style [css]="data.csstemplate" prefix=".addon-data-entries-{{data.id}}"></core-style>
|
||||||
|
|
||||||
<form (ngSubmit)="save($event)" [formGroup]="editForm">
|
<form (ngSubmit)="save($event)" [formGroup]="editForm" #editFormEl>
|
||||||
<core-compile-html [text]="editFormRender" [jsData]="jsData" [extraImports]="extraImports"></core-compile-html>
|
<core-compile-html [text]="editFormRender" [jsData]="jsData" [extraImports]="extraImports"></core-compile-html>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { Component, ViewChild } from '@angular/core';
|
import { Component, ViewChild, ElementRef } from '@angular/core';
|
||||||
import { Content, IonicPage, NavParams, NavController } from 'ionic-angular';
|
import { Content, IonicPage, NavParams, NavController } from 'ionic-angular';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { FormGroup } from '@angular/forms';
|
import { FormGroup } from '@angular/forms';
|
||||||
|
@ -40,6 +40,7 @@ import { CoreTagProvider } from '@core/tag/providers/tag';
|
||||||
})
|
})
|
||||||
export class AddonModDataEditPage {
|
export class AddonModDataEditPage {
|
||||||
@ViewChild(Content) content: Content;
|
@ViewChild(Content) content: Content;
|
||||||
|
@ViewChild('editFormEl') formElement: ElementRef;
|
||||||
|
|
||||||
protected module: any;
|
protected module: any;
|
||||||
protected courseId: number;
|
protected courseId: number;
|
||||||
|
@ -95,28 +96,25 @@ export class AddonModDataEditPage {
|
||||||
*
|
*
|
||||||
* @return Resolved if we can leave it, rejected if not.
|
* @return Resolved if we can leave it, rejected if not.
|
||||||
*/
|
*/
|
||||||
ionViewCanLeave(): boolean | Promise<void> {
|
async ionViewCanLeave(): Promise<void> {
|
||||||
if (this.forceLeave || !this.entry) {
|
if (this.forceLeave || !this.entry) {
|
||||||
return true;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const inputData = this.editForm.value;
|
const inputData = this.editForm.value;
|
||||||
|
|
||||||
return this.dataHelper.hasEditDataChanged(inputData, this.fieldsArray, this.data.id,
|
const changed = await this.dataHelper.hasEditDataChanged(inputData, this.fieldsArray, this.data.id, this.entry.contents);
|
||||||
this.entry.contents).then((changed) => {
|
|
||||||
if (!changed) {
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (changed) {
|
||||||
// Show confirmation if some data has been modified.
|
// Show confirmation if some data has been modified.
|
||||||
return this.domUtils.showConfirm(this.translate.instant('core.confirmcanceledit'));
|
await this.domUtils.showConfirm(this.translate.instant('core.confirmcanceledit'));
|
||||||
}).then(() => {
|
}
|
||||||
// Delete the local files from the tmp folder.
|
|
||||||
return this.dataHelper.getEditTmpFiles(inputData, this.fieldsArray, this.data.id,
|
// Delete the local files from the tmp folder.
|
||||||
this.entry.contents).then((files) => {
|
const files = await this.dataHelper.getEditTmpFiles(inputData, this.fieldsArray, this.data.id, this.entry.contents);
|
||||||
this.fileUploaderProvider.clearTmpFiles(files);
|
this.fileUploaderProvider.clearTmpFiles(files);
|
||||||
});
|
|
||||||
});
|
this.domUtils.triggerFormCancelledEvent(this.formElement, this.siteId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -216,6 +214,9 @@ export class AddonModDataEditPage {
|
||||||
|
|
||||||
// This is done if entry is updated when editing or creating if not.
|
// This is done if entry is updated when editing or creating if not.
|
||||||
if ((this.entryId && result.updated) || (!this.entryId && result.newentryid)) {
|
if ((this.entryId && result.updated) || (!this.entryId && result.newentryid)) {
|
||||||
|
|
||||||
|
this.domUtils.triggerFormSubmittedEvent(this.formElement, result.sent, this.siteId);
|
||||||
|
|
||||||
const promises = [];
|
const promises = [];
|
||||||
|
|
||||||
this.entryId = this.entryId || result.newentryid;
|
this.entryId = this.entryId || result.newentryid;
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
<a class="tab-slide" [attr.aria-selected]="!search.searchingAdvanced" (click)="changeAdvanced(false)">{{ 'addon.mod_data.search' | translate}}</a>
|
<a class="tab-slide" [attr.aria-selected]="!search.searchingAdvanced" (click)="changeAdvanced(false)">{{ 'addon.mod_data.search' | translate}}</a>
|
||||||
<a class="tab-slide" [attr.aria-selected]="search.searchingAdvanced" (click)="changeAdvanced(true)">{{ 'addon.mod_data.advancedsearch' | translate }}</a>
|
<a class="tab-slide" [attr.aria-selected]="search.searchingAdvanced" (click)="changeAdvanced(true)">{{ 'addon.mod_data.advancedsearch' | translate }}</a>
|
||||||
</div>
|
</div>
|
||||||
<form (ngSubmit)="searchEntries($event)" [formGroup]="searchForm">
|
<form (ngSubmit)="searchEntries($event)" [formGroup]="searchForm" #searchFormEl>
|
||||||
<ion-list no-margin>
|
<ion-list no-margin>
|
||||||
<ion-item [hidden]="search.searchingAdvanced">
|
<ion-item [hidden]="search.searchingAdvanced">
|
||||||
<ion-input type="text" placeholder="{{ 'addon.mod_data.search' | translate}}" [(ngModel)]="search.text" name="text" formControlName="text"></ion-input>
|
<ion-input type="text" placeholder="{{ 'addon.mod_data.search' | translate}}" [(ngModel)]="search.text" name="text" formControlName="text"></ion-input>
|
||||||
|
|
|
@ -12,9 +12,11 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { Component } from '@angular/core';
|
import { Component, ViewChild, ElementRef } from '@angular/core';
|
||||||
import { IonicPage, NavParams, ViewController } from 'ionic-angular';
|
import { IonicPage, NavParams, ViewController } from 'ionic-angular';
|
||||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||||
|
import { CoreEventsProvider } from '@providers/events';
|
||||||
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||||
|
@ -32,6 +34,8 @@ import { CoreTagProvider } from '@core/tag/providers/tag';
|
||||||
templateUrl: 'search.html',
|
templateUrl: 'search.html',
|
||||||
})
|
})
|
||||||
export class AddonModDataSearchPage {
|
export class AddonModDataSearchPage {
|
||||||
|
@ViewChild('searchFormEl') formElement: ElementRef;
|
||||||
|
|
||||||
search: any;
|
search: any;
|
||||||
fields: any;
|
fields: any;
|
||||||
data: any;
|
data: any;
|
||||||
|
@ -41,10 +45,17 @@ export class AddonModDataSearchPage {
|
||||||
jsData: any;
|
jsData: any;
|
||||||
fieldsArray: any;
|
fieldsArray: any;
|
||||||
|
|
||||||
constructor(params: NavParams, private viewCtrl: ViewController, fb: FormBuilder, protected utils: CoreUtilsProvider,
|
constructor(params: NavParams,
|
||||||
protected domUtils: CoreDomUtilsProvider, protected fieldsDelegate: AddonModDataFieldsDelegate,
|
protected viewCtrl: ViewController,
|
||||||
protected textUtils: CoreTextUtilsProvider, protected dataHelper: AddonModDataHelperProvider,
|
fb: FormBuilder,
|
||||||
private tagProvider: CoreTagProvider) {
|
protected utils: CoreUtilsProvider,
|
||||||
|
protected domUtils: CoreDomUtilsProvider,
|
||||||
|
protected fieldsDelegate: AddonModDataFieldsDelegate,
|
||||||
|
protected textUtils: CoreTextUtilsProvider,
|
||||||
|
protected dataHelper: AddonModDataHelperProvider,
|
||||||
|
protected tagProvider: CoreTagProvider,
|
||||||
|
protected eventsProvider: CoreEventsProvider,
|
||||||
|
protected sitesProvider: CoreSitesProvider) {
|
||||||
this.search = params.get('search');
|
this.search = params.get('search');
|
||||||
this.fields = params.get('fields');
|
this.fields = params.get('fields');
|
||||||
this.data = params.get('data');
|
this.data = params.get('data');
|
||||||
|
@ -175,6 +186,12 @@ export class AddonModDataSearchPage {
|
||||||
* @param data Data to return to the page.
|
* @param data Data to return to the page.
|
||||||
*/
|
*/
|
||||||
closeModal(data?: any): void {
|
closeModal(data?: any): void {
|
||||||
|
if (typeof data == 'undefined') {
|
||||||
|
this.domUtils.triggerFormCancelledEvent(this.formElement, this.sitesProvider.getCurrentSiteId());
|
||||||
|
} else {
|
||||||
|
this.domUtils.triggerFormSubmittedEvent(this.formElement, false, this.sitesProvider.getCurrentSiteId());
|
||||||
|
}
|
||||||
|
|
||||||
this.viewCtrl.dismiss(data);
|
this.viewCtrl.dismiss(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -126,7 +126,8 @@ export class AddonModDataProvider {
|
||||||
.then((entry) => {
|
.then((entry) => {
|
||||||
return {
|
return {
|
||||||
// Return provissional entry Id.
|
// Return provissional entry Id.
|
||||||
newentryid: entry
|
newentryid: entry,
|
||||||
|
sent: false,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -142,7 +143,11 @@ export class AddonModDataProvider {
|
||||||
return storeOffline();
|
return storeOffline();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.addEntryOnline(dataId, contents, groupId, siteId).catch((error) => {
|
return this.addEntryOnline(dataId, contents, groupId, siteId).then((result) => {
|
||||||
|
result.sent = true;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}).catch((error) => {
|
||||||
if (this.utils.isWebServiceError(error)) {
|
if (this.utils.isWebServiceError(error)) {
|
||||||
// The WebService has thrown an error, this means that responses cannot be submitted.
|
// The WebService has thrown an error, this means that responses cannot be submitted.
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
|
@ -194,7 +199,12 @@ export class AddonModDataProvider {
|
||||||
const storeOffline = (): Promise<any> => {
|
const storeOffline = (): Promise<any> => {
|
||||||
const action = approve ? 'approve' : 'disapprove';
|
const action = approve ? 'approve' : 'disapprove';
|
||||||
|
|
||||||
return this.dataOffline.saveEntry(dataId, entryId, action, courseId, undefined, undefined, undefined, siteId);
|
return this.dataOffline.saveEntry(dataId, entryId, action, courseId, undefined, undefined, undefined, siteId)
|
||||||
|
.then(() => {
|
||||||
|
return {
|
||||||
|
sent: false,
|
||||||
|
};
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get if the opposite action is not synced.
|
// Get if the opposite action is not synced.
|
||||||
|
@ -210,7 +220,11 @@ export class AddonModDataProvider {
|
||||||
return storeOffline();
|
return storeOffline();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.approveEntryOnline(entryId, approve, siteId).catch((error) => {
|
return this.approveEntryOnline(entryId, approve, siteId).then(() => {
|
||||||
|
return {
|
||||||
|
sent: true,
|
||||||
|
};
|
||||||
|
}).catch((error) => {
|
||||||
if (this.utils.isWebServiceError(error)) {
|
if (this.utils.isWebServiceError(error)) {
|
||||||
// The WebService has thrown an error, this means that responses cannot be submitted.
|
// The WebService has thrown an error, this means that responses cannot be submitted.
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
|
@ -288,7 +302,12 @@ export class AddonModDataProvider {
|
||||||
|
|
||||||
// Convenience function to store a data to be synchronized later.
|
// Convenience function to store a data to be synchronized later.
|
||||||
const storeOffline = (): Promise<any> => {
|
const storeOffline = (): Promise<any> => {
|
||||||
return this.dataOffline.saveEntry(dataId, entryId, 'delete', courseId, undefined, undefined, undefined, siteId);
|
return this.dataOffline.saveEntry(dataId, entryId, 'delete', courseId, undefined, undefined, undefined, siteId)
|
||||||
|
.then(() => {
|
||||||
|
return {
|
||||||
|
sent: false,
|
||||||
|
};
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
let justAdded = false;
|
let justAdded = false;
|
||||||
|
@ -318,7 +337,11 @@ export class AddonModDataProvider {
|
||||||
return storeOffline();
|
return storeOffline();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.deleteEntryOnline(entryId, siteId).catch((error) => {
|
return this.deleteEntryOnline(entryId, siteId).then(() => {
|
||||||
|
return {
|
||||||
|
sent: true,
|
||||||
|
};
|
||||||
|
}).catch((error) => {
|
||||||
if (this.utils.isWebServiceError(error)) {
|
if (this.utils.isWebServiceError(error)) {
|
||||||
// The WebService has thrown an error, this means that responses cannot be submitted.
|
// The WebService has thrown an error, this means that responses cannot be submitted.
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
|
@ -368,7 +391,8 @@ export class AddonModDataProvider {
|
||||||
return this.dataOffline.saveEntry(dataId, entryId, 'edit', courseId, undefined, contents, undefined, siteId)
|
return this.dataOffline.saveEntry(dataId, entryId, 'edit', courseId, undefined, contents, undefined, siteId)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return {
|
return {
|
||||||
updated: true
|
updated: true,
|
||||||
|
sent: false,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -408,6 +432,7 @@ export class AddonModDataProvider {
|
||||||
return this.addEntry(dataId, entryId, courseId, contents, groupId, fields, siteId, forceOffline)
|
return this.addEntry(dataId, entryId, courseId, contents, groupId, fields, siteId, forceOffline)
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
result.updated = true;
|
result.updated = true;
|
||||||
|
result.sent = true;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
|
@ -418,7 +443,11 @@ export class AddonModDataProvider {
|
||||||
return storeOffline();
|
return storeOffline();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.editEntryOnline(entryId, contents, siteId).catch((error) => {
|
return this.editEntryOnline(entryId, contents, siteId).then((result) => {
|
||||||
|
result.sent = true;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}).catch((error) => {
|
||||||
if (this.utils.isWebServiceError(error)) {
|
if (this.utils.isWebServiceError(error)) {
|
||||||
// The WebService has thrown an error, this means that responses cannot be submitted.
|
// The WebService has thrown an error, this means that responses cannot be submitted.
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
|
|
|
@ -34,7 +34,6 @@ import { AddonModFeedbackPushClickHandler } from './providers/push-click-handler
|
||||||
import { AddonModFeedbackSyncProvider } from './providers/sync';
|
import { AddonModFeedbackSyncProvider } from './providers/sync';
|
||||||
import { AddonModFeedbackSyncCronHandler } from './providers/sync-cron-handler';
|
import { AddonModFeedbackSyncCronHandler } from './providers/sync-cron-handler';
|
||||||
import { AddonModFeedbackOfflineProvider } from './providers/offline';
|
import { AddonModFeedbackOfflineProvider } from './providers/offline';
|
||||||
import { CoreUpdateManagerProvider } from '@providers/update-manager';
|
|
||||||
|
|
||||||
// List of providers (without handlers).
|
// List of providers (without handlers).
|
||||||
export const ADDON_MOD_FEEDBACK_PROVIDERS: any[] = [
|
export const ADDON_MOD_FEEDBACK_PROVIDERS: any[] = [
|
||||||
|
@ -73,7 +72,7 @@ export class AddonModFeedbackModule {
|
||||||
prefetchDelegate: CoreCourseModulePrefetchDelegate, prefetchHandler: AddonModFeedbackPrefetchHandler,
|
prefetchDelegate: CoreCourseModulePrefetchDelegate, prefetchHandler: AddonModFeedbackPrefetchHandler,
|
||||||
contentLinksDelegate: CoreContentLinksDelegate, linkHandler: AddonModFeedbackLinkHandler,
|
contentLinksDelegate: CoreContentLinksDelegate, linkHandler: AddonModFeedbackLinkHandler,
|
||||||
cronDelegate: CoreCronDelegate, syncHandler: AddonModFeedbackSyncCronHandler,
|
cronDelegate: CoreCronDelegate, syncHandler: AddonModFeedbackSyncCronHandler,
|
||||||
analysisLinkHandler: AddonModFeedbackAnalysisLinkHandler, updateManager: CoreUpdateManagerProvider,
|
analysisLinkHandler: AddonModFeedbackAnalysisLinkHandler,
|
||||||
showEntriesLinkHandler: AddonModFeedbackShowEntriesLinkHandler,
|
showEntriesLinkHandler: AddonModFeedbackShowEntriesLinkHandler,
|
||||||
showNonRespondentsLinkHandler: AddonModFeedbackShowNonRespondentsLinkHandler,
|
showNonRespondentsLinkHandler: AddonModFeedbackShowNonRespondentsLinkHandler,
|
||||||
completeLinkHandler: AddonModFeedbackCompleteLinkHandler,
|
completeLinkHandler: AddonModFeedbackCompleteLinkHandler,
|
||||||
|
@ -91,17 +90,5 @@ export class AddonModFeedbackModule {
|
||||||
contentLinksDelegate.registerHandler(listLinkHandler);
|
contentLinksDelegate.registerHandler(listLinkHandler);
|
||||||
cronDelegate.register(syncHandler);
|
cronDelegate.register(syncHandler);
|
||||||
pushNotificationsDelegate.registerClickHandler(pushClickHandler);
|
pushNotificationsDelegate.registerClickHandler(pushClickHandler);
|
||||||
|
|
||||||
// Allow migrating the tables from the old app to the new schema.
|
|
||||||
updateManager.registerSiteTableMigration({
|
|
||||||
name: 'mma_mod_feedback_responses',
|
|
||||||
newName: AddonModFeedbackOfflineProvider.FEEDBACK_TABLE,
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
name: 'responses',
|
|
||||||
type: 'object'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,9 +9,6 @@ ion-app.app-root page-addon-mod-feedback-form {
|
||||||
.item-ios .addon-mod_feedback-form-content {
|
.item-ios .addon-mod_feedback-form-content {
|
||||||
@include margin($item-ios-padding-media-top, $item-ios-padding-start, $item-ios-padding-media-bottom, 0);
|
@include margin($item-ios-padding-media-top, $item-ios-padding-start, $item-ios-padding-media-bottom, 0);
|
||||||
}
|
}
|
||||||
.item-wp .addon-mod_feedback-form-content {
|
|
||||||
@include margin($item-wp-padding-media-top, ($item-wp-padding-end / 2), $item-wp-padding-media-bottom, 0);
|
|
||||||
}
|
|
||||||
.addon-mod_feedback-postfix {
|
.addon-mod_feedback-postfix {
|
||||||
font-size: 1.4rem;
|
font-size: 1.4rem;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate" [href]="externalUrl" [iconAction]="'open'"></core-context-menu-item>
|
<core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate" [href]="externalUrl" [iconAction]="'open'"></core-context-menu-item>
|
||||||
<core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate" (action)="expandDescription()" [iconAction]="'arrow-forward'"></core-context-menu-item>
|
<core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate" (action)="expandDescription()" [iconAction]="'arrow-forward'"></core-context-menu-item>
|
||||||
<core-context-menu-item *ngIf="blog" [priority]="750" content="{{'addon.blog.blog' | translate}}" [iconAction]="'fa-newspaper-o'" (action)="gotoBlog($event)"></core-context-menu-item>
|
<core-context-menu-item *ngIf="blog" [priority]="750" content="{{'addon.blog.blog' | translate}}" [iconAction]="'fa-newspaper-o'" (action)="gotoBlog($event)"></core-context-menu-item>
|
||||||
<core-context-menu-item [priority]="700" [content]="'core.refresh' | translate" (action)="doRefresh(null, $event)" [iconAction]="refreshIcon" [closeOnClick]="false"></core-context-menu-item>
|
<core-context-menu-item *ngIf="!path" [priority]="700" [content]="'core.refresh' | translate" (action)="doRefresh(null, $event)" [iconAction]="refreshIcon" [closeOnClick]="false"></core-context-menu-item>
|
||||||
<core-context-menu-item *ngIf="prefetchStatusIcon" [priority]="600" [content]="prefetchText" (action)="prefetch($event)" [iconAction]="prefetchStatusIcon" [closeOnClick]="false"></core-context-menu-item>
|
<core-context-menu-item *ngIf="prefetchStatusIcon" [priority]="600" [content]="prefetchText" (action)="prefetch($event)" [iconAction]="prefetchStatusIcon" [closeOnClick]="false"></core-context-menu-item>
|
||||||
<core-context-menu-item *ngIf="size" [priority]="500" [content]="'core.removefiles' | translate:{$a: size}" [iconDescription]="'cube'" (action)="removeFiles()" [iconAction]="'trash'"></core-context-menu-item>
|
<core-context-menu-item *ngIf="size" [priority]="500" [content]="'core.removefiles' | translate:{$a: size}" [iconDescription]="'cube'" (action)="removeFiles()" [iconAction]="'trash'"></core-context-menu-item>
|
||||||
</core-context-menu>
|
</core-context-menu>
|
||||||
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
<ion-list *ngIf="contents && contents.length > 0">
|
<ion-list *ngIf="contents && contents.length > 0">
|
||||||
<ng-container *ngFor="let file of contents">
|
<ng-container *ngFor="let file of contents">
|
||||||
<a *ngIf="file.type === 'folder'" ion-item class="item-media" [navPush]="'AddonModFolderIndexPage'" [navParams]="{path: file.filepath, courseId: courseId, module: file}">
|
<a *ngIf="file.type === 'folder'" ion-item class="item-media" navPush="AddonModFolderIndexPage" [navParams]="{path: file.filepath, courseId: courseId, module: file}">
|
||||||
<ion-icon name="folder" item-start></ion-icon>
|
<ion-icon name="folder" item-start></ion-icon>
|
||||||
<h2>{{file.name}}</h2>
|
<h2>{{file.name}}</h2>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -26,6 +26,7 @@ import { AddonModForumIndexComponent } from './index/index';
|
||||||
import { AddonModForumPostComponent } from './post/post';
|
import { AddonModForumPostComponent } from './post/post';
|
||||||
import { AddonForumDiscussionOptionsMenuComponent } from './discussion-options-menu/discussion-options-menu';
|
import { AddonForumDiscussionOptionsMenuComponent } from './discussion-options-menu/discussion-options-menu';
|
||||||
import { AddonForumPostOptionsMenuComponent } from './post-options-menu/post-options-menu';
|
import { AddonForumPostOptionsMenuComponent } from './post-options-menu/post-options-menu';
|
||||||
|
import { CoreEditorComponentsModule } from '@core/editor/components/components.module';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
|
@ -43,7 +44,8 @@ import { AddonForumPostOptionsMenuComponent } from './post-options-menu/post-opt
|
||||||
CorePipesModule,
|
CorePipesModule,
|
||||||
CoreCourseComponentsModule,
|
CoreCourseComponentsModule,
|
||||||
CoreRatingComponentsModule,
|
CoreRatingComponentsModule,
|
||||||
CoreTagComponentsModule
|
CoreTagComponentsModule,
|
||||||
|
CoreEditorComponentsModule,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
],
|
],
|
||||||
|
|
|
@ -43,7 +43,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ng-container *ngFor="let discussion of offlineDiscussions">
|
<ng-container *ngFor="let discussion of offlineDiscussions">
|
||||||
<ion-item text-wrap (click)="openNewDiscussion(discussion.timecreated)" [attr.no-lines]="discussion.groupname" [class.core-split-item-selected]="discussion.timecreated == -selectedDiscussion" class="addon-mod-forum-discussion">
|
<a ion-item text-wrap (click)="openNewDiscussion(discussion.timecreated)" [attr.no-lines]="discussion.groupname" [class.core-split-item-selected]="discussion.timecreated == -selectedDiscussion" class="addon-mod-forum-discussion">
|
||||||
<div class="addon-mod-forum-discussion-title">
|
<div class="addon-mod-forum-discussion-title">
|
||||||
<h2>
|
<h2>
|
||||||
<core-format-text [text]="discussion.subject" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId"></core-format-text>
|
<core-format-text [text]="discussion.subject" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId"></core-format-text>
|
||||||
|
@ -57,17 +57,17 @@
|
||||||
<p><ion-icon name="time"></ion-icon> {{ 'core.notsent' | translate }}</p>
|
<p><ion-icon name="time"></ion-icon> {{ 'core.notsent' | translate }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ion-item>
|
</a>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container *ngFor="let discussion of discussions">
|
<ng-container *ngFor="let discussion of discussions">
|
||||||
<ion-item (click)="openDiscussion(discussion)" [class.core-split-item-selected]="discussion.discussion == selectedDiscussion" class="addon-mod-forum-discussion">
|
<a ion-item (click)="openDiscussion(discussion)" [class.core-split-item-selected]="discussion.discussion == selectedDiscussion" class="addon-mod-forum-discussion">
|
||||||
<div class="addon-mod-forum-discussion-title">
|
<div class="addon-mod-forum-discussion-title">
|
||||||
<h2 text-wrap>
|
<h2 text-wrap>
|
||||||
<core-icon name="fa-map-pin" *ngIf="discussion.pinned"></core-icon>
|
<core-icon name="fa-map-pin" *ngIf="discussion.pinned"></core-icon>
|
||||||
<core-icon name="fa-star" class="addon-forum-star" *ngIf="!discussion.pinned && discussion.starred"></core-icon>
|
<core-icon name="fa-star" class="addon-forum-star" *ngIf="!discussion.pinned && discussion.starred"></core-icon>
|
||||||
<core-format-text [text]="discussion.subject" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId"></core-format-text>
|
<core-format-text [text]="discussion.subject" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId"></core-format-text>
|
||||||
</h2>
|
</h2>
|
||||||
<button ion-button icon-only clear color="dark" (click)="showOptionsMenu($event, discussion)" *ngIf="canPin || discussion.canlock || discussion.canfavourite">
|
<button ion-button icon-only clear color="dark" (click)="showOptionsMenu($event, discussion)" *ngIf="canPin || discussion.canlock || discussion.canfavourite" [attr.aria-label]="('core.displayoptions' | translate)">
|
||||||
<core-icon name="more"></core-icon>
|
<core-icon name="more"></core-icon>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -94,7 +94,7 @@
|
||||||
</ion-note>
|
</ion-note>
|
||||||
</ion-col>
|
</ion-col>
|
||||||
</ion-row>
|
</ion-row>
|
||||||
</ion-item>
|
</a>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<core-infinite-loading [enabled]="canLoadMore" (action)="fetchMoreDiscussions($event)" [error]="loadMoreError"></core-infinite-loading>
|
<core-infinite-loading [enabled]="canLoadMore" (action)="fetchMoreDiscussions($event)" [error]="loadMoreError"></core-infinite-loading>
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
<ion-note float-end padding-left text-end *ngIf="trackPosts && !post.postread" [attr.aria-label]="'addon.mod_forum.unread' | translate">
|
<ion-note float-end padding-left text-end *ngIf="trackPosts && !post.postread" [attr.aria-label]="'addon.mod_forum.unread' | translate">
|
||||||
<core-icon name="fa-circle" color="primary"></core-icon>
|
<core-icon name="fa-circle" color="primary"></core-icon>
|
||||||
</ion-note>
|
</ion-note>
|
||||||
<button ion-button icon-only clear color="dark" (click)="showOptionsMenu($event)" *ngIf="optionsMenuEnabled">
|
<button ion-button icon-only clear color="dark" (click)="showOptionsMenu($event)" *ngIf="optionsMenuEnabled" [attr.aria-label]="('core.displayoptions' | translate)">
|
||||||
<core-icon name="more"></core-icon>
|
<core-icon name="more"></core-icon>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -26,7 +26,7 @@
|
||||||
<ion-note float-end padding-left text-end *ngIf="trackPosts && !post.postread" [attr.aria-label]="'addon.mod_forum.unread' | translate">
|
<ion-note float-end padding-left text-end *ngIf="trackPosts && !post.postread" [attr.aria-label]="'addon.mod_forum.unread' | translate">
|
||||||
<core-icon name="fa-circle" color="primary"></core-icon>
|
<core-icon name="fa-circle" color="primary"></core-icon>
|
||||||
</ion-note>
|
</ion-note>
|
||||||
<button ion-button icon-only clear color="dark" (click)="showOptionsMenu($event)" *ngIf="optionsMenuEnabled">
|
<button ion-button icon-only clear color="dark" (click)="showOptionsMenu($event)" *ngIf="optionsMenuEnabled" [attr.aria-label]="('core.displayoptions' | translate)">
|
||||||
<core-icon name="more"></core-icon>
|
<core-icon name="more"></core-icon>
|
||||||
</button>
|
</button>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
@ -57,18 +57,18 @@
|
||||||
</ion-item>
|
</ion-item>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ion-list [id]="'addon-forum-reply-edit-form-' + uniqueId" *ngIf="(post.id && !replyData.isEditing && replyData.replyingTo == post.id) || (!post.id && replyData.isEditing && replyData.replyingTo == post.parent)">
|
<form ion-list [id]="'addon-forum-reply-edit-form-' + uniqueId" *ngIf="(post.id && !replyData.isEditing && replyData.replyingTo == post.id) || (!post.id && replyData.isEditing && replyData.replyingTo == post.parent)" #replyFormEl>
|
||||||
<ion-item>
|
<ion-item>
|
||||||
<ion-label stacked>{{ 'addon.mod_forum.subject' | translate }}</ion-label>
|
<ion-label stacked>{{ 'addon.mod_forum.subject' | translate }}</ion-label>
|
||||||
<ion-input type="text" [placeholder]="'addon.mod_forum.subject' | translate" [(ngModel)]="replyData.subject"></ion-input>
|
<ion-input type="text" [placeholder]="'addon.mod_forum.subject' | translate" [(ngModel)]="replyData.subject" name="subject"></ion-input>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item>
|
<ion-item>
|
||||||
<ion-label stacked>{{ 'addon.mod_forum.message' | translate }}</ion-label>
|
<ion-label stacked>{{ 'addon.mod_forum.message' | translate }}</ion-label>
|
||||||
<core-rich-text-editor item-content [control]="messageControl" (contentChanged)="onMessageChange($event)" [placeholder]="'addon.mod_forum.replyplaceholder' | translate" [name]="'mod_forum_reply_' + post.id" [component]="component" [componentId]="componentId"></core-rich-text-editor>
|
<core-rich-text-editor item-content [control]="messageControl" (contentChanged)="onMessageChange($event)" [placeholder]="'addon.mod_forum.replyplaceholder' | translate" [name]="'mod_forum_reply_' + post.id" [component]="component" [componentId]="componentId" [autoSave]="true" contextLevel="module" [contextInstanceId]="forum && forum.cmid" elementId="message" [draftExtraParams]="{reply: post.id}"></core-rich-text-editor>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item text-wrap *ngIf="accessInfo.canpostprivatereply">
|
<ion-item text-wrap *ngIf="accessInfo.canpostprivatereply">
|
||||||
<ion-label>{{ 'addon.mod_forum.privatereply' | translate }}</ion-label>
|
<ion-label>{{ 'addon.mod_forum.privatereply' | translate }}</ion-label>
|
||||||
<ion-checkbox item-end [(ngModel)]="replyData.isprivatereply"></ion-checkbox>
|
<ion-checkbox item-end [(ngModel)]="replyData.isprivatereply" name="isprivatereply"></ion-checkbox>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item-divider text-wrap (click)="toggleAdvanced()" class="core-expandable">
|
<ion-item-divider text-wrap (click)="toggleAdvanced()" class="core-expandable">
|
||||||
<core-icon *ngIf="!advanced" name="fa-caret-right" item-start></core-icon>
|
<core-icon *ngIf="!advanced" name="fa-caret-right" item-start></core-icon>
|
||||||
|
@ -88,5 +88,5 @@
|
||||||
</ion-col>
|
</ion-col>
|
||||||
</ion-row>
|
</ion-row>
|
||||||
</ion-grid>
|
</ion-grid>
|
||||||
</ion-list>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -12,7 +12,9 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { Component, Input, Output, Optional, EventEmitter, OnInit, OnDestroy } from '@angular/core';
|
import {
|
||||||
|
Component, Input, Output, Optional, EventEmitter, OnInit, OnDestroy, ViewChild, ElementRef, OnChanges, SimpleChange
|
||||||
|
} from '@angular/core';
|
||||||
import { FormControl } from '@angular/forms';
|
import { FormControl } from '@angular/forms';
|
||||||
import { Content, PopoverController, ModalController } from 'ionic-angular';
|
import { Content, PopoverController, ModalController } from 'ionic-angular';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
@ -37,7 +39,7 @@ import { AddonForumPostOptionsMenuComponent } from '../post-options-menu/post-op
|
||||||
selector: 'addon-mod-forum-post',
|
selector: 'addon-mod-forum-post',
|
||||||
templateUrl: 'addon-mod-forum-post.html',
|
templateUrl: 'addon-mod-forum-post.html',
|
||||||
})
|
})
|
||||||
export class AddonModForumPostComponent implements OnInit, OnDestroy {
|
export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges {
|
||||||
@Input() post: any; // Post.
|
@Input() post: any; // Post.
|
||||||
@Input() courseId: number; // Post's course ID.
|
@Input() courseId: number; // Post's course ID.
|
||||||
@Input() discussionId: number; // Post's' discussion ID.
|
@Input() discussionId: number; // Post's' discussion ID.
|
||||||
|
@ -50,8 +52,11 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy {
|
||||||
@Input() accessInfo: any; // Forum access information.
|
@Input() accessInfo: any; // Forum access information.
|
||||||
@Input() parentSubject?: string; // Subject of parent post.
|
@Input() parentSubject?: string; // Subject of parent post.
|
||||||
@Input() ratingInfo?: CoreRatingInfo; // Rating info item.
|
@Input() ratingInfo?: CoreRatingInfo; // Rating info item.
|
||||||
|
@Input() leavingPage?: boolean; // Whether the page that contains this post is being left and will be destroyed.
|
||||||
@Output() onPostChange: EventEmitter<void>; // Event emitted when a reply is posted or modified.
|
@Output() onPostChange: EventEmitter<void>; // Event emitted when a reply is posted or modified.
|
||||||
|
|
||||||
|
@ViewChild('replyFormEl') formElement: ElementRef;
|
||||||
|
|
||||||
messageControl = new FormControl();
|
messageControl = new FormControl();
|
||||||
|
|
||||||
uniqueId: string;
|
uniqueId: string;
|
||||||
|
@ -100,6 +105,16 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy {
|
||||||
(this.forumProvider.isDeletePostAvailable() || this.forumProvider.isUpdatePostAvailable()));
|
(this.forumProvider.isDeletePostAvailable() || this.forumProvider.isUpdatePostAvailable()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect changes on input properties.
|
||||||
|
*/
|
||||||
|
ngOnChanges(changes: {[name: string]: SimpleChange}): void {
|
||||||
|
if (changes.leavingPage && this.leavingPage) {
|
||||||
|
// Download all courses is enabled now, initialize it.
|
||||||
|
this.domUtils.triggerFormCancelledEvent(this.formElement, this.sitesProvider.getCurrentSiteId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes an online post.
|
* Deletes an online post.
|
||||||
*/
|
*/
|
||||||
|
@ -408,6 +423,8 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
this.onPostChange.emit();
|
this.onPostChange.emit();
|
||||||
|
|
||||||
|
this.domUtils.triggerFormSubmittedEvent(this.formElement, sent, this.sitesProvider.getCurrentSiteId());
|
||||||
|
|
||||||
if (this.syncId) {
|
if (this.syncId) {
|
||||||
this.syncProvider.unblockOperation(AddonModForumProvider.COMPONENT, this.syncId);
|
this.syncProvider.unblockOperation(AddonModForumProvider.COMPONENT, this.syncId);
|
||||||
}
|
}
|
||||||
|
@ -426,6 +443,8 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy {
|
||||||
// Reset data.
|
// Reset data.
|
||||||
this.setReplyFormData();
|
this.setReplyFormData();
|
||||||
|
|
||||||
|
this.domUtils.triggerFormCancelledEvent(this.formElement, this.sitesProvider.getCurrentSiteId());
|
||||||
|
|
||||||
if (this.syncId) {
|
if (this.syncId) {
|
||||||
this.syncProvider.unblockOperation(AddonModForumProvider.COMPONENT, this.syncId);
|
this.syncProvider.unblockOperation(AddonModForumProvider.COMPONENT, this.syncId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,6 @@ import { AddonModForumPostLinkHandler } from './providers/post-link-handler';
|
||||||
import { AddonModForumPushClickHandler } from './providers/push-click-handler';
|
import { AddonModForumPushClickHandler } from './providers/push-click-handler';
|
||||||
import { AddonModForumTagAreaHandler } from './providers/tag-area-handler';
|
import { AddonModForumTagAreaHandler } from './providers/tag-area-handler';
|
||||||
import { AddonModForumComponentsModule } from './components/components.module';
|
import { AddonModForumComponentsModule } from './components/components.module';
|
||||||
import { CoreUpdateManagerProvider } from '@providers/update-manager';
|
|
||||||
|
|
||||||
// List of providers (without handlers).
|
// List of providers (without handlers).
|
||||||
export const ADDON_MOD_FORUM_PROVIDERS: any[] = [
|
export const ADDON_MOD_FORUM_PROVIDERS: any[] = [
|
||||||
|
@ -70,7 +69,7 @@ export class AddonModForumModule {
|
||||||
prefetchDelegate: CoreCourseModulePrefetchDelegate, prefetchHandler: AddonModForumPrefetchHandler,
|
prefetchDelegate: CoreCourseModulePrefetchDelegate, prefetchHandler: AddonModForumPrefetchHandler,
|
||||||
cronDelegate: CoreCronDelegate, syncHandler: AddonModForumSyncCronHandler, linksDelegate: CoreContentLinksDelegate,
|
cronDelegate: CoreCronDelegate, syncHandler: AddonModForumSyncCronHandler, linksDelegate: CoreContentLinksDelegate,
|
||||||
indexHandler: AddonModForumIndexLinkHandler, discussionHandler: AddonModForumDiscussionLinkHandler,
|
indexHandler: AddonModForumIndexLinkHandler, discussionHandler: AddonModForumDiscussionLinkHandler,
|
||||||
updateManager: CoreUpdateManagerProvider, listLinkHandler: AddonModForumListLinkHandler,
|
listLinkHandler: AddonModForumListLinkHandler,
|
||||||
pushNotificationsDelegate: CorePushNotificationsDelegate, pushClickHandler: AddonModForumPushClickHandler,
|
pushNotificationsDelegate: CorePushNotificationsDelegate, pushClickHandler: AddonModForumPushClickHandler,
|
||||||
postLinkHandler: AddonModForumPostLinkHandler, tagAreaDelegate: CoreTagAreaDelegate,
|
postLinkHandler: AddonModForumPostLinkHandler, tagAreaDelegate: CoreTagAreaDelegate,
|
||||||
tagAreaHandler: AddonModForumTagAreaHandler) {
|
tagAreaHandler: AddonModForumTagAreaHandler) {
|
||||||
|
@ -84,41 +83,5 @@ export class AddonModForumModule {
|
||||||
linksDelegate.registerHandler(postLinkHandler);
|
linksDelegate.registerHandler(postLinkHandler);
|
||||||
pushNotificationsDelegate.registerClickHandler(pushClickHandler);
|
pushNotificationsDelegate.registerClickHandler(pushClickHandler);
|
||||||
tagAreaDelegate.registerHandler(tagAreaHandler);
|
tagAreaDelegate.registerHandler(tagAreaHandler);
|
||||||
|
|
||||||
// Allow migrating the tables from the old app to the new schema.
|
|
||||||
updateManager.registerSiteTablesMigration([
|
|
||||||
{
|
|
||||||
name: 'mma_mod_forum_offline_discussions',
|
|
||||||
newName: AddonModForumOfflineProvider.DISCUSSIONS_TABLE,
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
name: 'forumAndUser',
|
|
||||||
delete: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'options',
|
|
||||||
type: 'object'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'mma_mod_forum_offline_replies',
|
|
||||||
newName: AddonModForumOfflineProvider.REPLIES_TABLE,
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
name: 'forumAndUser',
|
|
||||||
delete: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'discussionAndUser',
|
|
||||||
delete: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'options',
|
|
||||||
type: 'object'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,13 +42,13 @@
|
||||||
</ion-card>
|
</ion-card>
|
||||||
|
|
||||||
<div *ngIf="discussion" margin-bottom class="highlight">
|
<div *ngIf="discussion" margin-bottom class="highlight">
|
||||||
<addon-mod-forum-post [post]="discussion" [courseId]="courseId" [discussionId]="discussionId" [component]="component" [componentId]="cmId" [replyData]="replyData" [originalData]="originalData" [forum]="forum" [accessInfo]="accessInfo" [trackPosts]="trackPosts" [ratingInfo]="ratingInfo" (onPostChange)="postListChanged()"></addon-mod-forum-post>
|
<addon-mod-forum-post [post]="discussion" [courseId]="courseId" [discussionId]="discussionId" [component]="component" [componentId]="cmId" [replyData]="replyData" [originalData]="originalData" [forum]="forum" [accessInfo]="accessInfo" [trackPosts]="trackPosts" [ratingInfo]="ratingInfo" [leavingPage]="leavingPage" (onPostChange)="postListChanged()"></addon-mod-forum-post>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ion-card *ngIf="sort != 'nested'">
|
<ion-card *ngIf="sort != 'nested'">
|
||||||
<ng-container *ngFor="let post of posts; first as first">
|
<ng-container *ngFor="let post of posts; first as first">
|
||||||
<ion-item-divider *ngIf="!first"></ion-item-divider>
|
<ion-item-divider *ngIf="!first"></ion-item-divider>
|
||||||
<addon-mod-forum-post [post]="post" [courseId]="courseId" [discussionId]="discussionId" [component]="component" [componentId]="cmId" [replyData]="replyData" [originalData]="originalData" [parentSubject]="postSubjects[post.parent]" [forum]="forum" [accessInfo]="accessInfo" [trackPosts]="trackPosts" [ratingInfo]="ratingInfo" (onPostChange)="postListChanged()"></addon-mod-forum-post>
|
<addon-mod-forum-post [post]="post" [courseId]="courseId" [discussionId]="discussionId" [component]="component" [componentId]="cmId" [replyData]="replyData" [originalData]="originalData" [parentSubject]="postSubjects[post.parent]" [forum]="forum" [accessInfo]="accessInfo" [trackPosts]="trackPosts" [ratingInfo]="ratingInfo" [leavingPage]="leavingPage" (onPostChange)="postListChanged()"></addon-mod-forum-post>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</ion-card>
|
</ion-card>
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@
|
||||||
|
|
||||||
<ng-template #nestedPosts let-post="post">
|
<ng-template #nestedPosts let-post="post">
|
||||||
<ion-card>
|
<ion-card>
|
||||||
<addon-mod-forum-post [post]="post" [courseId]="courseId" [discussionId]="discussionId" [component]="component" [componentId]="cmId" [replyData]="replyData" [originalData]="originalData" [parentSubject]="postSubjects[post.parent]" [forum]="forum" [accessInfo]="accessInfo" [trackPosts]="trackPosts" [ratingInfo]="ratingInfo" (onPostChange)="postListChanged()"></addon-mod-forum-post>
|
<addon-mod-forum-post [post]="post" [courseId]="courseId" [discussionId]="discussionId" [component]="component" [componentId]="cmId" [replyData]="replyData" [originalData]="originalData" [parentSubject]="postSubjects[post.parent]" [forum]="forum" [accessInfo]="accessInfo" [trackPosts]="trackPosts" [ratingInfo]="ratingInfo" [leavingPage]="leavingPage" (onPostChange)="postListChanged()"></addon-mod-forum-post>
|
||||||
</ion-card>
|
</ion-card>
|
||||||
<div padding-left *ngIf="post.children.length && post.children[0].subject">
|
<div padding-left *ngIf="post.children.length && post.children[0].subject">
|
||||||
<ng-container *ngFor="let child of post.children">
|
<ng-container *ngFor="let child of post.children">
|
||||||
|
|
|
@ -81,6 +81,7 @@ export class AddonModForumDiscussionPage implements OnDestroy {
|
||||||
cmId: number;
|
cmId: number;
|
||||||
canPin = false;
|
canPin = false;
|
||||||
availabilityMessage: string;
|
availabilityMessage: string;
|
||||||
|
leavingPage = false;
|
||||||
|
|
||||||
protected forumId: number;
|
protected forumId: number;
|
||||||
protected postId: number;
|
protected postId: number;
|
||||||
|
@ -251,20 +252,17 @@ export class AddonModForumDiscussionPage implements OnDestroy {
|
||||||
*
|
*
|
||||||
* @return Resolved if we can leave it, rejected if not.
|
* @return Resolved if we can leave it, rejected if not.
|
||||||
*/
|
*/
|
||||||
ionViewCanLeave(): boolean | Promise<void> {
|
async ionViewCanLeave(): Promise<void> {
|
||||||
let promise: any;
|
|
||||||
|
|
||||||
if (this.forumHelper.hasPostDataChanged(this.replyData, this.originalData)) {
|
if (this.forumHelper.hasPostDataChanged(this.replyData, this.originalData)) {
|
||||||
// Show confirmation if some data has been modified.
|
// Show confirmation if some data has been modified.
|
||||||
promise = this.domUtils.showConfirm(this.translate.instant('core.confirmcanceledit'));
|
await this.domUtils.showConfirm(this.translate.instant('core.confirmcanceledit'));
|
||||||
} else {
|
|
||||||
promise = Promise.resolve();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return promise.then(() => {
|
// Delete the local files from the tmp folder.
|
||||||
// Delete the local files from the tmp folder.
|
this.uploaderProvider.clearTmpFiles(this.replyData.files);
|
||||||
this.uploaderProvider.clearTmpFiles(this.replyData.files);
|
|
||||||
});
|
this.leavingPage = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -9,14 +9,14 @@
|
||||||
</ion-navbar>
|
</ion-navbar>
|
||||||
</ion-header>
|
</ion-header>
|
||||||
<ion-content>
|
<ion-content>
|
||||||
<ion-list>
|
<form ion-list #editFormEl>
|
||||||
<ion-item>
|
<ion-item>
|
||||||
<ion-label stacked>{{ 'addon.mod_forum.subject' | translate }}</ion-label>
|
<ion-label stacked>{{ 'addon.mod_forum.subject' | translate }}</ion-label>
|
||||||
<ion-input type="text" [placeholder]="'addon.mod_forum.subject' | translate" [(ngModel)]="replyData.subject"></ion-input>
|
<ion-input type="text" [placeholder]="'addon.mod_forum.subject' | translate" [(ngModel)]="replyData.subject" name="subject"></ion-input>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item>
|
<ion-item>
|
||||||
<ion-label stacked>{{ 'addon.mod_forum.message' | translate }}</ion-label>
|
<ion-label stacked>{{ 'addon.mod_forum.message' | translate }}</ion-label>
|
||||||
<core-rich-text-editor item-content [control]="messageControl" (contentChanged)="onMessageChange($event)" [placeholder]="'addon.mod_forum.replyplaceholder' | translate" [name]="'mod_forum_reply_' + replyData.id" [component]="component" [componentId]="componentId"></core-rich-text-editor>
|
<core-rich-text-editor item-content [control]="messageControl" (contentChanged)="onMessageChange($event)" [placeholder]="'addon.mod_forum.replyplaceholder' | translate" [name]="'mod_forum_reply_' + replyData.id" [component]="component" [componentId]="componentId" [autoSave]="true" contextLevel="module" [contextInstanceId]="forum.cmid" elementId="message" [draftExtraParams]="{edit: replyData.id}"></core-rich-text-editor>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item-divider text-wrap (click)="toggleAdvanced()" class="core-expandable">
|
<ion-item-divider text-wrap (click)="toggleAdvanced()" class="core-expandable">
|
||||||
<core-icon *ngIf="!advanced" name="fa-caret-right" item-start></core-icon>
|
<core-icon *ngIf="!advanced" name="fa-caret-right" item-start></core-icon>
|
||||||
|
@ -36,5 +36,5 @@
|
||||||
</ion-col>
|
</ion-col>
|
||||||
</ion-row>
|
</ion-row>
|
||||||
</ion-grid>
|
</ion-grid>
|
||||||
</ion-list>
|
</form>
|
||||||
</ion-content>
|
</ion-content>
|
|
@ -19,6 +19,7 @@ import { CoreComponentsModule } from '@components/components.module';
|
||||||
import { CoreDirectivesModule } from '@directives/directives.module';
|
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||||
import { AddonModForumComponentsModule } from '../../components/components.module';
|
import { AddonModForumComponentsModule } from '../../components/components.module';
|
||||||
import { AddonModForumEditPostPage } from './edit-post';
|
import { AddonModForumEditPostPage } from './edit-post';
|
||||||
|
import { CoreEditorComponentsModule } from '@core/editor/components/components.module';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
|
@ -28,6 +29,7 @@ import { AddonModForumEditPostPage } from './edit-post';
|
||||||
CoreComponentsModule,
|
CoreComponentsModule,
|
||||||
CoreDirectivesModule,
|
CoreDirectivesModule,
|
||||||
AddonModForumComponentsModule,
|
AddonModForumComponentsModule,
|
||||||
|
CoreEditorComponentsModule,
|
||||||
IonicPageModule.forChild(AddonModForumEditPostPage),
|
IonicPageModule.forChild(AddonModForumEditPostPage),
|
||||||
TranslateModule.forChild()
|
TranslateModule.forChild()
|
||||||
],
|
],
|
||||||
|
|
|
@ -12,11 +12,12 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { Component } from '@angular/core';
|
import { Component, ViewChild, ElementRef } from '@angular/core';
|
||||||
import { FormControl } from '@angular/forms';
|
import { FormControl } from '@angular/forms';
|
||||||
import { IonicPage, ViewController, NavParams } from 'ionic-angular';
|
import { IonicPage, ViewController, NavParams } from 'ionic-angular';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader';
|
import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader';
|
||||||
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
import { AddonModForumProvider } from '../../providers/forum';
|
import { AddonModForumProvider } from '../../providers/forum';
|
||||||
import { AddonModForumHelperProvider } from '../../providers/helper';
|
import { AddonModForumHelperProvider } from '../../providers/helper';
|
||||||
|
@ -30,6 +31,8 @@ import { AddonModForumHelperProvider } from '../../providers/helper';
|
||||||
templateUrl: 'addon-mod-forum-edit-post.html',
|
templateUrl: 'addon-mod-forum-edit-post.html',
|
||||||
})
|
})
|
||||||
export class AddonModForumEditPostPage {
|
export class AddonModForumEditPostPage {
|
||||||
|
@ViewChild('editFormEl') formElement: ElementRef;
|
||||||
|
|
||||||
component: string; // Component this post belong to.
|
component: string; // Component this post belong to.
|
||||||
componentId: number; // Component ID.
|
componentId: number; // Component ID.
|
||||||
forum: any; // The forum the post belongs to. Required for attachments and offline posts.
|
forum: any; // The forum the post belongs to. Required for attachments and offline posts.
|
||||||
|
@ -48,7 +51,8 @@ export class AddonModForumEditPostPage {
|
||||||
protected domUtils: CoreDomUtilsProvider,
|
protected domUtils: CoreDomUtilsProvider,
|
||||||
protected uploaderProvider: CoreFileUploaderProvider,
|
protected uploaderProvider: CoreFileUploaderProvider,
|
||||||
protected forumHelper: AddonModForumHelperProvider,
|
protected forumHelper: AddonModForumHelperProvider,
|
||||||
protected translate: TranslateService) {
|
protected translate: TranslateService,
|
||||||
|
protected sitesProvider: CoreSitesProvider) {
|
||||||
|
|
||||||
const post = params.get('post');
|
const post = params.get('post');
|
||||||
this.component = params.get('component');
|
this.component = params.get('component');
|
||||||
|
@ -114,7 +118,13 @@ export class AddonModForumEditPostPage {
|
||||||
*
|
*
|
||||||
* @param data Data to return to the page.
|
* @param data Data to return to the page.
|
||||||
*/
|
*/
|
||||||
closeModal(data: any): void {
|
closeModal(data: any, ): void {
|
||||||
|
if (data) {
|
||||||
|
this.domUtils.triggerFormSubmittedEvent(this.formElement, false, this.sitesProvider.getCurrentSiteId());
|
||||||
|
} else {
|
||||||
|
this.domUtils.triggerFormCancelledEvent(this.formElement, this.sitesProvider.getCurrentSiteId());
|
||||||
|
}
|
||||||
|
|
||||||
this.viewCtrl.dismiss(data);
|
this.viewCtrl.dismiss(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,14 +12,14 @@
|
||||||
</ion-refresher>
|
</ion-refresher>
|
||||||
|
|
||||||
<core-loading [hideUntil]="groupsLoaded">
|
<core-loading [hideUntil]="groupsLoaded">
|
||||||
<ion-list *ngIf="showForm">
|
<form ion-list *ngIf="showForm" #newDiscFormEl>
|
||||||
<ion-item>
|
<ion-item>
|
||||||
<ion-label stacked>{{ 'addon.mod_forum.subject' | translate }}</ion-label>
|
<ion-label stacked>{{ 'addon.mod_forum.subject' | translate }}</ion-label>
|
||||||
<ion-input type="text" [placeholder]="'addon.mod_forum.subject' | translate" [(ngModel)]="newDiscussion.subject"></ion-input>
|
<ion-input type="text" [placeholder]="'addon.mod_forum.subject' | translate" [(ngModel)]="newDiscussion.subject" name="subject"></ion-input>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item>
|
<ion-item>
|
||||||
<ion-label stacked>{{ 'addon.mod_forum.message' | translate }}</ion-label>
|
<ion-label stacked>{{ 'addon.mod_forum.message' | translate }}</ion-label>
|
||||||
<core-rich-text-editor item-content [control]="messageControl" (contentChanged)="onMessageChange($event)" [placeholder]="'addon.mod_forum.message' | translate" name="addon_mod_forum_new_discussion" [component]="component" [componentId]="forum.cmid"></core-rich-text-editor>
|
<core-rich-text-editor item-content [control]="messageControl" (contentChanged)="onMessageChange($event)" [placeholder]="'addon.mod_forum.message' | translate" name="addon_mod_forum_new_discussion" [component]="component" [componentId]="forum.cmid" [autoSave]="true" contextLevel="module" [contextInstanceId]="forum.cmid" elementId="message"></core-rich-text-editor>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item-divider text-wrap (click)="toggleAdvanced()" class="core-expandable">
|
<ion-item-divider text-wrap (click)="toggleAdvanced()" class="core-expandable">
|
||||||
<core-icon *ngIf="!advanced" name="fa-caret-right" item-start></core-icon>
|
<core-icon *ngIf="!advanced" name="fa-caret-right" item-start></core-icon>
|
||||||
|
@ -29,21 +29,21 @@
|
||||||
<ng-container *ngIf="advanced">
|
<ng-container *ngIf="advanced">
|
||||||
<ion-item *ngIf="showGroups && groupIds.length > 1 && accessInfo.cancanposttomygroups">
|
<ion-item *ngIf="showGroups && groupIds.length > 1 && accessInfo.cancanposttomygroups">
|
||||||
<ion-label>{{ 'addon.mod_forum.posttomygroups' | translate }}</ion-label>
|
<ion-label>{{ 'addon.mod_forum.posttomygroups' | translate }}</ion-label>
|
||||||
<ion-toggle [(ngModel)]="newDiscussion.postToAllGroups"></ion-toggle>
|
<ion-toggle [(ngModel)]="newDiscussion.postToAllGroups" name="postallgroups"></ion-toggle>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item *ngIf="showGroups">
|
<ion-item *ngIf="showGroups">
|
||||||
<ion-label id="addon-mod-forum-groupslabel">{{ 'addon.mod_forum.group' | translate }}</ion-label>
|
<ion-label id="addon-mod-forum-groupslabel">{{ 'addon.mod_forum.group' | translate }}</ion-label>
|
||||||
<ion-select [(ngModel)]="newDiscussion.groupId" [disabled]="newDiscussion.postToAllGroups" aria-labelledby="addon-mod-forum-groupslabel" interface="action-sheet">
|
<ion-select [(ngModel)]="newDiscussion.groupId" [disabled]="newDiscussion.postToAllGroups" aria-labelledby="addon-mod-forum-groupslabel" interface="action-sheet" name="groupid">
|
||||||
<ion-option *ngFor="let group of groups" [value]="group.id">{{ group.name }}</ion-option>
|
<ion-option *ngFor="let group of groups" [value]="group.id">{{ group.name }}</ion-option>
|
||||||
</ion-select>
|
</ion-select>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item>
|
<ion-item>
|
||||||
<ion-label>{{ 'addon.mod_forum.discussionsubscription' | translate }}</ion-label>
|
<ion-label>{{ 'addon.mod_forum.discussionsubscription' | translate }}</ion-label>
|
||||||
<ion-toggle [(ngModel)]="newDiscussion.subscribe"></ion-toggle>
|
<ion-toggle [(ngModel)]="newDiscussion.subscribe" name="subscribe"></ion-toggle>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item *ngIf="canPin">
|
<ion-item *ngIf="canPin">
|
||||||
<ion-label>{{ 'addon.mod_forum.discussionpinned' | translate }}</ion-label>
|
<ion-label>{{ 'addon.mod_forum.discussionpinned' | translate }}</ion-label>
|
||||||
<ion-toggle [(ngModel)]="newDiscussion.pin"></ion-toggle>
|
<ion-toggle [(ngModel)]="newDiscussion.pin" name="pin"></ion-toggle>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<core-attachments *ngIf="canCreateAttachments && forum && forum.maxattachments > 0" [files]="newDiscussion.files" [maxSize]="forum.maxbytes" [maxSubmissions]="forum.maxattachments" [component]="component" [componentId]="forum.cmid" [allowOffline]="true"></core-attachments>
|
<core-attachments *ngIf="canCreateAttachments && forum && forum.maxattachments > 0" [files]="newDiscussion.files" [maxSize]="forum.maxbytes" [maxSubmissions]="forum.maxattachments" [component]="component" [componentId]="forum.cmid" [allowOffline]="true"></core-attachments>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
@ -57,6 +57,6 @@
|
||||||
</ion-col>
|
</ion-col>
|
||||||
</ion-row>
|
</ion-row>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
</ion-list>
|
</form>
|
||||||
</core-loading>
|
</core-loading>
|
||||||
</ion-content>
|
</ion-content>
|
||||||
|
|
|
@ -18,6 +18,7 @@ import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { CoreComponentsModule } from '@components/components.module';
|
import { CoreComponentsModule } from '@components/components.module';
|
||||||
import { CoreDirectivesModule } from '@directives/directives.module';
|
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||||
import { AddonModForumNewDiscussionPage } from './new-discussion';
|
import { AddonModForumNewDiscussionPage } from './new-discussion';
|
||||||
|
import { CoreEditorComponentsModule } from '@core/editor/components/components.module';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
|
@ -26,6 +27,7 @@ import { AddonModForumNewDiscussionPage } from './new-discussion';
|
||||||
imports: [
|
imports: [
|
||||||
CoreComponentsModule,
|
CoreComponentsModule,
|
||||||
CoreDirectivesModule,
|
CoreDirectivesModule,
|
||||||
|
CoreEditorComponentsModule,
|
||||||
IonicPageModule.forChild(AddonModForumNewDiscussionPage),
|
IonicPageModule.forChild(AddonModForumNewDiscussionPage),
|
||||||
TranslateModule.forChild()
|
TranslateModule.forChild()
|
||||||
],
|
],
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { Component, OnDestroy, Optional, ViewChild } from '@angular/core';
|
import { Component, OnDestroy, Optional, ViewChild, ElementRef } from '@angular/core';
|
||||||
import { FormControl } from '@angular/forms';
|
import { FormControl } from '@angular/forms';
|
||||||
import { IonicPage, NavController, NavParams } from 'ionic-angular';
|
import { IonicPage, NavController, NavParams } from 'ionic-angular';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
@ -25,7 +25,7 @@ import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||||
import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader';
|
import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader';
|
||||||
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
||||||
import { CoreRichTextEditorComponent } from '@components/rich-text-editor/rich-text-editor.ts';
|
import { CoreEditorRichTextEditorComponent } from '@core/editor/components/rich-text-editor/rich-text-editor.ts';
|
||||||
import { AddonModForumProvider } from '../../providers/forum';
|
import { AddonModForumProvider } from '../../providers/forum';
|
||||||
import { AddonModForumOfflineProvider } from '../../providers/offline';
|
import { AddonModForumOfflineProvider } from '../../providers/offline';
|
||||||
import { AddonModForumHelperProvider } from '../../providers/helper';
|
import { AddonModForumHelperProvider } from '../../providers/helper';
|
||||||
|
@ -41,7 +41,8 @@ import { AddonModForumSyncProvider } from '../../providers/sync';
|
||||||
})
|
})
|
||||||
export class AddonModForumNewDiscussionPage implements OnDestroy {
|
export class AddonModForumNewDiscussionPage implements OnDestroy {
|
||||||
|
|
||||||
@ViewChild(CoreRichTextEditorComponent) messageEditor: CoreRichTextEditorComponent;
|
@ViewChild('newDiscFormEl') formElement: ElementRef;
|
||||||
|
@ViewChild(CoreEditorRichTextEditorComponent) messageEditor: CoreEditorRichTextEditorComponent;
|
||||||
|
|
||||||
component = AddonModForumProvider.COMPONENT;
|
component = AddonModForumProvider.COMPONENT;
|
||||||
messageControl = new FormControl();
|
messageControl = new FormControl();
|
||||||
|
@ -74,6 +75,7 @@ export class AddonModForumNewDiscussionPage implements OnDestroy {
|
||||||
protected syncObserver: any;
|
protected syncObserver: any;
|
||||||
protected isDestroyed = false;
|
protected isDestroyed = false;
|
||||||
protected originalData: any;
|
protected originalData: any;
|
||||||
|
protected forceLeave = false;
|
||||||
|
|
||||||
constructor(navParams: NavParams,
|
constructor(navParams: NavParams,
|
||||||
private navCtrl: NavController,
|
private navCtrl: NavController,
|
||||||
|
@ -408,6 +410,7 @@ export class AddonModForumNewDiscussionPage implements OnDestroy {
|
||||||
this.newDiscussion.postToAllGroups = false;
|
this.newDiscussion.postToAllGroups = false;
|
||||||
this.messageEditor.clearText();
|
this.messageEditor.clearText();
|
||||||
this.originalData = this.utils.clone(this.newDiscussion);
|
this.originalData = this.utils.clone(this.newDiscussion);
|
||||||
|
this.forceLeave = true; // Avoid asking for confirmation.
|
||||||
|
|
||||||
// Trigger view event, to highlight the current opened discussion in the split view.
|
// Trigger view event, to highlight the current opened discussion in the split view.
|
||||||
this.eventsProvider.trigger(AddonModForumProvider.VIEW_DISCUSSION_EVENT, {
|
this.eventsProvider.trigger(AddonModForumProvider.VIEW_DISCUSSION_EVENT, {
|
||||||
|
@ -415,7 +418,7 @@ export class AddonModForumNewDiscussionPage implements OnDestroy {
|
||||||
discussion: 0
|
discussion: 0
|
||||||
}, this.sitesProvider.getCurrentSiteId());
|
}, this.sitesProvider.getCurrentSiteId());
|
||||||
} else {
|
} else {
|
||||||
this.originalData = null; // Avoid asking for confirmation.
|
this.forceLeave = true; // Avoid asking for confirmation.
|
||||||
this.navCtrl.pop();
|
this.navCtrl.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -477,6 +480,8 @@ export class AddonModForumNewDiscussionPage implements OnDestroy {
|
||||||
this.domUtils.showErrorModalDefault(null, 'addon.mod_forum.errorposttoallgroups', true);
|
this.domUtils.showErrorModalDefault(null, 'addon.mod_forum.errorposttoallgroups', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.domUtils.triggerFormSubmittedEvent(this.formElement, !!discussionIds, this.sitesProvider.getCurrentSiteId());
|
||||||
|
|
||||||
this.returnToDiscussions(discussionIds, discTimecreated);
|
this.returnToDiscussions(discussionIds, discTimecreated);
|
||||||
}).catch((message) => {
|
}).catch((message) => {
|
||||||
this.domUtils.showErrorModalDefault(message, 'addon.mod_forum.cannotcreatediscussion', true);
|
this.domUtils.showErrorModalDefault(message, 'addon.mod_forum.cannotcreatediscussion', true);
|
||||||
|
@ -498,6 +503,8 @@ export class AddonModForumNewDiscussionPage implements OnDestroy {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return Promise.all(promises).then(() => {
|
return Promise.all(promises).then(() => {
|
||||||
|
this.domUtils.triggerFormCancelledEvent(this.formElement, this.sitesProvider.getCurrentSiteId());
|
||||||
|
|
||||||
this.returnToDiscussions();
|
this.returnToDiscussions();
|
||||||
});
|
});
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
|
@ -517,20 +524,22 @@ export class AddonModForumNewDiscussionPage implements OnDestroy {
|
||||||
*
|
*
|
||||||
* @return Resolved if we can leave it, rejected if not.
|
* @return Resolved if we can leave it, rejected if not.
|
||||||
*/
|
*/
|
||||||
ionViewCanLeave(): boolean | Promise<void> {
|
async ionViewCanLeave(): Promise<void> {
|
||||||
let promise: any;
|
if (this.forceLeave) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.forumHelper.hasPostDataChanged(this.newDiscussion, this.originalData)) {
|
if (this.forumHelper.hasPostDataChanged(this.newDiscussion, this.originalData)) {
|
||||||
// Show confirmation if some data has been modified.
|
// Show confirmation if some data has been modified.
|
||||||
promise = this.domUtils.showConfirm(this.translate.instant('core.confirmcanceledit'));
|
await this.domUtils.showConfirm(this.translate.instant('core.confirmcanceledit'));
|
||||||
} else {
|
|
||||||
promise = Promise.resolve();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return promise.then(() => {
|
// Delete the local files from the tmp folder.
|
||||||
// Delete the local files from the tmp folder.
|
this.uploaderProvider.clearTmpFiles(this.newDiscussion.files);
|
||||||
this.uploaderProvider.clearTmpFiles(this.newDiscussion.files);
|
|
||||||
});
|
if (this.formElement) {
|
||||||
|
this.domUtils.triggerFormCancelledEvent(this.formElement, this.sitesProvider.getCurrentSiteId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -37,6 +37,7 @@ export class AddonModForumProvider {
|
||||||
static VIEW_DISCUSSION_EVENT = 'addon_mod_forum_view_discussion';
|
static VIEW_DISCUSSION_EVENT = 'addon_mod_forum_view_discussion';
|
||||||
static CHANGE_DISCUSSION_EVENT = 'addon_mod_forum_change_discussion_status';
|
static CHANGE_DISCUSSION_EVENT = 'addon_mod_forum_change_discussion_status';
|
||||||
static MARK_READ_EVENT = 'addon_mod_forum_mark_read';
|
static MARK_READ_EVENT = 'addon_mod_forum_mark_read';
|
||||||
|
static LEAVING_POSTS_PAGE = 'addon_mod_forum_leaving_posts_page';
|
||||||
|
|
||||||
static PREFERENCE_SORTORDER = 'forum_discussionlistsortorder';
|
static PREFERENCE_SORTORDER = 'forum_discussionlistsortorder';
|
||||||
static SORTORDER_LASTPOST_DESC = 1;
|
static SORTORDER_LASTPOST_DESC = 1;
|
||||||
|
@ -244,13 +245,14 @@ export class AddonModForumProvider {
|
||||||
* Check if a user can post to all groups.
|
* Check if a user can post to all groups.
|
||||||
*
|
*
|
||||||
* @param forumId Forum ID.
|
* @param forumId Forum ID.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved with an object with the following properties:
|
* @return Promise resolved with an object with the following properties:
|
||||||
* - status (boolean)
|
* - status (boolean)
|
||||||
* - canpindiscussions (boolean)
|
* - canpindiscussions (boolean)
|
||||||
* - cancreateattachment (boolean)
|
* - cancreateattachment (boolean)
|
||||||
*/
|
*/
|
||||||
canAddDiscussionToAll(forumId: number): Promise<any> {
|
canAddDiscussionToAll(forumId: number, siteId?: string): Promise<any> {
|
||||||
return this.canAddDiscussion(forumId, AddonModForumProvider.ALL_PARTICIPANTS);
|
return this.canAddDiscussion(forumId, AddonModForumProvider.ALL_PARTICIPANTS, siteId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -107,12 +107,13 @@ export class AddonModForumPrefetchHandler extends CoreCourseActivityPrefetchHand
|
||||||
* Get the posts to be prefetched.
|
* Get the posts to be prefetched.
|
||||||
*
|
*
|
||||||
* @param forum Forum instance.
|
* @param forum Forum instance.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved with array of posts.
|
* @return Promise resolved with array of posts.
|
||||||
*/
|
*/
|
||||||
protected getPostsForPrefetch(forum: any): Promise<any[]> {
|
protected getPostsForPrefetch(forum: any, siteId?: string): Promise<any[]> {
|
||||||
const promises = this.forumProvider.getAvailableSortOrders().map((sortOrder) => {
|
const promises = this.forumProvider.getAvailableSortOrders().map((sortOrder) => {
|
||||||
// Get discussions in first 2 pages.
|
// Get discussions in first 2 pages.
|
||||||
return this.forumProvider.getDiscussionsInPages(forum.id, sortOrder.value, false, 2).then((response) => {
|
return this.forumProvider.getDiscussionsInPages(forum.id, sortOrder.value, false, 2, 0, siteId).then((response) => {
|
||||||
if (response.error) {
|
if (response.error) {
|
||||||
return Promise.reject(null);
|
return Promise.reject(null);
|
||||||
}
|
}
|
||||||
|
@ -120,7 +121,7 @@ export class AddonModForumPrefetchHandler extends CoreCourseActivityPrefetchHand
|
||||||
const promises = [];
|
const promises = [];
|
||||||
|
|
||||||
response.discussions.forEach((discussion) => {
|
response.discussions.forEach((discussion) => {
|
||||||
promises.push(this.forumProvider.getDiscussionPosts(discussion.discussion));
|
promises.push(this.forumProvider.getDiscussionPosts(discussion.discussion, siteId));
|
||||||
});
|
});
|
||||||
|
|
||||||
return Promise.all(promises);
|
return Promise.all(promises);
|
||||||
|
@ -200,41 +201,31 @@ export class AddonModForumPrefetchHandler extends CoreCourseActivityPrefetchHand
|
||||||
*/
|
*/
|
||||||
protected prefetchForum(module: any, courseId: number, single: boolean, siteId: string): Promise<any> {
|
protected prefetchForum(module: any, courseId: number, single: boolean, siteId: string): Promise<any> {
|
||||||
// Get the forum data.
|
// Get the forum data.
|
||||||
return this.forumProvider.getForum(courseId, module.id).then((forum) => {
|
return this.forumProvider.getForum(courseId, module.id, siteId).then((forum) => {
|
||||||
const promises = [];
|
const promises = [];
|
||||||
|
|
||||||
// Prefetch the posts.
|
// Prefetch the posts.
|
||||||
promises.push(this.getPostsForPrefetch(forum).then((posts) => {
|
promises.push(this.getPostsForPrefetch(forum, siteId).then((posts) => {
|
||||||
const promises = [];
|
const promises = [];
|
||||||
|
|
||||||
// Gather user profile images.
|
const files = this.getIntroFilesFromInstance(module, forum).concat(this.getPostsFiles(posts));
|
||||||
const avatars = {}; // List of user avatars, preventing duplicates.
|
|
||||||
|
|
||||||
posts.forEach((post) => {
|
|
||||||
if (post.userpictureurl) {
|
|
||||||
avatars[post.userpictureurl] = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Prefetch intro files, attachments, embedded files and user avatars.
|
|
||||||
const avatarFiles = Object.keys(avatars).map((url) => {
|
|
||||||
return { fileurl: url };
|
|
||||||
});
|
|
||||||
const files = this.getIntroFilesFromInstance(module, forum).concat(this.getPostsFiles(posts)).concat(avatarFiles);
|
|
||||||
promises.push(this.filepoolProvider.addFilesToQueue(siteId, files, this.component, module.id));
|
promises.push(this.filepoolProvider.addFilesToQueue(siteId, files, this.component, module.id));
|
||||||
|
|
||||||
// Prefetch groups data.
|
// Prefetch groups data.
|
||||||
promises.push(this.prefetchGroupsInfo(forum, courseId, forum.cancreatediscussions));
|
promises.push(this.prefetchGroupsInfo(forum, courseId, forum.cancreatediscussions, siteId));
|
||||||
|
|
||||||
|
// Prefetch avatars.
|
||||||
|
promises.push(this.userProvider.prefetchUserAvatars(posts, 'userpictureurl', siteId));
|
||||||
|
|
||||||
return Promise.all(promises);
|
return Promise.all(promises);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Prefetch access information.
|
// Prefetch access information.
|
||||||
promises.push(this.forumProvider.getAccessInformation(forum.id));
|
promises.push(this.forumProvider.getAccessInformation(forum.id, false, siteId));
|
||||||
|
|
||||||
// Prefetch sort order preference.
|
// Prefetch sort order preference.
|
||||||
if (this.forumProvider.isDiscussionListSortingAvailable()) {
|
if (this.forumProvider.isDiscussionListSortingAvailable()) {
|
||||||
promises.push(this.userProvider.getUserPreference(AddonModForumProvider.PREFERENCE_SORTORDER));
|
promises.push(this.userProvider.getUserPreference(AddonModForumProvider.PREFERENCE_SORTORDER, siteId));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.all(promises);
|
return Promise.all(promises);
|
||||||
|
@ -247,30 +238,31 @@ export class AddonModForumPrefetchHandler extends CoreCourseActivityPrefetchHand
|
||||||
* @param module The module object returned by WS.
|
* @param module The module object returned by WS.
|
||||||
* @param courseI Course ID the module belongs to.
|
* @param courseI Course ID the module belongs to.
|
||||||
* @param canCreateDiscussions Whether the user can create discussions in the forum.
|
* @param canCreateDiscussions Whether the user can create discussions in the forum.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved when group data has been prefetched.
|
* @return Promise resolved when group data has been prefetched.
|
||||||
*/
|
*/
|
||||||
protected prefetchGroupsInfo(forum: any, courseId: number, canCreateDiscussions: boolean): any {
|
protected prefetchGroupsInfo(forum: any, courseId: number, canCreateDiscussions: boolean, siteId?: string): any {
|
||||||
// Check group mode.
|
// Check group mode.
|
||||||
return this.groupsProvider.getActivityGroupMode(forum.cmid).then((mode) => {
|
return this.groupsProvider.getActivityGroupMode(forum.cmid, siteId).then((mode) => {
|
||||||
if (mode !== CoreGroupsProvider.SEPARATEGROUPS && mode !== CoreGroupsProvider.VISIBLEGROUPS) {
|
if (mode !== CoreGroupsProvider.SEPARATEGROUPS && mode !== CoreGroupsProvider.VISIBLEGROUPS) {
|
||||||
// Activity doesn't use groups. Prefetch canAddDiscussionToAll to determine if user can pin/attach.
|
// Activity doesn't use groups. Prefetch canAddDiscussionToAll to determine if user can pin/attach.
|
||||||
return this.forumProvider.canAddDiscussionToAll(forum.id).catch(() => {
|
return this.forumProvider.canAddDiscussionToAll(forum.id, siteId).catch(() => {
|
||||||
// Ignore errors.
|
// Ignore errors.
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Activity uses groups, prefetch allowed groups.
|
// Activity uses groups, prefetch allowed groups.
|
||||||
return this.groupsProvider.getActivityAllowedGroups(forum.cmid).then((result) => {
|
return this.groupsProvider.getActivityAllowedGroups(forum.cmid, undefined, siteId).then((result) => {
|
||||||
if (mode === CoreGroupsProvider.SEPARATEGROUPS) {
|
if (mode === CoreGroupsProvider.SEPARATEGROUPS) {
|
||||||
// Groups are already filtered by WS. Prefetch canAddDiscussionToAll to determine if user can pin/attach.
|
// Groups are already filtered by WS. Prefetch canAddDiscussionToAll to determine if user can pin/attach.
|
||||||
return this.forumProvider.canAddDiscussionToAll(forum.id).catch(() => {
|
return this.forumProvider.canAddDiscussionToAll(forum.id, siteId).catch(() => {
|
||||||
// Ignore errors.
|
// Ignore errors.
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (canCreateDiscussions) {
|
if (canCreateDiscussions) {
|
||||||
// Prefetch data to check the visible groups when creating discussions.
|
// Prefetch data to check the visible groups when creating discussions.
|
||||||
return this.forumProvider.canAddDiscussionToAll(forum.id).catch(() => {
|
return this.forumProvider.canAddDiscussionToAll(forum.id, siteId).catch(() => {
|
||||||
// The call failed, let's assume he can't.
|
// The call failed, let's assume he can't.
|
||||||
return {
|
return {
|
||||||
status: false
|
status: false
|
||||||
|
@ -284,7 +276,7 @@ export class AddonModForumPrefetchHandler extends CoreCourseActivityPrefetchHand
|
||||||
// The user can't post to all groups, let's check which groups he can post to.
|
// The user can't post to all groups, let's check which groups he can post to.
|
||||||
const groupPromises = [];
|
const groupPromises = [];
|
||||||
result.groups.forEach((group) => {
|
result.groups.forEach((group) => {
|
||||||
groupPromises.push(this.forumProvider.canAddDiscussion(forum.id, group.id).catch(() => {
|
groupPromises.push(this.forumProvider.canAddDiscussion(forum.id, group.id, siteId).catch(() => {
|
||||||
// Ignore errors.
|
// Ignore errors.
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
|
@ -20,6 +20,7 @@ import { CoreComponentsModule } from '@components/components.module';
|
||||||
import { CoreDirectivesModule } from '@directives/directives.module';
|
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||||
import { CorePipesModule } from '@pipes/pipes.module';
|
import { CorePipesModule } from '@pipes/pipes.module';
|
||||||
import { CoreCourseComponentsModule } from '@core/course/components/components.module';
|
import { CoreCourseComponentsModule } from '@core/course/components/components.module';
|
||||||
|
import { CoreSearchComponentsModule } from '@core/search/components/components.module';
|
||||||
import { AddonModGlossaryIndexComponent } from './index/index';
|
import { AddonModGlossaryIndexComponent } from './index/index';
|
||||||
import { AddonModGlossaryModePickerPopoverComponent } from './mode-picker/mode-picker';
|
import { AddonModGlossaryModePickerPopoverComponent } from './mode-picker/mode-picker';
|
||||||
|
|
||||||
|
@ -35,7 +36,8 @@ import { AddonModGlossaryModePickerPopoverComponent } from './mode-picker/mode-p
|
||||||
CoreComponentsModule,
|
CoreComponentsModule,
|
||||||
CoreDirectivesModule,
|
CoreDirectivesModule,
|
||||||
CorePipesModule,
|
CorePipesModule,
|
||||||
CoreCourseComponentsModule
|
CoreCourseComponentsModule,
|
||||||
|
CoreSearchComponentsModule,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
<!-- Buttons to add to the header. -->
|
<!-- Buttons to add to the header. -->
|
||||||
<core-navbar-buttons end>
|
<core-navbar-buttons end>
|
||||||
<button *ngIf="glossary" ion-button icon-only (click)="openModePicker($event)" [attr.aria-label]="'addon.mod_glossary.browsemode' | translate">
|
<button *ngIf="glossary && glossary.browsemodes && glossary.browsemodes.length > 1" ion-button icon-only (click)="openModePicker($event)" [attr.aria-label]="'addon.mod_glossary.browsemode' | translate">
|
||||||
<ion-icon name="funnel"></ion-icon>
|
<core-icon name="fa-sort"></core-icon>
|
||||||
|
</button>
|
||||||
|
<button *ngIf="glossary" ion-button icon-only (click)="toggleSearch()" [attr.aria-label]="'addon.mod_glossary.bysearch' | translate">
|
||||||
|
<ion-icon name="search"></ion-icon>
|
||||||
</button>
|
</button>
|
||||||
<core-context-menu>
|
<core-context-menu>
|
||||||
<core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate" [href]="externalUrl" [iconAction]="'open'"></core-context-menu-item>
|
<core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate" [href]="externalUrl" [iconAction]="'open'"></core-context-menu-item>
|
||||||
|
@ -29,16 +32,16 @@
|
||||||
<ion-icon name="warning"></ion-icon> {{ 'core.hasdatatosync' | translate:{$a: moduleName} }}
|
<ion-icon name="warning"></ion-icon> {{ 'core.hasdatatosync' | translate:{$a: moduleName} }}
|
||||||
</ion-card>
|
</ion-card>
|
||||||
|
|
||||||
<core-search-box *ngIf="viewMode == 'search'" (onSubmit)="search($event)" [placeholder]="'addon.mod_glossary.searchquery' | translate" [autoFocus]="true" [lengthCheck]="2" [showClear]="false"></core-search-box>
|
<core-search-box *ngIf="isSearch" (onSubmit)="search($event)" [placeholder]="'addon.mod_glossary.searchquery' | translate" [autoFocus]="true" [lengthCheck]="2" (onClear)="toggleSearch($event)" searchArea="AddonModGlossary-{{module.id}}"></core-search-box>
|
||||||
|
|
||||||
<core-loading [hideUntil]="loaded" class="core-loading-center">
|
<core-loading [hideUntil]="loaded" class="core-loading-center">
|
||||||
|
|
||||||
<ion-list *ngIf="viewMode != 'search' && offlineEntries.length > 0">
|
<ion-list *ngIf="!isSearch && offlineEntries.length > 0">
|
||||||
<ion-item-divider>
|
<ion-item-divider>
|
||||||
{{ 'addon.mod_glossary.entriestobesynced' | translate }}
|
{{ 'addon.mod_glossary.entriestobesynced' | translate }}
|
||||||
</ion-item-divider>
|
</ion-item-divider>
|
||||||
<a ion-item *ngFor="let entry of offlineEntries" (click)="openNewEntry(entry)" detail-none>
|
<a ion-item *ngFor="let entry of offlineEntries" (click)="openNewEntry(entry)" detail-none>
|
||||||
<p>{{entry.concept}}</p>
|
<p><core-format-text [text]="entry.concept" contextLevel="module" [contextInstanceId]="glossary.coursemodule" [courseId]="courseId"></core-format-text></p>
|
||||||
</a>
|
</a>
|
||||||
</ion-list>
|
</ion-list>
|
||||||
|
|
||||||
|
@ -50,7 +53,7 @@
|
||||||
</ion-item-divider>
|
</ion-item-divider>
|
||||||
|
|
||||||
<a ion-item (click)="openEntry(entry.id)" [class.core-split-item-selected]="entry.id == selectedEntry" detail-none>
|
<a ion-item (click)="openEntry(entry.id)" [class.core-split-item-selected]="entry.id == selectedEntry" detail-none>
|
||||||
<p>{{entry.concept}}</p>
|
<p><core-format-text [text]="entry.concept" contextLevel="module" [contextInstanceId]="glossary.coursemodule" [courseId]="courseId"></core-format-text></p>
|
||||||
</a>
|
</a>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</ion-list>
|
</ion-list>
|
||||||
|
|
|
@ -25,7 +25,7 @@ import { AddonModGlossarySyncProvider } from '../../providers/sync';
|
||||||
import { AddonModGlossaryModePickerPopoverComponent } from '../mode-picker/mode-picker';
|
import { AddonModGlossaryModePickerPopoverComponent } from '../mode-picker/mode-picker';
|
||||||
import { AddonModGlossaryPrefetchHandler } from '../../providers/prefetch-handler';
|
import { AddonModGlossaryPrefetchHandler } from '../../providers/prefetch-handler';
|
||||||
|
|
||||||
type FetchMode = 'author_all' | 'cat_all' | 'newest_first' | 'recently_updated' | 'search' | 'letter_all';
|
type FetchMode = 'author_all' | 'cat_all' | 'newest_first' | 'recently_updated' | 'letter_all';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that displays a glossary entry page.
|
* Component that displays a glossary entry page.
|
||||||
|
@ -41,8 +41,6 @@ export class AddonModGlossaryIndexComponent extends CoreCourseModuleMainActivity
|
||||||
component = AddonModGlossaryProvider.COMPONENT;
|
component = AddonModGlossaryProvider.COMPONENT;
|
||||||
moduleName = 'glossary';
|
moduleName = 'glossary';
|
||||||
|
|
||||||
fetchMode: FetchMode;
|
|
||||||
viewMode: string;
|
|
||||||
isSearch = false;
|
isSearch = false;
|
||||||
entries = [];
|
entries = [];
|
||||||
offlineEntries = [];
|
offlineEntries = [];
|
||||||
|
@ -60,6 +58,10 @@ export class AddonModGlossaryIndexComponent extends CoreCourseModuleMainActivity
|
||||||
protected showDivider: (entry: any, previous?: any) => boolean;
|
protected showDivider: (entry: any, previous?: any) => boolean;
|
||||||
protected getDivider: (entry: any) => string;
|
protected getDivider: (entry: any) => string;
|
||||||
protected addEntryObserver: any;
|
protected addEntryObserver: any;
|
||||||
|
protected fetchMode: FetchMode;
|
||||||
|
protected viewMode: string;
|
||||||
|
protected fetchedEntriesCanLoadMore = false;
|
||||||
|
protected fetchedEntries = [];
|
||||||
|
|
||||||
hasOfflineRatings: boolean;
|
hasOfflineRatings: boolean;
|
||||||
protected ratingOfflineObserver: any;
|
protected ratingOfflineObserver: any;
|
||||||
|
@ -187,6 +189,21 @@ export class AddonModGlossaryIndexComponent extends CoreCourseModuleMainActivity
|
||||||
Array.prototype.push.apply(this.entries, result.entries);
|
Array.prototype.push.apply(this.entries, result.entries);
|
||||||
} else {
|
} else {
|
||||||
this.entries = result.entries;
|
this.entries = result.entries;
|
||||||
|
|
||||||
|
if (this.splitviewCtrl.isOn()) {
|
||||||
|
// Load the first entry.
|
||||||
|
if (this.entries.length > 0) {
|
||||||
|
const found = this.selectedEntry && this.entries.some((entry) => entry.id == this.selectedEntry);
|
||||||
|
|
||||||
|
// The current selected entry is not found in the current list, open first item.
|
||||||
|
if (!found) {
|
||||||
|
this.openEntry(this.entries[0].id);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.selectedEntry = null;
|
||||||
|
this.splitviewCtrl.emptyDetails();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.canLoadMore = this.entries.length < result.count;
|
this.canLoadMore = this.entries.length < result.count;
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
|
@ -252,6 +269,7 @@ export class AddonModGlossaryIndexComponent extends CoreCourseModuleMainActivity
|
||||||
*/
|
*/
|
||||||
protected switchMode(mode: FetchMode): void {
|
protected switchMode(mode: FetchMode): void {
|
||||||
this.fetchMode = mode;
|
this.fetchMode = mode;
|
||||||
|
this.isSearch = false;
|
||||||
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case 'author_all':
|
case 'author_all':
|
||||||
|
@ -294,15 +312,6 @@ export class AddonModGlossaryIndexComponent extends CoreCourseModuleMainActivity
|
||||||
this.getDivider = null;
|
this.getDivider = null;
|
||||||
this.showDivider = (): boolean => false;
|
this.showDivider = (): boolean => false;
|
||||||
break;
|
break;
|
||||||
case 'search':
|
|
||||||
// Search for entries.
|
|
||||||
this.viewMode = 'search';
|
|
||||||
this.fetchFunction = this.glossaryProvider.getEntriesBySearch;
|
|
||||||
this.fetchInvalidate = this.glossaryProvider.invalidateEntriesBySearch;
|
|
||||||
this.fetchArguments = null; // Dynamically set later.
|
|
||||||
this.getDivider = null;
|
|
||||||
this.showDivider = (): boolean => false;
|
|
||||||
break;
|
|
||||||
case 'letter_all':
|
case 'letter_all':
|
||||||
default:
|
default:
|
||||||
// Consider it is 'letter_all'.
|
// Consider it is 'letter_all'.
|
||||||
|
@ -311,7 +320,12 @@ export class AddonModGlossaryIndexComponent extends CoreCourseModuleMainActivity
|
||||||
this.fetchFunction = this.glossaryProvider.getEntriesByLetter;
|
this.fetchFunction = this.glossaryProvider.getEntriesByLetter;
|
||||||
this.fetchInvalidate = this.glossaryProvider.invalidateEntriesByLetter;
|
this.fetchInvalidate = this.glossaryProvider.invalidateEntriesByLetter;
|
||||||
this.fetchArguments = [this.glossary.id, 'ALL'];
|
this.fetchArguments = [this.glossary.id, 'ALL'];
|
||||||
this.getDivider = (entry: any): string => entry.concept.substr(0, 1).toUpperCase();
|
this.getDivider = (entry: any): string => {
|
||||||
|
// Try to get the first letter without HTML tags.
|
||||||
|
const noTags = this.textUtils.cleanTags(entry.concept);
|
||||||
|
|
||||||
|
return (noTags || entry.concept).substr(0, 1).toUpperCase();
|
||||||
|
};
|
||||||
this.showDivider = (entry?: any, previous?: any): boolean => {
|
this.showDivider = (entry?: any, previous?: any): boolean => {
|
||||||
return !previous || this.getDivider(entry) != this.getDivider(previous);
|
return !previous || this.getDivider(entry) != this.getDivider(previous);
|
||||||
};
|
};
|
||||||
|
@ -341,26 +355,15 @@ export class AddonModGlossaryIndexComponent extends CoreCourseModuleMainActivity
|
||||||
*/
|
*/
|
||||||
openModePicker(event: MouseEvent): void {
|
openModePicker(event: MouseEvent): void {
|
||||||
const popover = this.popoverCtrl.create(AddonModGlossaryModePickerPopoverComponent, {
|
const popover = this.popoverCtrl.create(AddonModGlossaryModePickerPopoverComponent, {
|
||||||
glossary: this.glossary,
|
browsemodes: this.glossary.browsemodes,
|
||||||
selectedMode: this.fetchMode
|
selectedMode: this.isSearch ? '' : this.fetchMode
|
||||||
});
|
});
|
||||||
|
|
||||||
popover.onDidDismiss((newMode: FetchMode) => {
|
popover.onDidDismiss((mode: FetchMode) => {
|
||||||
if (newMode === this.fetchMode) {
|
if (mode !== this.fetchMode) {
|
||||||
return;
|
this.changeFetchMode(mode);
|
||||||
}
|
} else if (this.isSearch) {
|
||||||
|
this.toggleSearch();
|
||||||
this.loadingMessage = this.translate.instant('core.loading');
|
|
||||||
this.domUtils.scrollToTop(this.content);
|
|
||||||
this.switchMode(newMode);
|
|
||||||
|
|
||||||
if (this.fetchMode === 'search') {
|
|
||||||
// If it's not an instant search, then we reset the values.
|
|
||||||
this.entries = [];
|
|
||||||
this.canLoadMore = false;
|
|
||||||
} else {
|
|
||||||
this.loaded = false;
|
|
||||||
this.loadContent();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -369,6 +372,44 @@ export class AddonModGlossaryIndexComponent extends CoreCourseModuleMainActivity
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggles between search and fetch mode.
|
||||||
|
*/
|
||||||
|
toggleSearch(): void {
|
||||||
|
if (this.isSearch) {
|
||||||
|
this.isSearch = false;
|
||||||
|
this.entries = this.fetchedEntries;
|
||||||
|
this.canLoadMore = this.fetchedEntriesCanLoadMore;
|
||||||
|
this.switchMode(this.fetchMode);
|
||||||
|
} else {
|
||||||
|
// Search for entries.
|
||||||
|
this.fetchFunction = this.glossaryProvider.getEntriesBySearch;
|
||||||
|
this.fetchInvalidate = this.glossaryProvider.invalidateEntriesBySearch;
|
||||||
|
this.fetchArguments = null; // Dynamically set later.
|
||||||
|
this.getDivider = null;
|
||||||
|
this.showDivider = (): boolean => false;
|
||||||
|
this.isSearch = true;
|
||||||
|
|
||||||
|
this.fetchedEntries = this.entries;
|
||||||
|
this.fetchedEntriesCanLoadMore = this.canLoadMore;
|
||||||
|
this.canLoadMore = false;
|
||||||
|
this.entries = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change fetch mode
|
||||||
|
* @param {FetchMode} mode [description]
|
||||||
|
*/
|
||||||
|
changeFetchMode(mode: FetchMode): void {
|
||||||
|
this.isSearch = false;
|
||||||
|
this.loadingMessage = this.translate.instant('core.loading');
|
||||||
|
this.domUtils.scrollToTop(this.content);
|
||||||
|
this.switchMode(mode);
|
||||||
|
this.loaded = false;
|
||||||
|
this.loadContent();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens an entry.
|
* Opens an entry.
|
||||||
*
|
*
|
||||||
|
|
|
@ -27,14 +27,10 @@ export class AddonModGlossaryModePickerPopoverComponent {
|
||||||
selectedMode: string;
|
selectedMode: string;
|
||||||
|
|
||||||
constructor(navParams: NavParams, private viewCtrl: ViewController) {
|
constructor(navParams: NavParams, private viewCtrl: ViewController) {
|
||||||
this.selectedMode = navParams.get('selectedMode');
|
this.selectedMode = navParams.get('selectedMode') || '';
|
||||||
const glossary = navParams.get('glossary');
|
const browsemodes = navParams.get('browsemodes');
|
||||||
|
|
||||||
// Preparing browse modes.
|
browsemodes.forEach((mode) => {
|
||||||
this.modes = [
|
|
||||||
{key: 'search', langkey: 'addon.mod_glossary.bysearch'}
|
|
||||||
];
|
|
||||||
glossary.browsemodes.forEach((mode) => {
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case 'letter' :
|
case 'letter' :
|
||||||
this.modes.push({key: 'letter_all', langkey: 'addon.mod_glossary.byalphabet'});
|
this.modes.push({key: 'letter_all', langkey: 'addon.mod_glossary.byalphabet'});
|
||||||
|
|
|
@ -31,7 +31,6 @@ import { AddonModGlossaryListLinkHandler } from './providers/list-link-handler';
|
||||||
import { AddonModGlossaryEditLinkHandler } from './providers/edit-link-handler';
|
import { AddonModGlossaryEditLinkHandler } from './providers/edit-link-handler';
|
||||||
import { AddonModGlossaryTagAreaHandler } from './providers/tag-area-handler';
|
import { AddonModGlossaryTagAreaHandler } from './providers/tag-area-handler';
|
||||||
import { AddonModGlossaryComponentsModule } from './components/components.module';
|
import { AddonModGlossaryComponentsModule } from './components/components.module';
|
||||||
import { CoreUpdateManagerProvider } from '@providers/update-manager';
|
|
||||||
|
|
||||||
// List of providers (without handlers).
|
// List of providers (without handlers).
|
||||||
export const ADDON_MOD_GLOSSARY_PROVIDERS: any[] = [
|
export const ADDON_MOD_GLOSSARY_PROVIDERS: any[] = [
|
||||||
|
@ -67,7 +66,7 @@ export class AddonModGlossaryModule {
|
||||||
prefetchDelegate: CoreCourseModulePrefetchDelegate, prefetchHandler: AddonModGlossaryPrefetchHandler,
|
prefetchDelegate: CoreCourseModulePrefetchDelegate, prefetchHandler: AddonModGlossaryPrefetchHandler,
|
||||||
cronDelegate: CoreCronDelegate, syncHandler: AddonModGlossarySyncCronHandler, linksDelegate: CoreContentLinksDelegate,
|
cronDelegate: CoreCronDelegate, syncHandler: AddonModGlossarySyncCronHandler, linksDelegate: CoreContentLinksDelegate,
|
||||||
indexHandler: AddonModGlossaryIndexLinkHandler, discussionHandler: AddonModGlossaryEntryLinkHandler,
|
indexHandler: AddonModGlossaryIndexLinkHandler, discussionHandler: AddonModGlossaryEntryLinkHandler,
|
||||||
updateManager: CoreUpdateManagerProvider, listLinkHandler: AddonModGlossaryListLinkHandler,
|
listLinkHandler: AddonModGlossaryListLinkHandler,
|
||||||
editLinkHandler: AddonModGlossaryEditLinkHandler, tagAreaDelegate: CoreTagAreaDelegate,
|
editLinkHandler: AddonModGlossaryEditLinkHandler, tagAreaDelegate: CoreTagAreaDelegate,
|
||||||
tagAreaHandler: AddonModGlossaryTagAreaHandler) {
|
tagAreaHandler: AddonModGlossaryTagAreaHandler) {
|
||||||
|
|
||||||
|
@ -79,29 +78,5 @@ export class AddonModGlossaryModule {
|
||||||
linksDelegate.registerHandler(listLinkHandler);
|
linksDelegate.registerHandler(listLinkHandler);
|
||||||
linksDelegate.registerHandler(editLinkHandler);
|
linksDelegate.registerHandler(editLinkHandler);
|
||||||
tagAreaDelegate.registerHandler(tagAreaHandler);
|
tagAreaDelegate.registerHandler(tagAreaHandler);
|
||||||
|
|
||||||
// Allow migrating the tables from the old app to the new schema.
|
|
||||||
updateManager.registerSiteTableMigration({
|
|
||||||
name: 'mma_mod_glossary_add_entry',
|
|
||||||
newName: AddonModGlossaryOfflineProvider.ENTRIES_TABLE,
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
name: 'glossaryAndConcept',
|
|
||||||
delete: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'glossaryAndUser',
|
|
||||||
delete: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'options',
|
|
||||||
type: 'object'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'attachments',
|
|
||||||
type: 'object'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,24 +8,24 @@
|
||||||
</ion-header>
|
</ion-header>
|
||||||
<ion-content>
|
<ion-content>
|
||||||
<core-loading [hideUntil]="loaded">
|
<core-loading [hideUntil]="loaded">
|
||||||
<ion-list>
|
<form ion-list #editFormEl>
|
||||||
<ion-item>
|
<ion-item>
|
||||||
<ion-label stacked>{{ 'addon.mod_glossary.concept' | translate }}</ion-label>
|
<ion-label stacked>{{ 'addon.mod_glossary.concept' | translate }}</ion-label>
|
||||||
<ion-input type="text" [placeholder]="'addon.mod_glossary.concept' | translate" [(ngModel)]="entry.concept"></ion-input>
|
<ion-input type="text" [placeholder]="'addon.mod_glossary.concept' | translate" [(ngModel)]="entry.concept" name="concept"></ion-input>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item>
|
<ion-item>
|
||||||
<ion-label stacked>{{ 'addon.mod_glossary.definition' | translate }}</ion-label>
|
<ion-label stacked>{{ 'addon.mod_glossary.definition' | translate }}</ion-label>
|
||||||
<core-rich-text-editor item-content [control]="definitionControl" (contentChanged)="onDefinitionChange($event)" [placeholder]="'addon.mod_glossary.definition' | translate" name="addon_mod_glossary_edit" [component]="component" [componentId]="glossary.cmid"></core-rich-text-editor>
|
<core-rich-text-editor item-content [control]="definitionControl" (contentChanged)="onDefinitionChange($event)" [placeholder]="'addon.mod_glossary.definition' | translate" name="addon_mod_glossary_edit" [component]="component" [componentId]="glossary.cmid" [autoSave]="true" contextLevel="module" [contextInstanceId]="module.id" elementId="definition_editor" [draftExtraParams]="editorExtraParams"></core-rich-text-editor>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item *ngIf="categories.length > 0">
|
<ion-item *ngIf="categories.length > 0">
|
||||||
<ion-label stacked id="addon-mod-glossary-categories-label">{{ 'addon.mod_glossary.categories' | translate }}</ion-label>
|
<ion-label stacked id="addon-mod-glossary-categories-label">{{ 'addon.mod_glossary.categories' | translate }}</ion-label>
|
||||||
<ion-select [(ngModel)]="options.categories" multiple="true" aria-labelledby="addon-mod-glossary-categories-label" interface="action-sheet" [placeholder]="'addon.mod_glossary.categories' | translate">
|
<ion-select [(ngModel)]="options.categories" multiple="true" aria-labelledby="addon-mod-glossary-categories-label" interface="action-sheet" [placeholder]="'addon.mod_glossary.categories' | translate" name="categories">
|
||||||
<ion-option *ngFor="let category of categories" [value]="category.id">{{ category.name }}</ion-option>
|
<ion-option *ngFor="let category of categories" [value]="category.id">{{ category.name }}</ion-option>
|
||||||
</ion-select>
|
</ion-select>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item>
|
<ion-item>
|
||||||
<ion-label stacked id="addon-mod-glossary-aliases-label">{{ 'addon.mod_glossary.aliases' | translate }}</ion-label>
|
<ion-label stacked id="addon-mod-glossary-aliases-label">{{ 'addon.mod_glossary.aliases' | translate }}</ion-label>
|
||||||
<ion-textarea [(ngModel)]="options.aliases" rows="1" core-auto-rows aria-labelledby="addon-mod-glossary-aliases-label"></ion-textarea>
|
<ion-textarea [(ngModel)]="options.aliases" rows="1" core-auto-rows aria-labelledby="addon-mod-glossary-aliases-label" name="aliases"></ion-textarea>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item-divider>{{ 'addon.mod_glossary.attachment' | translate }}</ion-item-divider>
|
<ion-item-divider>{{ 'addon.mod_glossary.attachment' | translate }}</ion-item-divider>
|
||||||
<core-attachments [files]="attachments" [component]="component" [componentId]="glossary.cmid" [allowOffline]="true"></core-attachments>
|
<core-attachments [files]="attachments" [component]="component" [componentId]="glossary.cmid" [allowOffline]="true"></core-attachments>
|
||||||
|
@ -33,17 +33,17 @@
|
||||||
<ion-item-divider>{{ 'addon.mod_glossary.linking' | translate }}</ion-item-divider>
|
<ion-item-divider>{{ 'addon.mod_glossary.linking' | translate }}</ion-item-divider>
|
||||||
<ion-item text-wrap>
|
<ion-item text-wrap>
|
||||||
<ion-label>{{ 'addon.mod_glossary.entryusedynalink' | translate }}</ion-label>
|
<ion-label>{{ 'addon.mod_glossary.entryusedynalink' | translate }}</ion-label>
|
||||||
<ion-toggle [(ngModel)]="options.usedynalink"></ion-toggle>
|
<ion-toggle [(ngModel)]="options.usedynalink" name="usedynalink"></ion-toggle>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item text-wrap>
|
<ion-item text-wrap>
|
||||||
<ion-label>{{ 'addon.mod_glossary.casesensitive' | translate }}</ion-label>
|
<ion-label>{{ 'addon.mod_glossary.casesensitive' | translate }}</ion-label>
|
||||||
<ion-toggle [disabled]="!options.usedynalink" [(ngModel)]="options.casesensitive"></ion-toggle>
|
<ion-toggle [disabled]="!options.usedynalink" [(ngModel)]="options.casesensitive" name="casesensitive"></ion-toggle>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item text-wrap>
|
<ion-item text-wrap>
|
||||||
<ion-label>{{ 'addon.mod_glossary.fullmatch' | translate }}</ion-label>
|
<ion-label>{{ 'addon.mod_glossary.fullmatch' | translate }}</ion-label>
|
||||||
<ion-toggle [disabled]="!options.usedynalink" [(ngModel)]="options.fullmatch"></ion-toggle>
|
<ion-toggle [disabled]="!options.usedynalink" [(ngModel)]="options.fullmatch" name="fullmatch"></ion-toggle>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</ion-list>
|
</form>
|
||||||
</core-loading>
|
</core-loading>
|
||||||
</ion-content>
|
</ion-content>
|
||||||
|
|
|
@ -18,6 +18,7 @@ import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { CoreComponentsModule } from '@components/components.module';
|
import { CoreComponentsModule } from '@components/components.module';
|
||||||
import { CoreDirectivesModule } from '@directives/directives.module';
|
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||||
import { AddonModGlossaryEditPage } from './edit';
|
import { AddonModGlossaryEditPage } from './edit';
|
||||||
|
import { CoreEditorComponentsModule } from '@core/editor/components/components.module';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
|
@ -26,6 +27,7 @@ import { AddonModGlossaryEditPage } from './edit';
|
||||||
imports: [
|
imports: [
|
||||||
CoreComponentsModule,
|
CoreComponentsModule,
|
||||||
CoreDirectivesModule,
|
CoreDirectivesModule,
|
||||||
|
CoreEditorComponentsModule,
|
||||||
IonicPageModule.forChild(AddonModGlossaryEditPage),
|
IonicPageModule.forChild(AddonModGlossaryEditPage),
|
||||||
TranslateModule.forChild()
|
TranslateModule.forChild()
|
||||||
],
|
],
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit, ViewChild, ElementRef } from '@angular/core';
|
||||||
import { FormControl } from '@angular/forms';
|
import { FormControl } from '@angular/forms';
|
||||||
import { IonicPage, NavController, NavParams } from 'ionic-angular';
|
import { IonicPage, NavController, NavParams } from 'ionic-angular';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
@ -34,6 +34,8 @@ import { AddonModGlossaryHelperProvider } from '../../providers/helper';
|
||||||
templateUrl: 'edit.html',
|
templateUrl: 'edit.html',
|
||||||
})
|
})
|
||||||
export class AddonModGlossaryEditPage implements OnInit {
|
export class AddonModGlossaryEditPage implements OnInit {
|
||||||
|
@ViewChild('editFormEl') formElement: ElementRef;
|
||||||
|
|
||||||
component = AddonModGlossaryProvider.COMPONENT;
|
component = AddonModGlossaryProvider.COMPONENT;
|
||||||
loaded = false;
|
loaded = false;
|
||||||
entry = {
|
entry = {
|
||||||
|
@ -51,6 +53,7 @@ export class AddonModGlossaryEditPage implements OnInit {
|
||||||
attachments = [];
|
attachments = [];
|
||||||
definitionControl = new FormControl();
|
definitionControl = new FormControl();
|
||||||
categories = [];
|
categories = [];
|
||||||
|
editorExtraParams: {[name: string]: any} = {};
|
||||||
|
|
||||||
protected courseId: number;
|
protected courseId: number;
|
||||||
protected module: any;
|
protected module: any;
|
||||||
|
@ -113,6 +116,10 @@ export class AddonModGlossaryEditPage implements OnInit {
|
||||||
this.originalData.files = files.slice();
|
this.originalData.files = files.slice();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (entry.id) {
|
||||||
|
this.editorExtraParams.id = entry.id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.definitionControl.setValue(this.entry.definition);
|
this.definitionControl.setValue(this.entry.definition);
|
||||||
|
@ -140,20 +147,20 @@ export class AddonModGlossaryEditPage implements OnInit {
|
||||||
*
|
*
|
||||||
* @return Resolved if we can leave it, rejected if not.
|
* @return Resolved if we can leave it, rejected if not.
|
||||||
*/
|
*/
|
||||||
ionViewCanLeave(): boolean | Promise<void> {
|
async ionViewCanLeave(): Promise<void> {
|
||||||
let promise: any;
|
if (this.saved) {
|
||||||
|
return;
|
||||||
if (!this.saved && this.glossaryHelper.hasEntryDataChanged(this.entry, this.attachments, this.originalData)) {
|
|
||||||
// Show confirmation if some data has been modified.
|
|
||||||
promise = this.domUtils.showConfirm(this.translate.instant('core.confirmcanceledit'));
|
|
||||||
} else {
|
|
||||||
promise = Promise.resolve();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return promise.then(() => {
|
if (this.glossaryHelper.hasEntryDataChanged(this.entry, this.attachments, this.originalData)) {
|
||||||
// Delete the local files from the tmp folder.
|
// Show confirmation if some data has been modified.
|
||||||
this.uploaderProvider.clearTmpFiles(this.attachments);
|
await this.domUtils.showConfirm(this.translate.instant('core.confirmcanceledit'));
|
||||||
});
|
}
|
||||||
|
|
||||||
|
// Delete the local files from the tmp folder.
|
||||||
|
this.uploaderProvider.clearTmpFiles(this.attachments);
|
||||||
|
|
||||||
|
this.domUtils.triggerFormCancelledEvent(this.formElement, this.sitesProvider.getCurrentSiteId());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -246,6 +253,8 @@ export class AddonModGlossaryEditPage implements OnInit {
|
||||||
};
|
};
|
||||||
this.eventsProvider.trigger(AddonModGlossaryProvider.ADD_ENTRY_EVENT, data, this.sitesProvider.getCurrentSiteId());
|
this.eventsProvider.trigger(AddonModGlossaryProvider.ADD_ENTRY_EVENT, data, this.sitesProvider.getCurrentSiteId());
|
||||||
|
|
||||||
|
this.domUtils.triggerFormSubmittedEvent(this.formElement, !!entryId, this.sitesProvider.getCurrentSiteId());
|
||||||
|
|
||||||
this.saved = true;
|
this.saved = true;
|
||||||
this.navCtrl.pop();
|
this.navCtrl.pop();
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue