Merge pull request #3861 from crazyserver/MOBILE-3947

Mobile 3947
main
Dani Palou 2023-11-27 15:04:40 +01:00 committed by GitHub
commit af215a7f5c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
461 changed files with 3221 additions and 3491 deletions

View File

@ -96,14 +96,14 @@ const appConfig = {
'error',
{
selector: [
'classProperty',
'objectLiteralProperty',
'typeProperty',
'classMethod',
'objectLiteralMethod',
'typeMethod',
'accessor',
'enumMember'
'classProperty',
'objectLiteralProperty',
'typeProperty',
'classMethod',
'objectLiteralMethod',
'typeMethod',
'accessor',
'enumMember'
],
modifiers: ['requiresQuotes'],
format: null,
@ -332,10 +332,11 @@ module.exports = {
'@angular-eslint/template/alt-text': 'error',
'@angular-eslint/template/elements-content': 'error',
'@angular-eslint/template/label-has-associated-control': 'error',
'@angular-eslint/template/table-scope': 'error',
'@angular-eslint/template/valid-aria': 'error',
'@angular-eslint/template/no-duplicate-attributes': 'error',
'@angular-eslint/template/no-positive-tabindex': 'error',
'@angular-eslint/template/prefer-self-closing-tags': 'error',
'@angular-eslint/template/table-scope': 'error',
'@angular-eslint/template/valid-aria': 'error',
'max-len': ['warn', { code: 140 }],
},
},

View File

@ -12,7 +12,7 @@ module.exports = {
transform: {
'^.+\\.(ts|html)$': 'ts-jest',
},
transformIgnorePatterns: ['node_modules/(?!@ionic-native|@ionic|@moodlehq/ionic-native-push)'],
transformIgnorePatterns: ['node_modules/(?!@awesome-cordova-plugins|@ionic/core|@stencil/core|ionicons|@moodlehq/ionic-native-push)'],
moduleNameMapper: {
...pathsToModuleNameMapper(compilerOptions.paths, { prefix: '<rootDir>/src/' }),
'^!raw-loader!.*': 'jest-raw-loader',

317
package-lock.json generated
View File

@ -18,6 +18,29 @@
"@angular/platform-browser": "^16.2.0",
"@angular/platform-browser-dynamic": "^16.2.0",
"@angular/router": "^16.2.0",
"@awesome-cordova-plugins/badge": "^6.3.0",
"@awesome-cordova-plugins/camera": "^6.3.0",
"@awesome-cordova-plugins/chooser": "^6.3.0",
"@awesome-cordova-plugins/clipboard": "^6.3.0",
"@awesome-cordova-plugins/core": "^6.3.0",
"@awesome-cordova-plugins/device": "^6.3.0",
"@awesome-cordova-plugins/diagnostic": "^6.3.0",
"@awesome-cordova-plugins/file": "^6.3.0",
"@awesome-cordova-plugins/file-opener": "^6.3.0",
"@awesome-cordova-plugins/file-transfer": "^6.3.0",
"@awesome-cordova-plugins/geolocation": "^6.3.0",
"@awesome-cordova-plugins/http": "^6.3.0",
"@awesome-cordova-plugins/in-app-browser": "^6.3.0",
"@awesome-cordova-plugins/ionic-webview": "^6.3.0",
"@awesome-cordova-plugins/keyboard": "^6.3.0",
"@awesome-cordova-plugins/local-notifications": "^6.3.0",
"@awesome-cordova-plugins/media-capture": "^6.3.0",
"@awesome-cordova-plugins/network": "^6.3.0",
"@awesome-cordova-plugins/splash-screen": "^6.3.0",
"@awesome-cordova-plugins/sqlite": "^6.3.0",
"@awesome-cordova-plugins/status-bar": "^6.3.0",
"@awesome-cordova-plugins/web-intent": "^6.3.0",
"@awesome-cordova-plugins/zip": "^6.3.0",
"@ionic/angular": "^7.0.0",
"@ionic/cordova-builders": "^10.0.0",
"@moodlehq/cordova-plugin-advanced-http": "3.3.1-moodle.1",
@ -71,6 +94,7 @@
"nl.kingsquare.cordova.background-audio": "^1.0.1",
"ogv": "^1.8.9",
"rxjs": "~7.8.0",
"swiper": "^11.0.3",
"ts-md5": "^1.2.7",
"tslib": "^2.3.0",
"video.js": "^7.21.1",
@ -758,6 +782,281 @@
"resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.10.1.tgz",
"integrity": "sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg=="
},
"node_modules/@awesome-cordova-plugins/badge": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/@awesome-cordova-plugins/badge/-/badge-6.4.0.tgz",
"integrity": "sha512-4Fy1ykRH6hfSVTbVOX98g+MiJlhc3ozjEePTl7z2FsJ4kkfUT9lVPM6TdBo1PVNn+PePNR2gKCKOIMJK4BJ+jw==",
"dependencies": {
"@types/cordova": "latest"
},
"peerDependencies": {
"@awesome-cordova-plugins/core": "^6.0.1",
"rxjs": "^5.5.0 || ^6.5.0 || ^7.3.0"
}
},
"node_modules/@awesome-cordova-plugins/camera": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/@awesome-cordova-plugins/camera/-/camera-6.4.0.tgz",
"integrity": "sha512-ApkGm5DaRet1XVTZEk2flhFqPaxbI2Ymj3I7FjqfjR+oP5GvVj0MQ17Oo8gVGRIHZIde6ifks0xn/NfdiT9ySg==",
"dependencies": {
"@types/cordova": "latest"
},
"peerDependencies": {
"@awesome-cordova-plugins/core": "^6.0.1",
"rxjs": "^5.5.0 || ^6.5.0 || ^7.3.0"
}
},
"node_modules/@awesome-cordova-plugins/chooser": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/@awesome-cordova-plugins/chooser/-/chooser-6.4.0.tgz",
"integrity": "sha512-/0Tei8A9Ms2sE9VyWKtt0pVC9r+d9hURQxFnVmtZfKX3nM5fdYRRfzh5BBIrJzn4YMV+TIOv6g3EE1YAhvYTpA==",
"dependencies": {
"@types/cordova": "latest"
},
"peerDependencies": {
"@awesome-cordova-plugins/core": "^6.0.1",
"rxjs": "^5.5.0 || ^6.5.0 || ^7.3.0"
}
},
"node_modules/@awesome-cordova-plugins/clipboard": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/@awesome-cordova-plugins/clipboard/-/clipboard-6.4.0.tgz",
"integrity": "sha512-oDPQ/vJe8wQi/GeqHt7bCydeiReRMQ1lcmC9EvH0GoQUa/M3MOb4dWcSAAz23CxdGY6ePeeCS+727+Hc7xpakA==",
"dependencies": {
"@types/cordova": "latest"
},
"peerDependencies": {
"@awesome-cordova-plugins/core": "^6.0.1",
"rxjs": "^5.5.0 || ^6.5.0 || ^7.3.0"
}
},
"node_modules/@awesome-cordova-plugins/core": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/@awesome-cordova-plugins/core/-/core-6.4.0.tgz",
"integrity": "sha512-06I5JdTgAgKTby+VG+3sQF5+z4RNCEyVl6y7tz2rICx8MURL1biuh3oGGN1rCQQjsuMZcX5siMBr0NF/OHqxLQ==",
"dependencies": {
"@types/cordova": "latest"
},
"peerDependencies": {
"rxjs": "^5.5.0 || ^6.5.0 || ^7.3.0"
}
},
"node_modules/@awesome-cordova-plugins/device": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/@awesome-cordova-plugins/device/-/device-6.4.0.tgz",
"integrity": "sha512-uGwlkNH7siiMvgC6PijCDySn59JY0qwxXLvOvhhumX1Lh8VjCEUfVRjkrgJEPEcJGY6lHjw4jChCWY2Tr1g/UQ==",
"dependencies": {
"@types/cordova": "latest"
},
"peerDependencies": {
"@awesome-cordova-plugins/core": "^6.0.1",
"rxjs": "^5.5.0 || ^6.5.0 || ^7.3.0"
}
},
"node_modules/@awesome-cordova-plugins/diagnostic": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/@awesome-cordova-plugins/diagnostic/-/diagnostic-6.4.0.tgz",
"integrity": "sha512-XvYYviet10nRLeNDLcYkO9EdWxxTKWKt4XFHscmhP1Nyg7QFi2vJS8FwZj6u4y/u3JBlCe+/4s3xXC/6jpQTbg==",
"dependencies": {
"@types/cordova": "latest"
},
"peerDependencies": {
"@awesome-cordova-plugins/core": "^6.0.1",
"rxjs": "^5.5.0 || ^6.5.0 || ^7.3.0"
}
},
"node_modules/@awesome-cordova-plugins/file": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/@awesome-cordova-plugins/file/-/file-6.4.0.tgz",
"integrity": "sha512-KOpYpUYA9PU37qiNS1QsTwtms1bX6YsOY2ASxwCqhD+gOqpCfkFHW0kJ9yFmO68gRR2vK3k6PgUimwJXkG6RgA==",
"dependencies": {
"@types/cordova": "latest"
},
"peerDependencies": {
"@awesome-cordova-plugins/core": "^6.0.1",
"rxjs": "^5.5.0 || ^6.5.0 || ^7.3.0"
}
},
"node_modules/@awesome-cordova-plugins/file-opener": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/@awesome-cordova-plugins/file-opener/-/file-opener-6.4.0.tgz",
"integrity": "sha512-1IIFvnz8gPL4mQNqkA1LOUQeWoF8aUAbFGSVLfcvEwJlgXvzT6yC/t8F3Czf0se2TX4tbaoqCiAtxnwBaoIItA==",
"dependencies": {
"@types/cordova": "latest"
},
"peerDependencies": {
"@awesome-cordova-plugins/core": "^6.0.1",
"rxjs": "^5.5.0 || ^6.5.0 || ^7.3.0"
}
},
"node_modules/@awesome-cordova-plugins/file-transfer": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/@awesome-cordova-plugins/file-transfer/-/file-transfer-6.4.0.tgz",
"integrity": "sha512-0FCtWSJQh62tlRu6GZdila6Yz5Z4D538zoIDvbmPNJfH8Urhops5ihscatebYHjNAt2UF8WmxzVjDyGOljcCbQ==",
"dependencies": {
"@types/cordova": "latest"
},
"peerDependencies": {
"@awesome-cordova-plugins/core": "^6.0.1",
"rxjs": "^5.5.0 || ^6.5.0 || ^7.3.0"
}
},
"node_modules/@awesome-cordova-plugins/geolocation": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/@awesome-cordova-plugins/geolocation/-/geolocation-6.4.0.tgz",
"integrity": "sha512-Jyn3pB1sPBqpZrv6/6A1gT7/FIDSYOaGbYNEeF9EHm9hz2maLXIVVdwYY8Hp9vggZ2g2mDpRLRNbXRfSo+R9Gw==",
"dependencies": {
"@types/cordova": "latest"
},
"peerDependencies": {
"@awesome-cordova-plugins/core": "^6.0.1",
"rxjs": "^5.5.0 || ^6.5.0 || ^7.3.0"
}
},
"node_modules/@awesome-cordova-plugins/http": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/@awesome-cordova-plugins/http/-/http-6.4.0.tgz",
"integrity": "sha512-nv8Az1bYnEm/MQSA6Oec0FFAGFu5bmubQXypp08ZUJA4uGkps3IJS02F0451PF/vM0Jcpnb+W6ZRqRCNJ34ZJA==",
"dependencies": {
"@types/cordova": "latest"
},
"peerDependencies": {
"@awesome-cordova-plugins/core": "^6.0.1",
"rxjs": "^5.5.0 || ^6.5.0 || ^7.3.0"
}
},
"node_modules/@awesome-cordova-plugins/in-app-browser": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/@awesome-cordova-plugins/in-app-browser/-/in-app-browser-6.4.0.tgz",
"integrity": "sha512-AaHg/hpXrfKtX+HCzENJDjR9z2wYyNe86o2VeKZlQQP/teuo8SleOIsmN4yTVI1nqnGrQTuD4sKQIhiNeS836g==",
"dependencies": {
"@types/cordova": "latest"
},
"peerDependencies": {
"@awesome-cordova-plugins/core": "^6.0.1",
"rxjs": "^5.5.0 || ^6.5.0 || ^7.3.0"
}
},
"node_modules/@awesome-cordova-plugins/ionic-webview": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/@awesome-cordova-plugins/ionic-webview/-/ionic-webview-6.4.0.tgz",
"integrity": "sha512-w45CjIn/CqZ3D5pXG5t+QyXwdUnBskFmbdN/A6Tv0NVu6uYl3cz0ZWFN+4bK2X/5IPlSC1orQAHaEHrGFomvpw==",
"dependencies": {
"@types/cordova": "latest"
},
"peerDependencies": {
"@awesome-cordova-plugins/core": "^6.0.1",
"rxjs": "^5.5.0 || ^6.5.0 || ^7.3.0"
}
},
"node_modules/@awesome-cordova-plugins/keyboard": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/@awesome-cordova-plugins/keyboard/-/keyboard-6.4.0.tgz",
"integrity": "sha512-9CNRYlnqbi47FyRPzcPGUkW8kQNv0nowxPAmBFkda51GNXvtw+BQ7YLDQSkthyBZr5RlU3GmtGibyYIMeOlLkQ==",
"dependencies": {
"@types/cordova": "latest"
},
"peerDependencies": {
"@awesome-cordova-plugins/core": "^6.0.1",
"rxjs": "^5.5.0 || ^6.5.0 || ^7.3.0"
}
},
"node_modules/@awesome-cordova-plugins/local-notifications": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/@awesome-cordova-plugins/local-notifications/-/local-notifications-6.4.0.tgz",
"integrity": "sha512-q6oeVqLx6SSOkeSCu46PvX6twdY3GevRz5aIlOtroNhqmCmeDmIq2OppDnL3DmnqOY0VVO3spYdQEuo2uYRCwQ==",
"dependencies": {
"@types/cordova": "latest"
},
"peerDependencies": {
"@awesome-cordova-plugins/core": "^6.0.1",
"rxjs": "^5.5.0 || ^6.5.0 || ^7.3.0"
}
},
"node_modules/@awesome-cordova-plugins/media-capture": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/@awesome-cordova-plugins/media-capture/-/media-capture-6.4.0.tgz",
"integrity": "sha512-HP6OsvIDr1V76UgBfNvhoy9dM1IC7Eo0ACisbPoJECZKVSNfTwWoVZonyCvp76U0xaoXuF10GRN6WlQisoWXIw==",
"dependencies": {
"@types/cordova": "latest"
},
"peerDependencies": {
"@awesome-cordova-plugins/core": "^6.0.1",
"rxjs": "^5.5.0 || ^6.5.0 || ^7.3.0"
}
},
"node_modules/@awesome-cordova-plugins/network": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/@awesome-cordova-plugins/network/-/network-6.4.0.tgz",
"integrity": "sha512-opEQo8c/KeGH2bSqJ6F7jOxgC2lMkTZD2PxIsN5jId4KsbOMfvB4cE5ljVcx8TCmeKwHj7pGPlLcR9Eze1BawA==",
"dependencies": {
"@types/cordova": "latest"
},
"peerDependencies": {
"@awesome-cordova-plugins/core": "^6.0.1",
"rxjs": "^5.5.0 || ^6.5.0 || ^7.3.0"
}
},
"node_modules/@awesome-cordova-plugins/splash-screen": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/@awesome-cordova-plugins/splash-screen/-/splash-screen-6.4.0.tgz",
"integrity": "sha512-0JlJysmxOPiU+wCMfo4ocHTPBWrhyTsh34Kig6t+lgO5fLUUtrLXW3LCOxx0TaXJt5XjYdfXXQciLYGXcQ4gww==",
"dependencies": {
"@types/cordova": "latest"
},
"peerDependencies": {
"@awesome-cordova-plugins/core": "^6.0.1",
"rxjs": "^5.5.0 || ^6.5.0 || ^7.3.0"
}
},
"node_modules/@awesome-cordova-plugins/sqlite": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/@awesome-cordova-plugins/sqlite/-/sqlite-6.4.0.tgz",
"integrity": "sha512-2CXSZxp0pQUabSv4kLzCMpicNjuKmwraJ3xCP3+6QVvZr0kZ10Xh8cgIU5nPL/x9G2zzG9fVubLewh3hEgR8Zg==",
"dependencies": {
"@types/cordova": "latest"
},
"peerDependencies": {
"@awesome-cordova-plugins/core": "^6.0.1",
"rxjs": "^5.5.0 || ^6.5.0 || ^7.3.0"
}
},
"node_modules/@awesome-cordova-plugins/status-bar": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/@awesome-cordova-plugins/status-bar/-/status-bar-6.4.0.tgz",
"integrity": "sha512-BQ0Nip16YFQ7PvQvu5+SJASDl8Uomxm1wn9tZpk+8NcQGLKTf8fL94Z2MA5HjFsHaAlzw+fFo7UJ9+9qfd1hPA==",
"dependencies": {
"@types/cordova": "latest"
},
"peerDependencies": {
"@awesome-cordova-plugins/core": "^6.0.1",
"rxjs": "^5.5.0 || ^6.5.0 || ^7.3.0"
}
},
"node_modules/@awesome-cordova-plugins/web-intent": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/@awesome-cordova-plugins/web-intent/-/web-intent-6.4.0.tgz",
"integrity": "sha512-JJJpQ/viDCCkdhvq0A3ZnY5mmnNPd1IHGlB8tDR0jfunWzh2wK5EbETt0RVAF7zYxRnHKltx6Ep3FAd7JIY0Kw==",
"dependencies": {
"@types/cordova": "latest"
},
"peerDependencies": {
"@awesome-cordova-plugins/core": "^6.0.1",
"rxjs": "^5.5.0 || ^6.5.0 || ^7.3.0"
}
},
"node_modules/@awesome-cordova-plugins/zip": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/@awesome-cordova-plugins/zip/-/zip-6.4.0.tgz",
"integrity": "sha512-s6Bg+sBepwhVvN+8fdns8QOOY5Mo5pg9Iy1aJiFDBUipuQOLaO++zw5u+no0igObnYcaQAdSO2XyEBX/h0T59g==",
"dependencies": {
"@types/cordova": "latest"
},
"peerDependencies": {
"@awesome-cordova-plugins/core": "^6.0.1",
"rxjs": "^5.5.0 || ^6.5.0 || ^7.3.0"
}
},
"node_modules/@babel/code-frame": {
"version": "7.23.4",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.4.tgz",
@ -27268,6 +27567,24 @@
"es6-symbol": "^3.1.1"
}
},
"node_modules/swiper": {
"version": "11.0.5",
"resolved": "https://registry.npmjs.org/swiper/-/swiper-11.0.5.tgz",
"integrity": "sha512-rhCwupqSyRnWrtNzWzemnBLMoyYuoDgGgspAm/8iBD3jCvAWycPLH4Z3TB0O5520DHLzMx94yUMH/B9Efpa48w==",
"funding": [
{
"type": "patreon",
"url": "https://www.patreon.com/swiperjs"
},
{
"type": "open_collective",
"url": "http://opencollective.com/swiper"
}
],
"engines": {
"node": ">= 4.7.0"
}
},
"node_modules/symbol-observable": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz",

View File

@ -53,6 +53,29 @@
"@angular/platform-browser": "^16.2.0",
"@angular/platform-browser-dynamic": "^16.2.0",
"@angular/router": "^16.2.0",
"@awesome-cordova-plugins/badge": "^6.3.0",
"@awesome-cordova-plugins/camera": "^6.3.0",
"@awesome-cordova-plugins/chooser": "^6.3.0",
"@awesome-cordova-plugins/clipboard": "^6.3.0",
"@awesome-cordova-plugins/core": "^6.3.0",
"@awesome-cordova-plugins/device": "^6.3.0",
"@awesome-cordova-plugins/diagnostic": "^6.3.0",
"@awesome-cordova-plugins/file": "^6.3.0",
"@awesome-cordova-plugins/file-opener": "^6.3.0",
"@awesome-cordova-plugins/file-transfer": "^6.3.0",
"@awesome-cordova-plugins/geolocation": "^6.3.0",
"@awesome-cordova-plugins/http": "^6.3.0",
"@awesome-cordova-plugins/in-app-browser": "^6.3.0",
"@awesome-cordova-plugins/ionic-webview": "^6.3.0",
"@awesome-cordova-plugins/keyboard": "^6.3.0",
"@awesome-cordova-plugins/local-notifications": "^6.3.0",
"@awesome-cordova-plugins/media-capture": "^6.3.0",
"@awesome-cordova-plugins/network": "^6.3.0",
"@awesome-cordova-plugins/splash-screen": "^6.3.0",
"@awesome-cordova-plugins/sqlite": "^6.3.0",
"@awesome-cordova-plugins/status-bar": "^6.3.0",
"@awesome-cordova-plugins/web-intent": "^6.3.0",
"@awesome-cordova-plugins/zip": "^6.3.0",
"@ionic/angular": "^7.0.0",
"@ionic/cordova-builders": "^10.0.0",
"@moodlehq/cordova-plugin-advanced-http": "3.3.1-moodle.1",
@ -106,6 +129,7 @@
"nl.kingsquare.cordova.background-audio": "^1.0.1",
"ogv": "^1.8.9",
"rxjs": "~7.8.0",
"swiper": "^11.0.3",
"ts-md5": "^1.2.7",
"tslib": "^2.3.0",
"video.js": "^7.21.1",

View File

@ -1,7 +1,7 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
<ion-back-button [text]="'core.back' | translate" />
</ion-buttons>
<ion-title>
<h1 *ngIf="badge">{{ badge.name }}</h1>
@ -11,7 +11,7 @@
</ion-header>
<ion-content [core-swipe-navigation]="badges" class="limited-width">
<ion-refresher slot="fixed" [disabled]="!badgeLoaded" (ionRefresh)="refreshBadges($event.target)">
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}" />
</ion-refresher>
<core-loading [hideUntil]="badgeLoaded">
<ion-item-group *ngIf="badge">
@ -122,8 +122,7 @@
<ion-label>
<p class="item-heading">{{ 'core.course' | translate}}</p>
<p>
<core-format-text [text]="course.fullname" contextLevel="course" [contextInstanceId]="courseId">
</core-format-text>
<core-format-text [text]="course.fullname" contextLevel="course" [contextInstanceId]="courseId" />
</p>
</ion-label>
</ion-item>

View File

@ -1,7 +1,7 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
<ion-back-button [text]="'core.back' | translate" />
</ion-buttons>
<ion-title>
<h1>{{ 'addon.badges.badges' | translate }}</h1>
@ -11,11 +11,10 @@
<ion-content>
<core-split-view>
<ion-refresher slot="fixed" [disabled]="!badges.loaded" (ionRefresh)="refreshBadges($event.target)">
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}" />
</ion-refresher>
<core-loading [hideUntil]="badges.loaded">
<core-empty-box *ngIf="badges.empty" icon="fas-trophy" [message]="'addon.badges.nobadges' | translate">
</core-empty-box>
<core-empty-box *ngIf="badges.empty" icon="fas-trophy" [message]="'addon.badges.nobadges' | translate" />
<ion-list *ngIf="!badges.empty" class="ion-no-margin">
<ion-item button class="ion-text-wrap" *ngFor="let badge of badges.items" [attr.aria-label]="badge.name"

View File

@ -5,8 +5,7 @@
</ion-item-divider>
<core-loading [hideUntil]="loaded">
<ion-item class="ion-text-wrap" *ngFor="let entry of entries" [detail]="true" button (click)="gotoCoureListModType(entry)">
<core-mod-icon slot="start" [modicon]="entry.icon" [modname]="entry.iconModName" [showAlt]="false">
</core-mod-icon>
<core-mod-icon slot="start" [modicon]="entry.icon" [modname]="entry.iconModName" [showAlt]="false" />
<ion-label>{{ entry.name }}</ion-label>
</ion-item>
</core-loading>

View File

@ -7,16 +7,14 @@
<div *ngIf="downloadCoursesEnabled && filteredCourses.length > 0" class="core-button-spinner">
<ion-button *ngIf="!prefetchCoursesData.loading" fill="clear" (click)="prefetchCourses()"
[attr.aria-label]="prefetchCoursesData.statusTranslatable | translate">
<ion-icon [name]="prefetchCoursesData.icon" slot="icon-only" aria-hidden="true">
</ion-icon>
<ion-icon [name]="prefetchCoursesData.icon" slot="icon-only" aria-hidden="true" />
</ion-button>
<ion-badge class="core-course-download-courses-progress" *ngIf="prefetchCoursesData.badge" role="progressbar"
[attr.aria-valuemax]="prefetchCoursesData.total" [attr.aria-valuenow]="prefetchCoursesData.count"
[attr.aria-valuetext]="prefetchCoursesData.badgeA11yText">
{{prefetchCoursesData.badge}}
</ion-badge>
<ion-spinner *ngIf="prefetchCoursesData.loading" [attr.aria-label]="'core.loading' | translate">
</ion-spinner>
<ion-spinner *ngIf="prefetchCoursesData.loading" [attr.aria-label]="'core.loading' | translate" />
</div>
</div>
</ion-item-divider>
@ -26,8 +24,7 @@
<ion-col>
<!-- Filter courses. -->
<ion-searchbar [(ngModel)]="textFilter" (ionInput)="filterTextChanged($event.target)"
(ionCancel)="filterTextChanged($event.target)" [placeholder]="'core.courses.filtermycourses' | translate">
</ion-searchbar>
(ionCancel)="filterTextChanged($event.target)" [placeholder]="'core.courses.filtermycourses' | translate" />
</ion-col>
</ion-row>
<ion-row class="ion-justify-content-between ion-align-items-center addon-block-myoverview-filter" *ngIf="hasCourses">
@ -70,8 +67,7 @@
<ion-col>
<!-- Filter courses. -->
<ion-searchbar class="ion-hide-md-down" [(ngModel)]="textFilter" (ionInput)="filterTextChanged($event.target)"
(ionCancel)="filterTextChanged($event.target)" [placeholder]="'core.courses.filtermycourses' | translate">
</ion-searchbar>
(ionCancel)="filterTextChanged($event.target)" [placeholder]="'core.courses.filtermycourses' | translate" />
</ion-col>
<ion-col size="auto" *ngIf="sort.enabled">
<core-combobox [label]="'core.sortby' | translate" [selection]="sort.selected" (onChange)="sortCourses($event)"
@ -90,11 +86,11 @@
<ion-col size="auto" *ngIf="isLayoutSwitcherAvailable">
<ion-button *ngIf="layout === 'card'" fill="outline" (click)="toggleLayout('list')"
[attr.aria-label]="'addon.block_myoverview.aria:list' | translate">
<ion-icon slot="icon-only" name="fas-list" aria-hidden="true"></ion-icon>
<ion-icon slot="icon-only" name="fas-list" aria-hidden="true" />
</ion-button>
<ion-button *ngIf="layout === 'list'" fill="outline" (click)="toggleLayout('card')"
[attr.aria-label]="'addon.block_myoverview.aria:card' | translate">
<ion-icon slot="icon-only" name="fas-table-cells-large" aria-hidden="true"></ion-icon>
<ion-icon slot="icon-only" name="fas-table-cells-large" aria-hidden="true" />
</ion-button>
</ion-col>
</ion-row>
@ -114,8 +110,7 @@
{{'addon.block_myoverview.nocoursesenrolleddescription' | translate}}
</p>
<ion-button (click)="openSearch()" fill="outline">
<ion-icon name="fas-magnifying-glass" slot="start" aria-hidden="true">
</ion-icon>
<ion-icon name="fas-magnifying-glass" slot="start" aria-hidden="true" />
{{'addon.block_myoverview.browseallcourses' | translate}}
</ion-button>
</ng-container>
@ -128,8 +123,7 @@
<ion-col *ngFor="let course of filteredCourses" class="ion-no-padding" size="12" size-sm="6" size-md="6" size-lg="4"
size-xl="3">
<core-courses-course-list-item [course]="course" class="core-courseoverview" [showDownload]="downloadCourseEnabled"
[layout]="layout">
</core-courses-course-list-item>
[layout]="layout" />
</ion-col>
</ion-row>
</ion-grid>

View File

@ -3,21 +3,19 @@
<h2>{{ 'addon.block_recentlyaccessedcourses.pluginname' | translate }}</h2>
</ion-label>
<div slot="end" class="flex-row">
<core-horizontal-scroll-controls #scrollControls [aria-controls]="scrollElementId">
</core-horizontal-scroll-controls>
<core-horizontal-scroll-controls #scrollControls [aria-controls]="scrollElementId" />
</div>
</ion-item-divider>
<core-loading [hideUntil]="loaded">
<core-empty-box *ngIf="courses.length === 0" image="assets/img/icons/courses.svg"
[message]="'addon.block_recentlyaccessedcourses.nocourses' | translate"></core-empty-box>
[message]="'addon.block_recentlyaccessedcourses.nocourses' | translate" />
<!-- List of courses. -->
<div [hidden]="courses.length === 0" [id]="scrollElementId" class="core-horizontal-scroll"
(scroll)="scrollControls.updateScrollPosition()">
<div (onResize)="scrollControls.updateScrollPosition()" class="flex-row">
<div class="safe-area-pseudo-padding-start"></div>
<ng-container *ngFor="let course of courses">
<core-courses-course-list-item [course]="course" class="core-recentlyaccessedcourses" layout="summarycard">
</core-courses-course-list-item>
<core-courses-course-list-item [course]="course" class="core-recentlyaccessedcourses" layout="summarycard" />
</ng-container>
<div class="safe-area-pseudo-padding-end"></div>
</div>

View File

@ -3,8 +3,7 @@
<h2>{{ 'addon.block_recentlyaccesseditems.pluginname' | translate }}</h2>
</ion-label>
<div slot="end">
<core-horizontal-scroll-controls #scrollControls [aria-controls]="scrollElementId">
</core-horizontal-scroll-controls>
<core-horizontal-scroll-controls #scrollControls [aria-controls]="scrollElementId" />
</div>
</ion-item-divider>
<core-loading [hideUntil]="loaded">
@ -16,18 +15,16 @@
<ion-card>
<ion-item class="core-course-module-handler ion-text-wrap" [detail]="false" (click)="action($event, item)" button>
<core-mod-icon slot="start" *ngIf="item.iconUrl" [modicon]="item.iconUrl" [modname]="item.modname"
[componentId]="item.cmid" [showAlt]="false" [purpose]="item.purpose">
</core-mod-icon>
[componentId]="item.cmid" [showAlt]="false" [purpose]="item.purpose" />
<ion-label>
<!-- Add the icon title so accessibility tools read it. -->
<span class="sr-only" *ngIf="item.iconTitle">{{ item.iconTitle }}</span>
<p class="item-heading">
<core-format-text [text]="item.name" contextLevel="module" [contextInstanceId]="item.cmid"
[courseId]="item.courseid"></core-format-text>
[courseId]="item.courseid" />
</p>
<p>
<core-format-text [text]="item.coursename" contextLevel="course" [contextInstanceId]="item.courseid">
</core-format-text>
<core-format-text [text]="item.coursename" contextLevel="course" [contextInstanceId]="item.courseid" />
</p>
</ion-label>
</ion-item>
@ -38,6 +35,6 @@
</div>
<core-empty-box *ngIf="items.length <= 0" image="assets/img/icons/activities.svg"
[message]="'addon.block_recentlyaccesseditems.noitems' | translate"></core-empty-box>
[message]="'addon.block_recentlyaccesseditems.noitems' | translate" />
</core-loading>

View File

@ -8,10 +8,10 @@
<ion-item class="ion-text-wrap" *ngIf="mainMenuBlock.summary">
<ion-label>
<core-format-text [text]="mainMenuBlock.summary" [component]="component" [componentId]="siteHomeId" contextLevel="course"
[contextInstanceId]="siteHomeId"></core-format-text>
[contextInstanceId]="siteHomeId" />
</ion-label>
</ion-item>
<core-course-module *ngFor="let module of mainMenuBlock.modules" [module]="module" [section]="mainMenuBlock"></core-course-module>
<core-course-module *ngFor="let module of mainMenuBlock.modules" [module]="module" [section]="mainMenuBlock" />
</ion-list>
</core-loading>

View File

@ -3,21 +3,19 @@
<h2>{{ 'addon.block_starredcourses.pluginname' | translate }}</h2>
</ion-label>
<div slot="end" class="flex-row">
<core-horizontal-scroll-controls #scrollControls [aria-controls]="scrollElementId">
</core-horizontal-scroll-controls>
<core-horizontal-scroll-controls #scrollControls [aria-controls]="scrollElementId" />
</div>
</ion-item-divider>
<core-loading [hideUntil]="loaded">
<core-empty-box *ngIf="courses.length === 0" image="assets/img/icons/courses.svg"
[message]="'addon.block_starredcourses.nocourses' | translate"></core-empty-box>
[message]="'addon.block_starredcourses.nocourses' | translate" />
<!-- List of courses. -->
<div [hidden]="courses.length === 0" [id]="scrollElementId" class="core-horizontal-scroll"
(scroll)="scrollControls.updateScrollPosition()">
<div (onResize)="scrollControls.updateScrollPosition()" class="flex-row">
<div class="safe-area-pseudo-padding-start"></div>
<ng-container *ngFor="let course of courses">
<core-courses-course-list-item [course]="course" class="core-block_starredcourses" layout="summarycard">
</core-courses-course-list-item>
<core-courses-course-list-item [course]="course" class="core-block_starredcourses" layout="summarycard" />
</ng-container>
<div class="safe-area-pseudo-padding-end"></div>
</div>

View File

@ -2,8 +2,7 @@
<ion-label class="ion-text-wrap">
<h3>
<span class="sr-only">{{ 'core.courses.aria:coursename' | translate }}</span>
<core-format-text [text]="course.displayname || course.fullname" contextLevel="course" [contextInstanceId]="course.id">
</core-format-text>
<core-format-text [text]="course.displayname || course.fullname" contextLevel="course" [contextInstanceId]="course.id" />
</h3>
</ion-label>
</ion-item>
@ -23,15 +22,13 @@
<ion-col class="addon-block-timeline-activity-time ion-no-padding">
<small>{{event.timesort * 1000 | coreFormatDate:"strftimetime24" }}</small>
<core-mod-icon *ngIf="event.iconUrl" [modicon]="event.iconUrl" [componentId]="event.instance"
[modname]="event.modulename" [purpose]="event.purpose">
</core-mod-icon>
[modname]="event.modulename" [purpose]="event.purpose" />
</ion-col>
<ion-col class="addon-block-timeline-activity-name ion-no-padding">
<p class="item-heading">
<span>
<core-format-text [text]="event.activityname || event.name" contextLevel="module"
[contextInstanceId]="event.id" [courseId]="event.course?.id">
</core-format-text>
[contextInstanceId]="event.id" [courseId]="event.course?.id" />
</span>
<ion-badge *ngIf="event.overdue" color="danger">{{ 'addon.block_timeline.overdue' | translate }}
</ion-badge>
@ -39,15 +36,13 @@
<p *ngIf="showInlineCourse && event.course">
<span>
<core-format-text [text]="event.course.fullnamedisplay" contextLevel="course"
[contextInstanceId]="event.course.id">
</core-format-text>
[contextInstanceId]="event.course.id" />
</span>
</p>
<p *ngIf="event.activitystr">
<span>
<core-format-text *ngIf="event.activitystr" [text]="event.activitystr" contextLevel="module"
[contextInstanceId]="event.id">
</core-format-text>
[contextInstanceId]="event.id" />
</span>
</p>
</ion-col>
@ -72,5 +67,5 @@
<ion-button expand="block" (click)="loadMore.emit()" fill="outline" *ngIf="!loadingMore">
{{ 'core.loadmore' | translate }}
</ion-button>
<ion-spinner *ngIf="loadingMore" [attr.aria-label]="'core.loading' | translate"></ion-spinner>
<ion-spinner *ngIf="loadingMore" [attr.aria-label]="'core.loading' | translate" />
</div>

View File

@ -9,7 +9,7 @@
<!-- Filter courses. -->
<core-search-box (onSubmit)="searchChanged($event)" (onClear)="searchChanged('')"
[placeholder]="'addon.block_timeline.searchevents' | translate" autocorrect="off" spellcheck="false" [lengthCheck]="2"
searchArea="AddonBlockTimeline"></core-search-box>
searchArea="AddonBlockTimeline" />
</ion-col>
</ion-row>
<ion-row class="ion-justify-content-between ion-align-items-center addon-block-timeline-filter">
@ -31,7 +31,7 @@
<!-- Filter courses. -->
<core-search-box (onSubmit)="searchChanged($event)" (onClear)="searchChanged('')"
[placeholder]="'addon.block_timeline.searchevents' | translate" autocorrect="off" spellcheck="false" [lengthCheck]="2"
searchArea="AddonBlockTimeline"></core-search-box>
searchArea="AddonBlockTimeline" />
</ion-col>
<ion-col size="auto">
<core-combobox [label]="'core.sortby' | translate" [formControl]="sort" (onChange)="sortChanged($event)"
@ -46,9 +46,9 @@
<ng-container *ngFor="let section of sections">
<addon-block-timeline-events *ngIf="section.data$ | async as data" [events]="data.events"
[showInlineCourse]="(sort$ | async) === AddonBlockTimelineSort.ByDates" [canLoadMore]="data.canLoadMore"
[loadingMore]="data.loadingMore" (loadMore)="section.loadMore()" [course]="section.course"> </addon-block-timeline-events>
[loadingMore]="data.loadingMore" (loadMore)="section.loadMore()" [course]="section.course" />
</ng-container>
<core-empty-box *ngIf="sections && sections.length === 0" image="assets/img/icons/courses.svg"
[message]="'addon.block_timeline.noevents' | translate"></core-empty-box>
[message]="'addon.block_timeline.noevents' | translate" />
</ng-container>
</core-loading>

View File

@ -1,37 +1,35 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
<ion-back-button [text]="'core.back' | translate" />
</ion-buttons>
<ion-title>
<h1>{{ title | translate }}</h1>
</ion-title>
<ion-buttons slot="end">
<core-user-menu-button></core-user-menu-button>
<core-user-menu-button />
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content class="limited-width">
<ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="refresh($event.target)">
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}" />
</ion-refresher>
<core-loading [hideUntil]="loaded">
<ion-item *ngIf="showMyEntriesToggle">
<ion-label>{{ 'addon.blog.showonlyyourentries' | translate }}</ion-label>
<ion-toggle [(ngModel)]="onlyMyEntries" (ionChange)="onlyMyEntriesToggleChanged(onlyMyEntries)" slot="end"></ion-toggle>
<ion-toggle [(ngModel)]="onlyMyEntries" (ionChange)="onlyMyEntriesToggleChanged(onlyMyEntries)" slot="end" />
</ion-item>
<core-empty-box *ngIf="entries && entries.length === 0" icon="far-newspaper" [message]="'addon.blog.noentriesyet' | translate">
</core-empty-box>
<core-empty-box *ngIf="entries && entries.length === 0" icon="far-newspaper" [message]="'addon.blog.noentriesyet' | translate" />
<ng-container *ngFor="let entry of entries">
<ion-card *ngIf="!onlyMyEntries || entry.userid === currentUserId">
<ion-item class="ion-text-wrap">
<core-user-avatar [user]="entry.user" slot="start" [courseId]="entry.courseid"></core-user-avatar>
<core-user-avatar [user]="entry.user" slot="start" [courseId]="entry.courseid" />
<ion-label>
<div class="flex-row ion-justify-content-between ion-align-items-center">
<h2>
<core-format-text [text]="entry.subject" [contextLevel]="contextLevel"
[contextInstanceId]="contextInstanceId">
</core-format-text>
[contextInstanceId]="contextInstanceId" />
</h2>
<ion-note class="ion-text-end">
{{ 'addon.blog.' + entry.publishTranslated! | translate}}
@ -49,35 +47,32 @@
<ion-item class="ion-text-wrap">
<ion-label>
<core-format-text [text]="entry.summary" [component]="this.component" [componentId]="entry.id"
[contextLevel]="contextLevel" [contextInstanceId]="contextInstanceId">
</core-format-text>
[contextLevel]="contextLevel" [contextInstanceId]="contextInstanceId" />
</ion-label>
</ion-item>
<ion-item class="ion-text-wrap" *ngIf="tagsEnabled && entry.tags && entry.tags!.length > 0">
<ion-label>
<div slot="start">{{ 'core.tag.tags' | translate }}:</div>
<core-tag-list [tags]="entry.tags"></core-tag-list>
<core-tag-list [tags]="entry.tags" />
</ion-label>
</ion-item>
<core-comments *ngIf="commentsEnabled" [component]="this.component" [itemId]="entry.id" area="format_blog"
[instanceId]="entry.userid" contextLevel="user" [showItem]="true">
</core-comments>
[instanceId]="entry.userid" contextLevel="user" [showItem]="true" />
<core-file *ngFor="let file of entry.attachmentfiles" [file]="file" [component]="this.component"
[componentId]="entry.id">
</core-file>
[componentId]="entry.id" />
<ion-item *ngIf="entry.uniquehash" [href]="entry.uniquehash" core-link [detail]="true">
<ion-label>{{ 'addon.blog.linktooriginalentry' | translate }}</ion-label>
</ion-item>
</ion-card-content>
<div class="ion-text-center ion-margin-bottom" *ngIf="entry.lastmodified > entry.created">
<ion-note>
<ion-icon name="fas-clock" [attr.aria-label]="'core.lastmodified' | translate"></ion-icon> {{entry.lastmodified
<ion-icon name="fas-clock" [attr.aria-label]="'core.lastmodified' | translate" /> {{entry.lastmodified
|
coreTimeAgo}}
</ion-note>
</div>
</ion-card>
</ng-container>
<core-infinite-loading [enabled]="canLoadMore" (action)="loadMore($event)" [error]="loadMoreError"></core-infinite-loading>
<core-infinite-loading [enabled]="canLoadMore" (action)="loadMore($event)" [error]="loadMoreError" />
</core-loading>
</ion-content>

View File

@ -2,8 +2,7 @@
<core-navbar-buttons slot="end" prepend>
<core-context-menu>
<core-context-menu-item *ngIf="canNavigate && !selectedMonthIsCurrent() && displayNavButtons" [priority]="900"
[content]="'addon.calendar.currentmonth' | translate" iconAction="fas-calendar-day" (action)="goToCurrentMonth()">
</core-context-menu-item>
[content]="'addon.calendar.currentmonth' | translate" iconAction="fas-calendar-day" (action)="goToCurrentMonth()" />
</core-context-menu>
</core-navbar-buttons>
@ -14,19 +13,18 @@
<ion-row class="ion-align-items-center">
<ion-col class="ion-text-start" *ngIf="canNavigate">
<ion-button fill="clear" (click)="loadPrevious()" [attr.aria-label]="'core.previous' | translate">
<ion-icon name="fas-chevron-left" slot="icon-only" aria-hidden="true"></ion-icon>
<ion-icon name="fas-chevron-left" slot="icon-only" aria-hidden="true" />
</ion-button>
</ion-col>
<ion-col class="ion-text-center addon-calendar-period">
<h2 id="addon-calendar-monthname">
{{ periodName }}
<ion-spinner *ngIf="!selectedMonthLoaded()" class="addon-calendar-loading-month">
</ion-spinner>
<ion-spinner *ngIf="!selectedMonthLoaded()" class="addon-calendar-loading-month" />
</h2>
</ion-col>
<ion-col class="ion-text-end" *ngIf="canNavigate">
<ion-button fill="clear" (click)="loadNext()" [attr.aria-label]="'core.next' | translate">
<ion-icon name="fas-chevron-right" slot="icon-only" aria-hidden="true"></ion-icon>
<ion-icon name="fas-chevron-right" slot="icon-only" aria-hidden="true" />
</ion-button>
</ion-col>
</ion-row>
@ -50,8 +48,7 @@
<!-- Weeks. -->
<ion-row *ngFor="let week of month.weeks" class="addon-calendar-week" role="row">
<!-- Empty slots (first week). -->
<ion-col *ngFor="let value of week.prepadding" class="dayblank addon-calendar-day" role="cell">
</ion-col>
<ion-col *ngFor="let value of week.prepadding" class="dayblank addon-calendar-day" role="cell" />
<ion-col *ngFor="let day of week.days" class="addon-calendar-day ion-text-center" [ngClass]='{
"hasevents": day.hasevents,
"today": month.isCurrentMonth && day.istoday,
@ -76,9 +73,9 @@
[tabindex]="activeView ? 0 : -1">
<span class="calendar_event_type calendar_event_{{event.formattedType}}"></span>
<ion-icon *ngIf="event.offline && !event.deleted" name="fas-clock"
[attr.aria-label]="'core.notsent' | translate"></ion-icon>
[attr.aria-label]="'core.notsent' | translate" />
<ion-icon *ngIf="event.deleted" name="fas-trash"
[attr.aria-label]="'core.deletedoffline' | translate"></ion-icon>
[attr.aria-label]="'core.deletedoffline' | translate" />
<span class="addon-calendar-event-time">
{{ event.timestart * 1000 | coreFormatDate: timeFormat }}
</span>
@ -98,8 +95,7 @@
</div>
</ion-col>
<!-- Empty slots (last week). -->
<ion-col *ngFor="let value of week.postpadding" class="dayblank addon-calendar-day" role="cell">
</ion-col>
<ion-col *ngFor="let value of week.postpadding" class="dayblank addon-calendar-day" role="cell" />
</ion-row>
</div>
</ion-grid>

View File

@ -142,7 +142,7 @@
}
}
ion-slide {
swiper-slide {
display: block;
font-size: inherit;
justify-content: start;

View File

@ -64,7 +64,7 @@ import { Translate } from '@singletons';
})
export class AddonCalendarCalendarComponent implements OnInit, DoCheck, OnDestroy {
@ViewChild(CoreSwipeSlidesComponent) slides?: CoreSwipeSlidesComponent<PreloadedMonth>;
@ViewChild(CoreSwipeSlidesComponent) swipeSlidesComponent?: CoreSwipeSlidesComponent<PreloadedMonth>;
@Input() initialYear?: number; // Initial year to load.
@Input() initialMonth?: number; // Initial month to load.
@ -185,7 +185,7 @@ export class AddonCalendarCalendarComponent implements OnInit, DoCheck, OnDestro
this.hiddenDiffer = this.hidden;
if (!this.hidden) {
this.slides?.slides?.getSwiper().then(swipper => swipper.update());
this.swipeSlidesComponent?.updateSlidesComponent();
}
}
}
@ -248,14 +248,14 @@ export class AddonCalendarCalendarComponent implements OnInit, DoCheck, OnDestro
* Load next month.
*/
loadNext(): void {
this.slides?.slideNext();
this.swipeSlidesComponent?.slideNext();
}
/**
* Load previous month.
*/
loadPrevious(): void {
this.slides?.slidePrev();
this.swipeSlidesComponent?.slidePrev();
}
/**
@ -343,8 +343,7 @@ export class AddonCalendarCalendarComponent implements OnInit, DoCheck, OnDestro
*/
async viewMonth(month: number, year: number): Promise<void> {
const manager = this.manager;
const slides = this.slides;
if (!manager || !slides) {
if (!manager || !this.swipeSlidesComponent) {
return;
}
@ -360,7 +359,7 @@ export class AddonCalendarCalendarComponent implements OnInit, DoCheck, OnDestro
// Make sure the day is loaded.
await manager.getSource().loadItem(item);
slides.slideToItem(item);
this.swipeSlidesComponent.slideToItem(item);
} catch (error) {
CoreDomUtils.showErrorModalDefault(error, 'addon.calendar.errorloadevents', true);
} finally {

View File

@ -2,7 +2,7 @@
<ion-toolbar>
<ion-buttons slot="end">
<ion-button fill="clear" (click)="closeModal()" [attr.aria-label]="'core.close' | translate">
<ion-icon name="fas-xmark" slot="icon-only" aria-hidden=true></ion-icon>
<ion-icon name="fas-xmark" slot="icon-only" aria-hidden=true />
</ion-button>
</ion-buttons>
</ion-toolbar>
@ -10,18 +10,18 @@
<ion-content [fullscreen]="true">
<ion-list>
<ion-item *ngFor="let type of types" class="addon-calendar-event" [ngClass]="['addon-calendar-eventtype-'+type]">
<ion-icon [name]="typeIcons[type]" slot="start" aria-hidden="true"></ion-icon>
<ion-icon [name]="typeIcons[type]" slot="start" aria-hidden="true" />
<ion-label>{{ 'addon.calendar.' + type + 'events' | translate}}</ion-label>
<ion-toggle [(ngModel)]="filter[type]" (ionChange)="onChange()" slot="end"></ion-toggle>
<ion-toggle [(ngModel)]="filter[type]" (ionChange)="onChange()" slot="end" />
</ion-item>
<core-spacer *ngIf="filter.course || filter.category || filter.group"></core-spacer>
<core-spacer *ngIf="filter.course || filter.category || filter.group" />
<ng-container *ngIf="filter.course || filter.category || filter.group">
<ion-radio-group [(ngModel)]="courseId" (ionChange)="onChange()">
<ion-item class="ion-text-wrap" *ngFor="let course of sortedCourses">
<ion-label>
<core-format-text [text]="course.shortname"></core-format-text>
<core-format-text [text]="course.shortname" />
</ion-label>
<ion-radio slot="end" [value]="course.id"></ion-radio>
<ion-radio slot="end" [value]="course.id" />
</ion-item>
</ion-radio-group>
</ng-container>

View File

@ -1,6 +1,6 @@
<core-loading [hideUntil]="loaded">
<core-empty-box *ngIf="!filteredEvents || !filteredEvents.length" icon="fas-calendar" [message]="'addon.calendar.noevents' | translate">
</core-empty-box>
<core-empty-box *ngIf="!filteredEvents || !filteredEvents.length" icon="fas-calendar"
[message]="'addon.calendar.noevents' | translate" />
<ion-list *ngIf="filteredEvents && filteredEvents.length" class="list-item-limited-width">
<ng-container *ngFor="let event of filteredEvents">
@ -8,9 +8,8 @@
<ion-item class="ion-text-wrap addon-calendar-event" [attr.aria-label]="event.name" (click)="eventClicked(event)" button
[ngClass]="['addon-calendar-eventtype-'+event.eventtype]" [detail]="false">
<core-mod-icon *ngIf="event.moduleIcon" [modicon]="event.moduleIcon" slot="start" [modname]="event.modulename"
[componentId]="event.instance" [showAlt]="false" [purpose]="event.purpose"></core-mod-icon>
<ion-icon *ngIf="event.eventIcon && !event.moduleIcon" [name]="event.eventIcon" slot="start" aria-hidden="true">
</ion-icon>
[componentId]="event.instance" [showAlt]="false" [purpose]="event.purpose" />
<ion-icon *ngIf="event.eventIcon && !event.moduleIcon" [name]="event.eventIcon" slot="start" aria-hidden="true" />
<ion-label>
<!-- Add the icon title so accessibility tools read it. -->
<span class="sr-only">
@ -19,18 +18,18 @@
</span>
<p class="item-heading">
<core-format-text [text]="event.name" [contextLevel]="event.contextLevel"
[contextInstanceId]="event.contextInstanceId"></core-format-text>
[contextInstanceId]="event.contextInstanceId" />
</p>
<p>
<core-format-text [text]="event.formattedtime" [filter]="false"></core-format-text>
<core-format-text [text]="event.formattedtime" [filter]="false" />
</p>
</ion-label>
<ion-note *ngIf="event.offline && !event.deleted" slot="end">
<ion-icon name="fas-clock" aria-hidden="true"></ion-icon>
<ion-icon name="fas-clock" aria-hidden="true" />
<span class="ion-text-wrap">{{ 'core.notsent' | translate }}</span>
</ion-note>
<ion-note *ngIf="event.deleted" slot="end">
<ion-icon name="fas-trash" aria-hidden="true"></ion-icon>
<ion-icon name="fas-trash" aria-hidden="true" />
<span class="ion-text-wrap">{{ 'core.deletedoffline' | translate }}</span>
</ion-note>
</ion-item>

View File

@ -1,30 +1,28 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
<ion-back-button [text]="'core.back' | translate" />
</ion-buttons>
<ion-title>
<h1>{{ 'addon.calendar.calendarevents' | translate }}</h1>
</ion-title>
<ion-buttons slot="end">
<ion-button fill="clear" (click)="openFilter()" [attr.aria-label]="'core.filter' | translate">
<ion-icon slot="icon-only" name="fas-filter" aria-hidden="true"></ion-icon>
<ion-icon slot="icon-only" name="fas-filter" aria-hidden="true" />
</ion-button>
<core-context-menu>
<core-context-menu-item *ngIf="!selectedDayIsCurrent()" [priority]="900" [content]="'addon.calendar.today' | translate"
iconAction="fas-calendar-day" (action)="goToCurrentDay()">
</core-context-menu-item>
iconAction="fas-calendar-day" (action)="goToCurrentDay()" />
<core-context-menu-item [hidden]="!loaded || !selectedDayHasOffline() || !isOnline" [priority]="400"
[content]="'core.settings.synchronizenow' | translate" (action)="doRefresh(undefined, $event)" [iconAction]="syncIcon"
[closeOnClick]="false">
</core-context-menu-item>
[closeOnClick]="false" />
</core-context-menu>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="doRefresh($event.target)">
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}" />
</ion-refresher>
<core-loading [hideUntil]="loaded">
@ -34,7 +32,7 @@
<ion-row class="ion-align-items-center">
<ion-col class="ion-text-start">
<ion-button fill="clear" (click)="loadPrevious()" [attr.aria-label]="'addon.calendar.dayprev' | translate">
<ion-icon name="fas-chevron-left" slot="icon-only" aria-hidden="true"></ion-icon>
<ion-icon name="fas-chevron-left" slot="icon-only" aria-hidden="true" />
</ion-button>
</ion-col>
<ion-col class="ion-text-center addon-calendar-period">
@ -42,7 +40,7 @@
</ion-col>
<ion-col class="ion-text-end">
<ion-button fill="clear" (click)="loadNext()" [attr.aria-label]="'addon.calendar.daynext' | translate">
<ion-icon name="fas-chevron-right" slot="icon-only" aria-hidden="true"></ion-icon>
<ion-icon name="fas-chevron-right" slot="icon-only" aria-hidden="true" />
</ion-button>
</ion-col>
</ion-row>
@ -54,14 +52,13 @@
<!-- There is data to be synchronized -->
<ion-card class="core-warning-card list-item-limited-width" *ngIf="day.hasOffline">
<ion-item>
<ion-icon name="fas-triangle-exclamation" slot="start" aria-hidden="true"></ion-icon>
<ion-icon name="fas-triangle-exclamation" slot="start" aria-hidden="true" />
<ion-label>{{ 'core.hasdatatosync' | translate:{$a: 'core.day' | translate} }}</ion-label>
</ion-item>
</ion-card>
<core-empty-box *ngIf="!day.filteredEvents || !day.filteredEvents.length" icon="fas-calendar"
[message]="'addon.calendar.noevents' | translate">
</core-empty-box>
[message]="'addon.calendar.noevents' | translate" />
<ion-list *ngIf="day.filteredEvents && day.filteredEvents.length" class="list-item-limited-width">
<ng-container *ngFor="let event of day.filteredEvents">
@ -70,11 +67,9 @@
(click)="gotoEvent(event.id, day)" [class.item-dimmed]="event.ispast"
[ngClass]="['addon-calendar-eventtype-'+event.eventtype]" button [detail]="false">
<core-mod-icon *ngIf="event.moduleIcon" [modicon]="event.moduleIcon" slot="start" [showAlt]="false"
[modname]="event.modulename" [componentId]="event.instance" [purpose]="event.purpose">
</core-mod-icon>
[modname]="event.modulename" [componentId]="event.instance" [purpose]="event.purpose" />
<ion-icon *ngIf="event.eventIcon && !event.moduleIcon" [name]="event.eventIcon" slot="start"
aria-hidden="true">
</ion-icon>
aria-hidden="true" />
<ion-label>
<!-- Add the icon title so accessibility tools read it. -->
<span class="sr-only">
@ -84,18 +79,18 @@
</span>
<p class="item-heading">
<core-format-text [text]="event.name" [contextLevel]="event.contextLevel"
[contextInstanceId]="event.contextInstanceId"></core-format-text>
[contextInstanceId]="event.contextInstanceId" />
</p>
<p>
<core-format-text [text]="event.formattedtime" [filter]="false"></core-format-text>
<core-format-text [text]="event.formattedtime" [filter]="false" />
</p>
</ion-label>
<ion-note *ngIf="event.offline && !event.deleted" slot="end">
<ion-icon name="fas-clock" aria-hidden="true"></ion-icon>
<ion-icon name="fas-clock" aria-hidden="true" />
<span class="ion-text-wrap">{{ 'core.notsent' | translate }}</span>
</ion-note>
<ion-note *ngIf="event.deleted" slot="end">
<ion-icon name="fas-trash" aria-hidden="true"></ion-icon>
<ion-icon name="fas-trash" aria-hidden="true" />
<span class="ion-text-wrap">{{ 'core.deletedoffline' | translate }}</span>
</ion-note>
</ion-item>
@ -111,7 +106,7 @@
<!-- Create a calendar event. -->
<ion-fab slot="fixed" core-fab vertical="bottom" horizontal="end" *ngIf="canCreate && loaded">
<ion-fab-button (click)="openEdit()" [attr.aria-label]="'addon.calendar.newevent' | translate">
<ion-icon name="fas-plus" aria-hidden="true"></ion-icon>
<ion-icon name="fas-plus" aria-hidden="true" />
<span class="sr-only">{{ 'addon.calendar.newevent' | translate }}</span>
</ion-fab-button>
</ion-fab>

View File

@ -60,7 +60,7 @@ import { CoreTime } from '@singletons/time';
})
export class AddonCalendarDayPage implements OnInit, OnDestroy {
@ViewChild(CoreSwipeSlidesComponent) slides?: CoreSwipeSlidesComponent<PreloadedDay>;
@ViewChild(CoreSwipeSlidesComponent) swipeSlidesComponent?: CoreSwipeSlidesComponent<PreloadedDay>;
protected currentSiteId: string;
@ -434,8 +434,7 @@ export class AddonCalendarDayPage implements OnInit, OnDestroy {
*/
async goToCurrentDay(): Promise<void> {
const manager = this.manager;
const slides = this.slides;
if (!manager || !slides) {
if (!manager || !this.swipeSlidesComponent) {
return;
}
@ -448,7 +447,7 @@ export class AddonCalendarDayPage implements OnInit, OnDestroy {
// Make sure the day is loaded.
await manager.getSource().loadItem(currentDay);
slides.slideToItem(currentDay);
this.swipeSlidesComponent.slideToItem(currentDay);
} catch (error) {
CoreDomUtils.showErrorModalDefault(error, 'addon.calendar.errorloadevents', true);
} finally {
@ -460,14 +459,14 @@ export class AddonCalendarDayPage implements OnInit, OnDestroy {
* Load next day.
*/
async loadNext(): Promise<void> {
this.slides?.slideNext();
this.swipeSlidesComponent?.slideNext();
}
/**
* Load previous day.
*/
async loadPrevious(): Promise<void> {
this.slides?.slidePrev();
this.swipeSlidesComponent?.slidePrev();
}
/**

View File

@ -1,7 +1,7 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
<ion-back-button [text]="'core.back' | translate" />
</ion-buttons>
<ion-title>
<h1>{{ title | translate }}</h1>
@ -10,7 +10,7 @@
</ion-header>
<ion-content>
<ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="refreshData($event.target)">
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}" />
</ion-refresher>
<core-loading [hideUntil]="loaded">
@ -20,9 +20,8 @@
<ion-label position="stacked">
<p class="item-heading" [core-mark-required]="true">{{ 'addon.calendar.eventname' | translate }}</p>
</ion-label>
<ion-input type="text" name="name" [placeholder]="'addon.calendar.eventname' | translate" formControlName="name">
</ion-input>
<core-input-errors [control]="form.controls.name" [errorMessages]="errors"></core-input-errors>
<ion-input type="text" name="name" [placeholder]="'addon.calendar.eventname' | translate" formControlName="name" />
<core-input-errors [control]="form.controls.name" [errorMessages]="errors" />
</ion-item>
<!-- Date. -->
@ -31,9 +30,8 @@
<p class="item-heading" [core-mark-required]="true">{{ 'core.date' | translate }}</p>
</ion-label>
<ion-datetime formControlName="timestart" [placeholder]="'core.date' | translate" [displayFormat]="dateFormat"
[max]="maxDate" [min]="minDate" [displayTimezone]="displayTimezone">
</ion-datetime>
<core-input-errors [control]="form.controls.timestart" [errorMessages]="errors"></core-input-errors>
[max]="maxDate" [min]="minDate" [displayTimezone]="displayTimezone" />
<core-input-errors [control]="form.controls.timestart" [errorMessages]="errors" />
</ion-item>
<!-- Type. -->
@ -108,7 +106,7 @@
<!-- Loading groups. -->
<ion-item class="ion-text-wrap" *ngIf="loadingGroups">
<ion-label>
<ion-spinner *ngIf="loadingGroups" [attr.aria-label]="'core.loading' | translate"></ion-spinner>
<ion-spinner *ngIf="loadingGroups" [attr.aria-label]="'core.loading' | translate" />
</ion-label>
</ion-item>
</ng-container>
@ -121,7 +119,7 @@
</ion-label>
<ion-button fill="clear" (click)="addReminder()" slot="end"
[attr.aria-label]="'addon.calendar.setnewreminder' | translate">
<ion-icon name="fas-plus" slot="icon-only" aria-hidden="true"></ion-icon>
<ion-icon name="fas-plus" slot="icon-only" aria-hidden="true" />
</ion-button>
</ion-item-divider>
<ion-item *ngFor="let reminder of reminders" class="ion-text-wrap">
@ -129,7 +127,7 @@
<p>{{ reminder.label }}</p>
</ion-label>
<ion-button fill="clear" (click)="removeReminder(reminder)" [attr.aria-label]="'core.delete' | translate" slot="end">
<ion-icon name="fas-trash" color="danger" slot="icon-only" aria-hidden="true"></ion-icon>
<ion-icon name="fas-trash" color="danger" slot="icon-only" aria-hidden="true" />
</ion-button>
</ion-item>
</ng-container>
@ -146,31 +144,30 @@
<ion-label>
<p>{{ 'addon.calendar.durationnone' | translate }}</p>
</ion-label>
<ion-radio slot="end" [value]="0"></ion-radio>
<ion-radio slot="end" [value]="0" />
</ion-item>
<ion-item>
<ion-label>
<p>{{ 'addon.calendar.durationuntil' | translate }}</p>
</ion-label>
<ion-radio slot="end" [value]="1"></ion-radio>
<ion-radio slot="end" [value]="1" />
</ion-item>
<ion-item *ngIf="form.controls.duration.value === 1">
<ion-label position="stacked"></ion-label>
<ion-label position="stacked" />
<ion-datetime formControlName="timedurationuntil" [max]="maxDate" [min]="minDate"
[placeholder]="'addon.calendar.durationuntil' | translate" [displayFormat]="dateFormat"
[displayTimezone]="displayTimezone">
</ion-datetime>
[displayTimezone]="displayTimezone" />
</ion-item>
<ion-item>
<ion-label>
<p>{{ 'addon.calendar.durationminutes' | translate }}</p>
</ion-label>
<ion-radio slot="end" [value]="2"></ion-radio>
<ion-radio slot="end" [value]="2" />
</ion-item>
<ion-item *ngIf="form.controls.duration.value === 2">
<ion-label class="sr-only">{{ 'addon.calendar.durationminutes' | translate }}</ion-label>
<ion-input type="number" name="timedurationminutes" slot="end"
[placeholder]="'addon.calendar.durationminutes' | translate" formControlName="timedurationminutes"></ion-input>
[placeholder]="'addon.calendar.durationminutes' | translate" formControlName="timedurationminutes" />
</ion-item>
</ion-radio-group>
</div>
@ -181,14 +178,13 @@
<ion-label>
<p class="item-heading">{{ 'addon.calendar.repeatevent' | translate }}</p>
</ion-label>
<ion-checkbox slot="end" formControlName="repeat"></ion-checkbox>
<ion-checkbox slot="end" formControlName="repeat" />
</ion-item>
<ion-item class="ion-text-wrap">
<ion-label position="stacked">
<p class="item-heading">{{ 'addon.calendar.repeatweeksl' | translate }}</p>
</ion-label>
<ion-input type="number" name="repeats" formControlName="repeats" [disabled]="!form.controls.repeat.value">
</ion-input>
<ion-input type="number" name="repeats" formControlName="repeats" [disabled]="!form.controls.repeat.value" />
</ion-item>
</ng-container>
@ -204,13 +200,13 @@
<ion-label>
<p>{{ 'addon.calendar.repeateditall' | translate:{$a: otherEventsCount} }}</p>
</ion-label>
<ion-radio slot="end" value="1"></ion-radio>
<ion-radio slot="end" value="1" />
</ion-item>
<ion-item>
<ion-label>
<p>{{ 'addon.calendar.repeateditthis' | translate }}</p>
</ion-label>
<ion-radio slot="end" value="0"></ion-radio>
<ion-radio slot="end" value="0" />
</ion-item>
</ion-radio-group>
</div>
@ -222,7 +218,7 @@
</ion-label>
<core-rich-text-editor [control]="descriptionControl" [attr.aria-label]="'core.description' | translate"
[placeholder]="'core.description' | translate" name="description" [component]="component" [componentId]="eventId"
[autoSave]="false"></core-rich-text-editor>
[autoSave]="false" />
</ion-item>
<!-- Location. -->
@ -230,8 +226,7 @@
<ion-label position="stacked">
<p class="item-heading">{{ 'core.location' | translate }}</p>
</ion-label>
<ion-input type="text" name="location" [placeholder]="'core.location' | translate" formControlName="location">
</ion-input>
<ion-input type="text" name="location" [placeholder]="'core.location' | translate" formControlName="location" />
</ion-item>
</form>
<div collapsible-footer appearOnBottom *ngIf="loaded && !error" slot="fixed">

View File

@ -1,42 +1,38 @@
<ion-header collapsible>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
<ion-back-button [text]="'core.back' | translate" />
</ion-buttons>
<ion-title>
<h1 *ngIf="event">
<core-format-text [text]="event.name" [contextLevel]="event.contextLevel" [contextInstanceId]="event.contextInstanceId">
</core-format-text>
<core-format-text [text]="event.name" [contextLevel]="event.contextLevel" [contextInstanceId]="event.contextInstanceId" />
</h1>
</ion-title>
<ion-buttons slot="end">
<core-context-menu>
<core-context-menu-item [hidden]="!eventLoaded || (!hasOffline && event && !event.deleted) || !isOnline" [priority]="400"
[content]="'core.settings.synchronizenow' | translate" (action)="doRefresh(undefined, $event, true)"
[iconAction]="syncIcon" [closeOnClick]="false">
</core-context-menu-item>
[iconAction]="syncIcon" [closeOnClick]="false" />
<core-context-menu-item [hidden]="!event || !event.canedit || event.deleted || (!canEdit && event.id > 0)" [priority]="300"
[content]="'core.edit' | translate" (action)="openEdit()" iconAction="fas-pen">
</core-context-menu-item>
[content]="'core.edit' | translate" (action)="openEdit()" iconAction="fas-pen" />
<core-context-menu-item [hidden]="!event || !event.candelete || event.deleted" [priority]="200"
[content]="'core.delete' | translate" (action)="deleteEvent()" iconAction="fas-trash"></core-context-menu-item>
[content]="'core.delete' | translate" (action)="deleteEvent()" iconAction="fas-trash" />
<core-context-menu-item [hidden]="!event || !event.deleted" [priority]="200" [content]="'core.restore' | translate"
(action)="undoDelete()" iconAction="fas-rotate-left"></core-context-menu-item>
(action)="undoDelete()" iconAction="fas-rotate-left" />
</core-context-menu>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content [core-swipe-navigation]="events">
<ion-refresher slot="fixed" [disabled]="!eventLoaded" (ionRefresh)="doRefresh($event.target)">
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}" />
</ion-refresher>
<core-loading [hideUntil]="eventLoaded">
<ion-list *ngIf="event">
<ion-item class="ion-text-wrap addon-calendar-event" collapsible [ngClass]="['addon-calendar-eventtype-'+event.eventtype]">
<core-mod-icon *ngIf="event.moduleIcon" [modicon]="event.moduleIcon" [showAlt]="false" [modname]="event.modulename"
[componentId]="event.instance" slot="start" [purpose]="event.purpose"></core-mod-icon>
<ion-icon *ngIf=" event.eventIcon && !event.moduleIcon" [name]="event.eventIcon" aria-hidden="true" slot="start">
</ion-icon>
[componentId]="event.instance" slot="start" [purpose]="event.purpose" />
<ion-icon *ngIf=" event.eventIcon && !event.moduleIcon" [name]="event.eventIcon" aria-hidden="true" slot="start" />
<ion-label>
<!-- Add the icon title so accessibility tools read it. -->
<span class="sr-only">
@ -45,25 +41,24 @@
</span>
<h1>
<core-format-text [text]="event.name" [contextLevel]="event.contextLevel"
[contextInstanceId]="event.contextInstanceId">
</core-format-text>
[contextInstanceId]="event.contextInstanceId" />
</h1>
</ion-label>
</ion-item>
<!-- There is data to be synchronized -->
<ion-card class="core-warning-card" *ngIf="hasOffline || event.deleted">
<ion-item>
<ion-icon name="fas-triangle-exclamation" slot="start" aria-hidden="true"></ion-icon>
<ion-icon name="fas-triangle-exclamation" slot="start" aria-hidden="true" />
<ion-label>{{ 'core.hasdatatosync' | translate:{$a: 'addon.calendar.calendarevent' | translate} }}</ion-label>
</ion-item>
</ion-card>
<ion-item class="ion-text-wrap">
<ion-label>
<p class="item-heading">{{ 'addon.calendar.when' | translate }}</p>
<core-format-text [text]="event.formattedtime" [filter]="false"></core-format-text>
<core-format-text [text]="event.formattedtime" [filter]="false" />
</ion-label>
<ion-note slot="end" *ngIf="event.deleted">
<ion-icon name="fas-trash" aria-hidden="true"></ion-icon> {{ 'core.deletedoffline' | translate }}
<ion-icon name="fas-trash" aria-hidden="true" /> {{ 'core.deletedoffline' | translate }}
</ion-note>
</ion-item>
<ion-item>
@ -76,8 +71,7 @@
<ion-label>
<p class="item-heading">{{ 'core.course' | translate}}</p>
<p>
<core-format-text [text]="courseName" contextLevel="course" [contextInstanceId]="courseId">
</core-format-text>
<core-format-text [text]="courseName" contextLevel="course" [contextInstanceId]="courseId" />
</p>
</ion-label>
</ion-item>
@ -85,8 +79,7 @@
<ion-label>
<p class="item-heading">{{ 'core.group' | translate}}</p>
<p>
<core-format-text [text]="groupName" contextLevel="course" [contextInstanceId]="event.courseid">
</core-format-text>
<core-format-text [text]="groupName" contextLevel="course" [contextInstanceId]="event.courseid" />
</p>
</ion-label>
</ion-item>
@ -94,8 +87,7 @@
<ion-label>
<p class="item-heading">{{ 'core.category' | translate}}</p>
<p>
<core-format-text [text]="categoryPath" contextLevel="coursecat" [contextInstanceId]="event.categoryid">
</core-format-text>
<core-format-text [text]="categoryPath" contextLevel="coursecat" [contextInstanceId]="event.categoryid" />
</p>
</ion-label>
</ion-item>
@ -104,7 +96,7 @@
<p class="item-heading">{{ 'core.description' | translate}}</p>
<p>
<core-format-text [text]="event.description" [contextLevel]="event.contextLevel"
[contextInstanceId]="event.contextInstanceId"></core-format-text>
[contextInstanceId]="event.contextInstanceId" />
</p>
</ion-label>
</ion-item>
@ -114,7 +106,7 @@
<p>
<a [href]="event.encodedLocation" core-link auto-login="no">
<core-format-text [text]="event.location" [contextLevel]="event.contextLevel"
[contextInstanceId]="event.contextInstanceId"></core-format-text>
[contextInstanceId]="event.contextInstanceId" />
</a>
</p>
</ion-label>
@ -142,7 +134,7 @@
</ion-label>
<ion-button fill="clear" (click)="deleteReminder(reminder.id, $event)" [attr.aria-label]="'core.delete' | translate"
slot="end">
<ion-icon name="fas-trash" color="danger" slot="icon-only" aria-hidden="true"></ion-icon>
<ion-icon name="fas-trash" color="danger" slot="icon-only" aria-hidden="true" />
</ion-button>
</ion-item>
</ng-container>

View File

@ -1,55 +1,52 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
<ion-back-button [text]="'core.back' | translate" />
</ion-buttons>
<ion-title>
<h1>{{ (showCalendar ? 'addon.calendar.calendarevents' : 'addon.calendar.upcomingevents') | translate }}</h1>
</ion-title>
<ion-buttons slot="end">
<ion-button fill="clear" (click)="openFilter()" [attr.aria-label]="'core.filter' | translate">
<ion-icon slot="icon-only" name="fas-filter" aria-hidden="true"></ion-icon>
<ion-icon slot="icon-only" name="fas-filter" aria-hidden="true" />
</ion-button>
<core-context-menu>
<core-context-menu-item *ngIf="showCalendar" [priority]="800" [content]="'addon.calendar.upcomingevents' | translate"
iconAction="fas-table-list" (action)="toggleDisplay()"></core-context-menu-item>
iconAction="fas-table-list" (action)="toggleDisplay()" />
<core-context-menu-item *ngIf="!showCalendar" [priority]="800" [content]="'addon.calendar.monthlyview' | translate"
iconAction="fas-calendar-days" (action)="toggleDisplay()"></core-context-menu-item>
iconAction="fas-calendar-days" (action)="toggleDisplay()" />
<core-context-menu-item [priority]="600" [content]="'core.settings.settings' | translate" (action)="openSettings()"
iconAction="fas-gears">
</core-context-menu-item>
iconAction="fas-gears" />
<core-context-menu-item [hidden]="!loaded || !hasOffline || !isOnline" [priority]="400"
[content]="'core.settings.synchronizenow' | translate" (action)="doRefresh(undefined, $event, true)"
[iconAction]="syncIcon" [closeOnClick]="false"></core-context-menu-item>
[iconAction]="syncIcon" [closeOnClick]="false" />
</core-context-menu>
<core-user-menu-button></core-user-menu-button>
<core-user-menu-button />
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="doRefresh($event.target)">
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}" />
</ion-refresher>
<!-- There is data to be synchronized -->
<ion-card class="core-warning-card" *ngIf="hasOffline">
<ion-item>
<ion-icon name="fas-triangle-exclamation" slot="start" aria-hidden="true"></ion-icon>
<ion-icon name="fas-triangle-exclamation" slot="start" aria-hidden="true" />
<ion-label>{{ 'core.hasdatatosync' | translate:{$a: 'addon.calendar.calendar' | translate} }}</ion-label>
</ion-item>
</ion-card>
<addon-calendar-calendar [hidden]="!showCalendar" [initialYear]="year" [initialMonth]="month" [filter]="filter"
[displayNavButtons]="showCalendar" (onEventClicked)="gotoEvent($event)" (onDayClicked)="gotoDay($event)">
</addon-calendar-calendar>
[displayNavButtons]="showCalendar" (onEventClicked)="gotoEvent($event)" (onDayClicked)="gotoDay($event)" />
<addon-calendar-upcoming-events *ngIf="loadUpcoming" [hidden]="showCalendar" [filter]="filter" (onEventClicked)="gotoEvent($event)">
</addon-calendar-upcoming-events>
<addon-calendar-upcoming-events *ngIf="loadUpcoming" [hidden]="showCalendar" [filter]="filter" (onEventClicked)="gotoEvent($event)" />
<!-- Create a calendar event. -->
<ion-fab slot="fixed" core-fab vertical="bottom" horizontal="end" *ngIf="canCreate">
<ion-fab-button (click)="openEdit()" [attr.aria-label]="'addon.calendar.newevent' | translate">
<ion-icon name="fas-plus" aria-hidden="true"></ion-icon>
<ion-icon name="fas-plus" aria-hidden="true" />
<span class="sr-only">{{ 'addon.calendar.newevent' | translate }}</span>
</ion-fab-button>
</ion-fab>

View File

@ -1,7 +1,7 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
<ion-back-button [text]="'core.back' | translate" />
</ion-buttons>
<ion-title>
<h1>{{ 'core.settings.settings' | translate }}</h1>

View File

@ -1,12 +1,11 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
<ion-back-button [text]="'core.back' | translate" />
</ion-buttons>
<ion-title>
<h1>
<core-format-text [text]="title" [contextLevel]="contextLevel" [contextInstanceId]="contextInstanceId">
</core-format-text>
<core-format-text [text]="title" [contextLevel]="contextLevel" [contextInstanceId]="contextInstanceId" />
</h1>
</ion-title>
</ion-toolbar>
@ -14,7 +13,7 @@
<ion-content>
<core-split-view>
<ion-refresher slot="fixed" [disabled]="!competencies.loaded" (ionRefresh)="refreshCompetencies($event.target)">
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}" />
</ion-refresher>
<core-loading [hideUntil]="competencies.loaded">
<ion-list>
@ -24,8 +23,7 @@
<ion-label>
<p class="item-heading">
<core-format-text [text]="competency.competency.shortname" [contextLevel]="contextLevel"
[contextInstanceId]="contextInstanceId">
</core-format-text> <em>{{competency.competency.idnumber}}</em>
[contextInstanceId]="contextInstanceId" /> <em>{{competency.competency.idnumber}}</em>
</p>
</ion-label>
<ion-badge slot="end" *ngIf="competency.usercompetency"

View File

@ -1,25 +1,24 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
<ion-back-button [text]="'core.back' | translate" />
</ion-buttons>
<ion-title>
<h1 *ngIf="competency">
<core-format-text [text]="competency.competency.competency.shortname" [contextLevel]="contextLevel"
[contextInstanceId]="contextInstanceId">
</core-format-text> <small>{{ competency.competency.competency.idnumber }}</small>
[contextInstanceId]="contextInstanceId" /> <small>{{ competency.competency.competency.idnumber }}</small>
</h1>
</ion-title>
</ion-toolbar>
</ion-header>
<ion-content [core-swipe-navigation]="competencies" class="limited-width">
<ion-refresher slot="fixed" [disabled]="!competencyLoaded" (ionRefresh)="refreshCompetency($event.target)">
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}" />
</ion-refresher>
<core-loading [hideUntil]="competencyLoaded">
<ion-card *ngIf="user">
<ion-item class="ion-text-wrap">
<core-user-avatar [user]="user" slot="start"></core-user-avatar>
<core-user-avatar [user]="user" slot="start" />
<ion-label>
<p class="item-heading">{{ user.fullname }}</p>
</ion-label>
@ -30,8 +29,7 @@
<ion-item class="ion-text-wrap" *ngIf="competency.competency.competency.description">
<ion-label>
<core-format-text [text]="competency.competency.competency.description" [contextLevel]="contextLevel"
[contextInstanceId]="contextInstanceId">
</core-format-text>
[contextInstanceId]="contextInstanceId" />
</ion-label>
</ion-item>
<ion-item class="ion-text-wrap only-links">
@ -40,26 +38,22 @@
<p>
<a *ngIf="competency.competency.comppath.showlinks" [href]="competencyFrameworkUrl" core-link>
<core-format-text [text]="competency.competency.comppath.framework.name" [contextLevel]="contextLevel"
[contextInstanceId]="contextInstanceId">
</core-format-text>
[contextInstanceId]="contextInstanceId" />
</a>
<ng-container *ngIf="!competency.competency.comppath.showlinks">
<core-format-text [text]="competency.competency.comppath.framework.name" [contextLevel]="contextLevel"
[contextInstanceId]="contextInstanceId">
</core-format-text>
[contextInstanceId]="contextInstanceId" />
</ng-container>
&nbsp;/&nbsp;
<ng-container *ngFor="let ancestor of competency.competency.comppath.ancestors">
<button *ngIf="competency.competency.comppath.showlinks" (click)="openCompetencySummary(ancestor.id)"
class="as-link">
<core-format-text [text]="ancestor.name" [contextLevel]="contextLevel"
[contextInstanceId]="contextInstanceId">
</core-format-text>
[contextInstanceId]="contextInstanceId" />
</button>
<ng-container *ngIf="!competency.competency.comppath.showlinks">
<core-format-text [text]="ancestor.name" [contextLevel]="contextLevel"
[contextInstanceId]="contextInstanceId">
</core-format-text>
[contextInstanceId]="contextInstanceId" />
</ng-container>
<ng-container *ngIf="!ancestor.last">&nbsp;/&nbsp;</ng-container>
</ng-container>
@ -76,8 +70,7 @@
<p *ngFor="let relatedcomp of competency.competency.relatedcompetencies">
<button (click)="openCompetencySummary(relatedcomp.id)" class="as-link">
<core-format-text [text]="relatedcomp.shortname" [contextLevel]="contextLevel"
[contextInstanceId]="contextInstanceId">
</core-format-text> - {{ relatedcomp.idnumber }}
[contextInstanceId]="contextInstanceId" /> - {{ relatedcomp.idnumber }}
</button>
</p>
</ng-container>
@ -91,12 +84,10 @@
</p>
<ion-item class="ion-text-wrap" *ngFor="let activity of coursemodules" [href]="activity.url"
[attr.aria-label]="activity.name" core-link capture="true">
<core-mod-icon slot="start" [modicon]="activity.iconurl" [showAlt]="false" *ngIf="activity.iconurl">
</core-mod-icon>
<core-mod-icon slot="start" [modicon]="activity.iconurl" [showAlt]="false" *ngIf="activity.iconurl" />
<ion-label>
<core-format-text [text]="activity.name" contextLevel="module" [contextInstanceId]="activity.id"
[courseId]="courseId">
</core-format-text>
[courseId]="courseId" />
</ion-label>
</ion-item>
</ion-label>
@ -136,7 +127,7 @@
<ion-card *ngFor="let evidence of competency.evidence">
<ion-item class="ion-text-wrap" *ngIf="evidence.actionuser" core-user-link [userId]="evidence.actionuser.id"
[courseId]="courseId">
<core-user-avatar [user]="evidence.actionuser" slot="start" [linkProfile]="false"></core-user-avatar>
<core-user-avatar [user]="evidence.actionuser" slot="start" [linkProfile]="false" />
<ion-label>
<p class="item-heading">{{ evidence.actionuser.fullname }}</p>
<p>{{ evidence.timemodified * 1000 | coreFormatDate }}</p>

View File

@ -1,28 +1,26 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
<ion-back-button [text]="'core.back' | translate" />
</ion-buttons>
<ion-title>
<h1 *ngIf="competency">
<core-format-text [text]="competency.competency.shortname" [contextLevel]="contextLevel"
[contextInstanceId]="contextInstanceId">
</core-format-text> <small>{{ competency.competency.idnumber }}</small>
[contextInstanceId]="contextInstanceId" /> <small>{{ competency.competency.idnumber }}</small>
</h1>
</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="limited-width">
<ion-refresher slot="fixed" [disabled]="!competencyLoaded" (ionRefresh)="refreshCompetency($event.target)">
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}" />
</ion-refresher>
<core-loading [hideUntil]="competencyLoaded">
<ion-card *ngIf="competency">
<ion-item class="ion-text-wrap" *ngIf="competency.competency.description">
<ion-label>
<core-format-text [text]="competency.competency.description" [contextLevel]="contextLevel"
[contextInstanceId]="contextInstanceId">
</core-format-text>
[contextInstanceId]="contextInstanceId" />
</ion-label>
</ion-item>
<ion-item class="ion-text-wrap">
@ -30,14 +28,12 @@
<p class="item-heading">{{ 'addon.competency.path' | translate }}</p>
<p>
<core-format-text [text]="competency.comppath.framework.name" [contextLevel]="contextLevel"
[contextInstanceId]="contextInstanceId">
</core-format-text>
[contextInstanceId]="contextInstanceId" />
<ng-container *ngFor="let ancestor of competency.comppath.ancestors">
&nbsp;/&nbsp;
<button class="as-link" (click)="openCompetencySummary(ancestor.id)">
<core-format-text [text]="ancestor.name" [contextLevel]="contextLevel"
[contextInstanceId]="contextInstanceId">
</core-format-text>
[contextInstanceId]="contextInstanceId" />
</button>
</ng-container>
</p>

View File

@ -1,7 +1,7 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
<ion-back-button [text]="'core.back' | translate" />
</ion-buttons>
<ion-title>
<h1>{{ 'addon.competency.coursecompetencies' | translate }}</h1>
@ -10,7 +10,7 @@
</ion-header>
<ion-content class="limited-width">
<ion-refresher slot="fixed" [disabled]="!competencies.loaded" (ionRefresh)="refreshCourseCompetencies($event.target)">
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}" />
</ion-refresher>
<core-loading [hideUntil]="competencies.loaded">
<ion-card *ngIf="!user && courseCompetencies && courseCompetencies.statistics.competencycount > 0">
@ -29,8 +29,7 @@
{x: courseCompetencies.statistics.proficientcompetencycount, y: courseCompetencies.statistics.competencycount} } }}
</span>
<core-progress-bar [progress]="courseCompetencies.statistics.proficientcompetencypercentage"
ariaDescribedBy="addon-competency-course-{{courseId}}-progress">
</core-progress-bar>
ariaDescribedBy="addon-competency-course-{{courseId}}-progress" />
</ion-label>
</ion-item>
<ion-item class="ion-text-wrap"
@ -39,8 +38,8 @@
<p class="item-heading">{{ 'addon.competency.competenciesmostoftennotproficientincourse' | translate }}</p>
<p *ngFor="let comp of courseCompetencies.statistics.leastproficient">
<button class="as-link" (click)="openCompetencySummary(comp.id)">
<core-format-text [text]="comp.shortname" contextLevel="course" [contextInstanceId]="courseId">
</core-format-text> - {{ comp.idnumber }}
<core-format-text [text]="comp.shortname" contextLevel="course" [contextInstanceId]="courseId" /> - {{
comp.idnumber }}
</button>
</p>
</ion-label>
@ -52,15 +51,14 @@
</h2>
<ion-card *ngIf="user">
<ion-item class="ion-text-wrap">
<core-user-avatar [user]="user" slot="start"></core-user-avatar>
<core-user-avatar [user]="user" slot="start" />
<ion-label>
<p class="item-heading">{{ user.fullname }}</p>
</ion-label>
</ion-item>
</ion-card>
<core-empty-box *ngIf="courseCompetencies && courseCompetencies.statistics.competencycount === 0" icon="fas-award"
message="{{ 'addon.competency.nocompetenciesincourse' | translate }}">
</core-empty-box>
message="{{ 'addon.competency.nocompetenciesincourse' | translate }}" />
<div *ngIf="competencies.loaded">
<ion-card *ngFor="let competency of competencies.items">
@ -68,8 +66,8 @@
[attr.aria-label]="competency.competency.shortname" [detail]="true" button>
<ion-label>
<p class="item-heading">
<core-format-text [text]="competency.competency.shortname" contextLevel="course" [contextInstanceId]="courseId">
</core-format-text> <em>{{competency.competency.idnumber}}</em>
<core-format-text [text]="competency.competency.shortname" contextLevel="course"
[contextInstanceId]="courseId" /> <em>{{competency.competency.idnumber}}</em>
</p>
</ion-label>
<ion-badge slot="end" *ngIf="competency.usercompetencycourse && competency.usercompetencycourse.gradename"
@ -81,8 +79,7 @@
<ion-label>
<p *ngIf="competency.competency.description">
<core-format-text [text]="competency.competency.description" contextLevel="course"
[contextInstanceId]="courseId">
</core-format-text>
[contextInstanceId]="courseId" />
</p>
<div>
<p class="item-heading">{{ 'addon.competency.path' | translate }}</p>
@ -90,24 +87,20 @@
<a *ngIf="competency.comppath.showlinks" [href]="getCompetencyFrameworkUrl(competency)" core-link
[title]="competency.comppath.framework.name">
<core-format-text [text]="competency.comppath.framework.name" contextLevel="course"
[contextInstanceId]="courseId">
</core-format-text>
[contextInstanceId]="courseId" />
</a>
<ng-container *ngIf="!competency.comppath.showlinks">
<core-format-text [text]="competency.comppath.framework.name" contextLevel="course"
[contextInstanceId]="courseId">
</core-format-text>
[contextInstanceId]="courseId" />
</ng-container>
&nbsp;/&nbsp;
<ng-container *ngFor="let ancestor of competency.comppath.ancestors">
<button class="as-link" *ngIf="competency.comppath.showlinks"
(click)="openCompetencySummary(ancestor.id)">
<core-format-text [text]="ancestor.name" contextLevel="course" [contextInstanceId]="courseId">
</core-format-text>
<core-format-text [text]="ancestor.name" contextLevel="course" [contextInstanceId]="courseId" />
</button>
<ng-container *ngIf="!competency.comppath.showlinks">
<core-format-text [text]="ancestor.name" contextLevel="course" [contextInstanceId]="courseId">
</core-format-text>
<core-format-text [text]="ancestor.name" contextLevel="course" [contextInstanceId]="courseId" />
</ng-container>
<ng-container *ngIf="!ancestor.last">&nbsp;/&nbsp;</ng-container>
</ng-container>
@ -126,12 +119,10 @@
</p>
<ion-item class="ion-text-wrap core-course-module-handler" [attr.aria-label]="activity.name" core-link
*ngFor="let activity of competency.coursemodules" [href]="activity.url" capture="true">
<core-mod-icon slot="start" [modicon]="activity.iconurl" [showAlt]="false" *ngIf="activity.iconurl">
</core-mod-icon>
<core-mod-icon slot="start" [modicon]="activity.iconurl" [showAlt]="false" *ngIf="activity.iconurl" />
<ion-label>
<core-format-text [text]="activity.name" contextLevel="module" [contextInstanceId]="activity.id"
[courseId]="courseId">
</core-format-text>
[courseId]="courseId" />
</ion-label>
</ion-item>
</div>
@ -143,8 +134,7 @@
<ion-item class="ion-text-wrap" *ngFor="let plan of competency.plans" [href]="plan.url"
[attr.aria-label]="plan.name" core-link capture="true">
<ion-label>
<core-format-text [text]="plan.name" contextLevel="user" [contextInstanceId]="plan.userid">
</core-format-text>
<core-format-text [text]="plan.name" contextLevel="user" [contextInstanceId]="plan.userid" />
</ion-label>
</ion-item>
</div>

View File

@ -1,25 +1,24 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
<ion-back-button [text]="'core.back' | translate" />
</ion-buttons>
<ion-title>
<h1 *ngIf="plan">
<core-format-text [text]="plan.plan.name" contextLevel="user" [contextInstanceId]="plan.plan.userid">
</core-format-text>
<core-format-text [text]="plan.plan.name" contextLevel="user" [contextInstanceId]="plan.plan.userid" />
</h1>
</ion-title>
</ion-toolbar>
</ion-header>
<ion-content [core-swipe-navigation]="plans" class="limited-width">
<ion-refresher slot="fixed" [disabled]="!competencies.loaded" (ionRefresh)="refreshLearningPlan($event.target)">
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}" />
</ion-refresher>
<core-loading [hideUntil]="competencies.loaded">
<ion-card *ngIf="user">
<ion-item class="ion-text-wrap">
<ion-label>
<core-user-avatar [user]="user" slot="start"></core-user-avatar>
<core-user-avatar [user]="user" slot="start" />
<p class="item-heading">{{ user.fullname }}</p>
</ion-label>
</ion-item>
@ -29,8 +28,7 @@
<ion-item class="ion-text-wrap" *ngIf="plan.plan.description">
<ion-label>
<p>
<core-format-text [text]="plan.plan.description" contextLevel="user" [contextInstanceId]="plan.plan.userid">
</core-format-text>
<core-format-text [text]="plan.plan.description" contextLevel="user" [contextInstanceId]="plan.plan.userid" />
</p>
</ion-label>
</ion-item>
@ -50,8 +48,7 @@
<ion-label>
<p class="item-heading">{{ 'addon.competency.template' | translate }}</p>
<p>
<core-format-text [text]="plan.plan.template.shortname" contextLevel="system" [contextInstanceId]="0">
</core-format-text>
<core-format-text [text]="plan.plan.template.shortname" contextLevel="system" [contextInstanceId]="0" />
</p>
</ion-label>
</ion-item>
@ -64,8 +61,7 @@
</p>
<core-progress-bar [progress]="plan.proficientcompetencypercentage"
[text]="plan.proficientcompetencypercentageformatted"
ariaDescribedBy="addon-competency-plan-{{plan.plan.id}}-progress">
</core-progress-bar>
ariaDescribedBy="addon-competency-plan-{{plan.plan.id}}-progress" />
</ion-label>
</ion-item>
</ion-list>
@ -85,8 +81,7 @@
<ion-label>
<p class="item-heading">
<core-format-text [text]="competency.competency.shortname" contextLevel="user"
[contextInstanceId]="plan.plan.userid">
</core-format-text> <em>{{competency.competency.idnumber}}</em>
[contextInstanceId]="plan.plan.userid" /> <em>{{competency.competency.idnumber}}</em>
</p>
</ion-label>
<ion-badge *ngIf="competency.usercompetencyplan" slot="end"

View File

@ -1,7 +1,7 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
<ion-back-button [text]="'core.back' | translate" />
</ion-buttons>
<ion-title>
<h1>{{ 'addon.competency.userplans' | translate }}</h1>
@ -11,19 +11,16 @@
<ion-content>
<core-split-view>
<ion-refresher slot="fixed" [disabled]="!plans.loaded" (ionRefresh)="refreshLearningPlans($event.target)">
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}" />
</ion-refresher>
<core-loading [hideUntil]="plans.loaded">
<core-empty-box *ngIf="plans.empty" icon="fas-route" [message]="'addon.competency.noplanswerecreated' | translate">
</core-empty-box>
<core-empty-box *ngIf="plans.empty" icon="fas-route" [message]="'addon.competency.noplanswerecreated' | translate" />
<ion-list *ngIf="!plans.empty" class="ion-no-margin">
<ion-item class="ion-text-wrap" *ngFor="let plan of plans.items" [attr.aria-label]="plan.name" (click)="plans.select(plan)"
[attr.aria-current]="plans.getItemAriaCurrent(plan)" button [detail]="true">
<ion-label>
<p class="item-heading">
<core-format-text [text]="plan.name" contextLevel="user" [contextInstanceId]="plan.userid">
</core-format-text>
<core-format-text [text]="plan.name" contextLevel="user" [contextInstanceId]="plan.userid" />
</p>
<p *ngIf="plan.duedate > 0">
{{ 'addon.competency.duedate' | translate }}:&nbsp;

View File

@ -1,7 +1,7 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
<ion-back-button [text]="'core.back' | translate" />
</ion-buttons>
<ion-title>
<h1>{{ 'addon.coursecompletion.coursecompletion' | translate }}</h1>
@ -10,11 +10,11 @@
</ion-header>
<ion-content>
<ion-refresher slot="fixed" [disabled]="!completionLoaded" (ionRefresh)="refreshCompletion($event.target)">
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}" />
</ion-refresher>
<core-loading [hideUntil]="completionLoaded">
<ion-item class="ion-text-wrap" *ngIf="user">
<core-user-avatar [user]="user" [courseId]="courseId" slot="start" [linkProfile]="false"></core-user-avatar>
<core-user-avatar [user]="user" [courseId]="courseId" slot="start" [linkProfile]="false" />
<ion-label>
<p class="item-heading">{{user.fullname}}</p>
</ion-label>
@ -44,10 +44,10 @@
<ion-item class="ion-hide-md-up ion-text-wrap" *ngFor="let criteria of completion.completions">
<ion-label>
<p class="item-heading">
<core-format-text clean="true" [text]="criteria.details.criteria" [filter]="false"></core-format-text>
<core-format-text clean="true" [text]="criteria.details.criteria" [filter]="false" />
</p>
<p>
<core-format-text clean="true" [text]="criteria.details.requirement" [filter]="false"></core-format-text>
<core-format-text clean="true" [text]="criteria.details.requirement" [filter]="false" />
</p>
</ion-label>
<strong slot="end" *ngIf="criteria.complete">{{ 'core.yes' | translate }}</strong>
@ -65,23 +65,23 @@
</ion-row>
<ion-row *ngFor="let criteria of completion.completions">
<ion-col>
<core-format-text clean="true" [text]="criteria.details.type" [filter]="false"></core-format-text>
<core-format-text clean="true" [text]="criteria.details.type" [filter]="false" />
</ion-col>
<ion-col>
<core-format-text clean="true" [text]="criteria.details.criteria" [filter]="false"></core-format-text>
<core-format-text clean="true" [text]="criteria.details.criteria" [filter]="false" />
</ion-col>
<ion-col>
<core-format-text clean="true" [text]="criteria.details.requirement" [filter]="false"></core-format-text>
<core-format-text clean="true" [text]="criteria.details.requirement" [filter]="false" />
</ion-col>
<ion-col>
<core-format-text [text]="criteria.details.status" [filter]="false"></core-format-text>
<core-format-text [text]="criteria.details.status" [filter]="false" />
</ion-col>
<ion-col *ngIf="criteria.complete">{{ 'core.yes' | translate }}</ion-col>
<ion-col *ngIf="!criteria.complete">{{ 'core.no' | translate }}</ion-col>
<ion-col *ngIf="criteria.timecompleted">
{{ criteria.timecompleted * 1000 | coreFormatDate :'strftimedatetimeshort' }}
</ion-col>
<ion-col *ngIf="!criteria.timecompleted"></ion-col>
<ion-col *ngIf="!criteria.timecompleted" />
</ion-row>
</ion-label>
</ion-item>
@ -103,7 +103,7 @@
<ion-card class="core-warning-card" *ngIf="!tracked">
<ion-item>
<ion-icon name="fas-triangle-exclamation" slot="start" aria-hidden="true"></ion-icon>
<ion-icon name="fas-triangle-exclamation" slot="start" aria-hidden="true" />
<ion-label>{{ 'addon.coursecompletion.nottracked' | translate }}</ion-label>
</ion-item>
</ion-card>

View File

@ -411,7 +411,7 @@ type MathJaxWindow = Window & {
_configured: boolean; // eslint-disable-line @typescript-eslint/naming-convention
// Add the configuration to the head and set the lang.
configure: (params: Record<string, unknown>) => void;
_setLocale: () => void; // eslint-disable-line @typescript-eslint/naming-convention
_setLocale: () => void;
typeset: (container: HTMLElement) => void;
};
};

View File

@ -1,7 +1,7 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
<ion-back-button [text]="'core.back' | translate" />
</ion-buttons>
<ion-title>
<h1>{{ 'addon.messageoutput_airnotifier.processorsettingsdesc' | translate }}</h1>
@ -10,7 +10,7 @@
</ion-header>
<ion-content>
<ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="refreshDevices($event.target)">
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}" />
</ion-refresher>
<core-loading [hideUntil]="loaded">
<ng-container *ngFor="let platform of platformDevices">
@ -33,8 +33,7 @@
</p>
</ion-label>
<core-button-with-spinner [loading]="device.updating" slot="end">
<ion-toggle [(ngModel)]="device.enable" (ngModelChange)="enableDevice(device, device.enable)">
</ion-toggle>
<ion-toggle [(ngModel)]="device.enable" (ngModelChange)="enableDevice(device, device.enable)" />
</core-button-with-spinner>
</ion-item>
</ion-list>

View File

@ -5,14 +5,14 @@
</ion-title>
<ion-buttons slot="end">
<ion-button fill="clear" (click)="closeModal()" [attr.aria-label]="'core.close' | translate">
<ion-icon name="fas-xmark" slot="icon-only" aria-hidden="true"></ion-icon>
<ion-icon name="fas-xmark" slot="icon-only" aria-hidden="true" />
</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="refreshData($event.target)">
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}" />
</ion-refresher>
<core-loading [hideUntil]="loaded">
@ -23,12 +23,11 @@
onError="this.src='assets/img/group-avatar.svg'">
</div>
<h2>
<core-format-text [text]="conversation.name" contextLevel="system" [contextInstanceId]="0"></core-format-text>
<core-format-text [text]="conversation.name" contextLevel="system" [contextInstanceId]="0" />
</h2>
<p>
<core-format-text *ngIf="conversation.subname" [text]="conversation.subname" contextLevel="system"
[contextInstanceId]="0">
</core-format-text>
[contextInstanceId]="0" />
</p>
<p>{{ 'addon.messages.numparticipants' | translate:{$a: conversation.membercount} }}</p>
</ion-label>
@ -36,19 +35,16 @@
<ion-item class="ion-text-wrap addon-messages-conversation-item" *ngFor="let member of members" (click)="closeModal(member.id)"
[detail]="true" button>
<core-user-avatar [user]="member" [linkProfile]="false" [checkOnline]="member.showonlinestatus" slot="start">
</core-user-avatar>
<core-user-avatar [user]="member" [linkProfile]="false" [checkOnline]="member.showonlinestatus" slot="start" />
<ion-label>
<p class="item-heading">
{{ member.fullname }}
<ion-icon name="fas-user-slash" *ngIf="member.isblocked"
[attr.aria-label]="'addon.messages.contactblocked' | translate">
</ion-icon>
[attr.aria-label]="'addon.messages.contactblocked' | translate" />
</p>
</ion-label>
</ion-item>
<core-infinite-loading [enabled]="canLoadMore" (action)="loadMoreMembers($event)" [error]="loadMoreError">
</core-infinite-loading>
<core-infinite-loading [enabled]="canLoadMore" (action)="loadMoreMembers($event)" [error]="loadMoreError" />
</core-loading>
</ion-content>

View File

@ -1,32 +1,32 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
<ion-back-button [text]="'core.back' | translate" />
</ion-buttons>
<ion-title>
<h1>{{ 'addon.messages.contacts' | translate }}</h1>
</ion-title>
<ion-buttons slot="end">
<!-- Add an empty context menu so split view pages can add items, otherwise the menu disappears in some cases. -->
<core-context-menu></core-context-menu>
<core-context-menu />
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content>
<core-split-view>
<ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="refreshData($event.target)">
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}" />
</ion-refresher>
<core-search-box (onSubmit)="search($event)" (onClear)="clearSearch()" [placeholder]="'addon.messages.contactname' | translate"
autocorrect="off" spellcheck="false" lengthCheck="2" [disabled]="!loaded" searchArea="AddonMessagesContacts"></core-search-box>
autocorrect="off" spellcheck="false" lengthCheck="2" [disabled]="!loaded" searchArea="AddonMessagesContacts" />
<core-loading [hideUntil]="loaded" [message]="loadingMessage">
<core-empty-box *ngIf="!hasContacts && searchString === ''" icon="fas-address-book"
[message]="'addon.messages.contactlistempty' | translate"></core-empty-box>
[message]="'addon.messages.contactlistempty' | translate" />
<core-empty-box *ngIf="!hasContacts && searchString !== ''" icon="fas-address-book"
[message]="'addon.messages.nousersfound' | translate"></core-empty-box>
[message]="'addon.messages.nousersfound' | translate" />
<ion-list *ngFor="let contactType of contactTypes" class="ion-no-margin">
<ng-container *ngIf="contacts[contactType] && (contacts[contactType].length > 0 || contactType === searchType)">
@ -44,7 +44,7 @@
*ngIf="contact.profileimageurl || contact.profileimageurlsmall" [attr.aria-label]="contact.fullname"
(click)="gotoDiscussion(contact.id)" [detail]="true" button
[attr.aria-current]="contact.id === discussionUserId ? 'page' : 'false'">
<core-user-avatar [user]="contact" slot="start" [checkOnline]="contact.showonlinestatus"></core-user-avatar>
<core-user-avatar [user]="contact" slot="start" [checkOnline]="contact.showonlinestatus" />
<ion-label>
<p class="item-heading">{{ contact.fullname }}</p>
</ion-label>

View File

@ -1,17 +1,17 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
<ion-back-button [text]="'core.back' | translate" />
</ion-buttons>
<ion-title>
<h1>{{ 'addon.messages.contacts' | translate }}</h1>
</ion-title>
<ion-buttons slot="end">
<ion-button fill="clear" (click)="gotoSearch()" [attr.aria-label]="'addon.messages.searchcombined' | translate">
<ion-icon name="fas-magnifying-glass" slot="icon-only" aria-hidden="true"></ion-icon>
<ion-icon name="fas-magnifying-glass" slot="icon-only" aria-hidden="true" />
</ion-button>
<!-- Add an empty context menu so split view pages can add items, otherwise the menu disappears in some cases. -->
<core-context-menu></core-context-menu>
<core-context-menu />
</ion-buttons>
</ion-toolbar>
</ion-header>
@ -23,7 +23,7 @@
<core-tab [title]="'addon.messages.contacts' | translate" (ionSelect)="selectTab('confirmed')">
<ng-template>
<ion-refresher slot="fixed" [disabled]="!confirmedLoaded" (ionRefresh)="refreshData($event.target)">
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}" />
</ion-refresher>
<core-loading [hideUntil]="confirmedLoaded">
<ion-list class="ion-no-margin" *ngIf="confirmedContacts.length">
@ -31,27 +31,22 @@
*ngFor="let contact of confirmedContacts" [attr.aria-label]="contact.fullname" [detail]="true"
[attr.aria-current]="contact.id === selectedUserId ? 'page' : 'false'">
<core-user-avatar slot="start" [user]="contact" [checkOnline]="contact.showonlinestatus"
[linkProfile]="false">
</core-user-avatar>
[linkProfile]="false" />
<ion-label>
<p class="item-heading">
<core-format-text [text]="contact.fullname" contextLevel="system" [contextInstanceId]="0">
</core-format-text>
<core-format-text [text]="contact.fullname" contextLevel="system" [contextInstanceId]="0" />
<ion-icon *ngIf="contact.isblocked" name="fas-user-slash" slot="end"
[attr.aria-label]="'addon.messages.contactblocked' | translate">
</ion-icon>
[attr.aria-label]="'addon.messages.contactblocked' | translate" />
</p>
</ion-label>
</ion-item>
</ion-list>
<core-empty-box *ngIf="!confirmedContacts.length" icon="far-address-book"
[message]="'addon.messages.nocontactsgetstarted' | translate">
</core-empty-box>
[message]="'addon.messages.nocontactsgetstarted' | translate" />
<core-infinite-loading [enabled]="confirmedCanLoadMore" (action)="loadMore($event)" [error]="confirmedLoadMoreError"
position="bottom">
</core-infinite-loading>
position="bottom" />
</core-loading>
</ng-template>
</core-tab>
@ -61,17 +56,16 @@
badgeA11yText="addon.messages.pendingcontactrequests">
<ng-template>
<ion-refresher slot="fixed" [disabled]="!requestsLoaded" (ionRefresh)="refreshData($event.target)">
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}" />
</ion-refresher>
<core-loading [hideUntil]="requestsLoaded">
<ion-list class="ion-no-margin" *ngIf="requests.length">
<ion-item class="ion-text-wrap addon-messages-conversation-item" *ngFor="let request of requests"
[attr.aria-label]="request.fullname" (click)="selectUser(request.id)" button
[attr.aria-current]="request.id === selectedUserId ? 'page' : 'false'" [detail]="true">
<core-user-avatar slot="start" [user]="request" [linkProfile]="false"></core-user-avatar>
<core-user-avatar slot="start" [user]="request" [linkProfile]="false" />
<ion-label>
<core-format-text [text]="request.fullname" contextLevel="system" [contextInstanceId]="0">
</core-format-text>
<core-format-text [text]="request.fullname" contextLevel="system" [contextInstanceId]="0" />
<p *ngIf="!request.iscontact">
{{ 'addon.messages.wouldliketocontactyou' | translate }}
</p>
@ -79,11 +73,9 @@
</ion-item>
</ion-list>
<core-empty-box *ngIf="!requests.length" icon="far-address-book"
[message]="'addon.messages.nocontactrequests' | translate">
</core-empty-box>
[message]="'addon.messages.nocontactrequests' | translate" />
<core-infinite-loading [enabled]="requestsCanLoadMore" (action)="loadMore($event)" [error]="requestsLoadMoreError"
position="bottom">
</core-infinite-loading>
position="bottom" />
</core-loading>
</ng-template>
</core-tab>

View File

@ -1,67 +1,57 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
<ion-back-button [text]="'core.back' | translate" />
</ion-buttons>
<ion-title>
<h1>
<img *ngIf="loaded && !otherMember && conversationImage" class="core-bar-button-image" [src]="conversationImage" alt=""
onError="this.src='assets/img/group-avatar.svg'" core-external-content role="presentation" [siteId]="siteId">
<core-user-avatar *ngIf="loaded && otherMember" class="core-bar-button-image" [user]="otherMember" [linkProfile]="false"
[checkOnline]="otherMember.showonlinestatus">
</core-user-avatar>
<core-format-text [text]="title" contextLevel="system" [contextInstanceId]="0"></core-format-text>
[checkOnline]="otherMember.showonlinestatus" />
<core-format-text [text]="title" contextLevel="system" [contextInstanceId]="0" />
<ion-icon *ngIf="conversation && conversation.isfavourite" name="fas-star"
[attr.aria-label]="'core.favourites' | translate">
</ion-icon>
[attr.aria-label]="'core.favourites' | translate" />
<ion-icon *ngIf="conversation && conversation.ismuted" name="fas-bell-slash"
[attr.aria-label]="'addon.messages.mutedconversation' | translate">
</ion-icon>
[attr.aria-label]="'addon.messages.mutedconversation' | translate" />
</h1>
</ion-title>
<ion-buttons slot="end"></ion-buttons>
<ion-buttons slot="end" />
</ion-toolbar>
<core-navbar-buttons slot="end">
<core-context-menu [attr.aria-label]="'addon.messages.conversationactions' | translate">
<core-context-menu-item [hidden]="isSelf || !showInfo || isGroup" [priority]="1000"
[content]="'addon.messages.info' | translate" (action)="viewInfo()" iconAction="fas-circle-info"></core-context-menu-item>
[content]="'addon.messages.info' | translate" (action)="viewInfo()" iconAction="fas-circle-info" />
<core-context-menu-item [hidden]="isSelf || !showInfo || !isGroup" [priority]="1000"
[content]="'addon.messages.groupinfo' | translate" (action)="viewInfo()" iconAction="fas-circle-info">
</core-context-menu-item>
[content]="'addon.messages.groupinfo' | translate" (action)="viewInfo()" iconAction="fas-circle-info" />
<core-context-menu-item [hidden]="!groupMessagingEnabled || !conversation" [priority]="800" (action)="changeFavourite($event)"
[closeOnClick]="false" [content]="(conversation && conversation.isfavourite ? 'addon.messages.removefromfavourites' :
'addon.messages.addtofavourites') | translate" [iconAction]="favouriteIcon" [iconSlash]="favouriteIconSlash">
</core-context-menu-item>
'addon.messages.addtofavourites') | translate" [iconAction]="favouriteIcon" [iconSlash]="favouriteIconSlash" />
<core-context-menu-item [hidden]="isSelf || !otherMember || otherMember.isblocked" [priority]="700"
[content]="'addon.messages.blockuser' | translate" (action)="blockUser()" [iconAction]="blockIcon">
</core-context-menu-item>
[content]="'addon.messages.blockuser' | translate" (action)="blockUser()" [iconAction]="blockIcon" />
<core-context-menu-item [hidden]="isSelf || !otherMember || !otherMember.isblocked" [priority]="700"
[content]="'addon.messages.unblockuser' | translate" (action)="unblockUser()" [iconAction]="blockIcon">
</core-context-menu-item>
[content]="'addon.messages.unblockuser' | translate" (action)="unblockUser()" [iconAction]="blockIcon" />
<core-context-menu-item [hidden]="isSelf || !muteEnabled || !conversation" [priority]="600" (action)="changeMute($event)"
[closeOnClick]="false" [content]="(conversation && conversation.ismuted ? 'addon.messages.unmuteconversation' :
'addon.messages.muteconversation') | translate" [iconAction]="muteIcon"></core-context-menu-item>
'addon.messages.muteconversation') | translate" [iconAction]="muteIcon" />
<core-context-menu-item [hidden]="!canDelete || !messages || !messages.length" [priority]="400"
[content]="'addon.messages.showdeletemessages' | translate" iconAction="toggle" [(toggle)]="showDelete">
</core-context-menu-item>
[content]="'addon.messages.showdeletemessages' | translate" iconAction="toggle" [(toggle)]="showDelete" />
<core-context-menu-item [hidden]="!groupMessagingEnabled || !conversationId || isGroup || !messages || !messages.length"
[priority]="200" [content]="'addon.messages.deleteconversation' | translate" (action)="deleteConversation($event)"
[closeOnClick]="false" [iconAction]="deleteIcon"></core-context-menu-item>
[closeOnClick]="false" [iconAction]="deleteIcon" />
<core-context-menu-item
[hidden]="isSelf || !otherMember || otherMember.iscontact || requestContactSent || requestContactReceived" [priority]="100"
[content]="'addon.messages.addtoyourcontacts' | translate" (action)="createContactRequest()" [iconAction]="addRemoveIcon">
</core-context-menu-item>
[content]="'addon.messages.addtoyourcontacts' | translate" (action)="createContactRequest()" [iconAction]="addRemoveIcon" />
<core-context-menu-item [hidden]="isSelf || !otherMember || !otherMember.iscontact" [priority]="100"
[content]="'addon.messages.removefromyourcontacts' | translate" (action)="removeContact()" [iconAction]="addRemoveIcon"
[iconSlash]="true"></core-context-menu-item>
[iconSlash]="true" />
</core-context-menu>
</core-navbar-buttons>
</ion-header>
<ion-content (ionScroll)="scrollFunction()">
<core-loading [hideUntil]="loaded">
<!-- Load previous messages. -->
<core-infinite-loading [enabled]="canLoadMore" (action)="loadPrevious($event)" position="top" [error]="loadMoreError">
</core-infinite-loading>
<core-infinite-loading [enabled]="canLoadMore" (action)="loadPrevious($event)" position="top" [error]="loadMoreError" />
<ng-container *ngIf="isSelf && !canLoadMore">
<p class="ion-text-center">{{ 'addon.messages.selfconversation' | translate }}</p>
@ -78,25 +68,23 @@
<ion-chip class="addon-messages-unreadfrom" *ngIf="unreadMessageFrom > 0 && message.id === unreadMessageFrom" color="light">
<ion-label>{{ 'addon.messages.newmessages' | translate }}</ion-label>
<ion-icon name="fas-arrow-down" aria-hidden="true"></ion-icon>
<ion-icon name="fas-arrow-down" aria-hidden="true" />
</ion-chip>
<core-message [message]="message" [user]="members[message.useridfrom]" (afterRender)="last && scrollToBottom()"
[text]="message.text" (onDeleteMessage)="deleteMessage(message, index)" [showDelete]="showDelete"
[time]="message.timecreated">
</core-message>
[time]="message.timecreated" />
</ng-container>
</ion-list>
<core-empty-box *ngIf="!messages || messages.length <= 0" icon="far-comments"
[message]="'addon.messages.nomessagesfound' | translate">
</core-empty-box>
[message]="'addon.messages.nomessagesfound' | translate" />
</core-loading>
<!-- Scroll bottom. -->
<ion-fab slot="fixed" core-fab vertical="bottom" horizontal="end" *ngIf="loaded && newMessages > 0">
<ion-fab-button size="small" (click)="scrollToFirstUnreadMessage()" color="light"
[attr.aria-label]="'addon.messages.newmessages' | translate">
<ion-icon name="fas-arrow-down" aria-hidden="true"></ion-icon>
<ion-icon name="fas-arrow-down" aria-hidden="true" />
<span class="sr-only">{{ 'addon.messages.newmessages' | translate }}</span>
</ion-fab-button>
<ion-badge class="core-discussion-messages-badge">{{ newMessages }}</ion-badge>
@ -138,6 +126,6 @@
</p>
</div>
<core-send-message-form *ngIf="footerType === 'message'" (onSubmit)="sendMessage($event)" [showKeyboard]="showKeyboard"
[placeholder]="'addon.messages.newmessage' | translate"></core-send-message-form>
[placeholder]="'addon.messages.newmessage' | translate" />
</ion-toolbar>
</ion-footer>

View File

@ -1,27 +1,27 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
<ion-back-button [text]="'core.back' | translate" />
</ion-buttons>
<ion-title>
<h1>{{ 'addon.messages.messages' | translate }}</h1>
</ion-title>
<ion-buttons slot="end">
<!-- Add an empty context menu so split view pages can add items, otherwise the menu disappears in some cases. -->
<core-context-menu></core-context-menu>
<core-user-menu-button></core-user-menu-button>
<core-context-menu />
<core-user-menu-button />
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content>
<core-split-view>
<ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="refreshData($event.target)">
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}" />
</ion-refresher>
<core-search-box (onSubmit)="searchMessage($event)" (onClear)="clearSearch()" [placeholder]=" 'addon.messages.message' | translate"
autocorrect="off" spellcheck="false" lengthCheck="2" [disabled]="!loaded" searchArea="AddonMessagesDiscussions"
[autoFocus]="false"></core-search-box>
[autoFocus]="false" />
<core-loading [hideUntil]="loaded" [message]="loadingMessage">
@ -29,7 +29,7 @@
<ion-item class="ion-text-wrap addon-message-discussion" (click)="gotoContacts()"
[attr.aria-label]="'addon.messages.contacts' | translate" [detail]="true" button>
<ion-icon name="fas-address-book" slot="start" aria-hidden="true"></ion-icon>
<ion-icon name="fas-address-book" slot="start" aria-hidden="true" />
<ion-label>
<p class="item-heading">{{ 'addon.messages.contacts' | translate }}</p>
</ion-label>
@ -47,12 +47,12 @@
<ion-item class="ion-text-wrap addon-message-discussion" *ngFor="let result of search.results" button
[attr.aria-label]="result.fullname" (click)="gotoDiscussion(result.userid, result.messageid)"
[attr.aria-current]="result.userid === discussionUserId ? 'page' : 'false'" [detail]="false">
<core-user-avatar [user]="result" slot="start" [checkOnline]="result.showonlinestatus"></core-user-avatar>
<core-user-avatar [user]="result" slot="start" [checkOnline]="result.showonlinestatus" />
<ion-label>
<p class="item-heading">{{ result.fullname }}</p>
<p>
<core-format-text clean="true" singleLine="true" [text]="result.lastmessage" contextLevel="system"
[contextInstanceId]="0"></core-format-text>
[contextInstanceId]="0" />
</p>
</ion-label>
</ion-item>
@ -61,7 +61,7 @@
<ion-item class="ion-text-wrap addon-message-discussion" *ngFor="let discussion of discussions" button
[attr.aria-label]="discussion.fullname" (click)="gotoDiscussion(discussion.message!.user)"
[attr.aria-current]="discussion.message!.user === discussionUserId ? 'page' : 'false'" [detail]="false">
<core-user-avatar [user]="discussion" slot="start" checkOnline="false"></core-user-avatar>
<core-user-avatar [user]="discussion" slot="start" checkOnline="false" />
<ion-label>
<div class="flex-row ion-justify-content-between">
<p class="item-heading">{{ discussion.fullname }}</p>
@ -69,8 +69,7 @@
<span *ngIf="discussion.message!.timecreated > 0" class="addon-message-last-message-date">
{{discussion.message!.timecreated / 1000 | coreDateDayOrTime}}
</span>
<ion-icon *ngIf="discussion.unread" name="fas-circle" color="primary" aria-hidden="true">
</ion-icon>
<ion-icon *ngIf="discussion.unread" name="fas-circle" color="primary" aria-hidden="true" />
<span *ngIf="discussion.unread" class="sr-only">
{{ 'addon.messages.unreadmessages' | translate }}
</span>
@ -78,8 +77,7 @@
</div>
<p>
<core-format-text clean="true" singleLine="true" [text]="discussion.message!.message" contextLevel="system"
[contextInstanceId]="0">
</core-format-text>
[contextInstanceId]="0" />
</p>
</ion-label>
</ion-item>
@ -87,10 +85,10 @@
</ion-list>
<core-empty-box *ngIf="(!discussions || discussions.length <= 0) && !search.showResults" icon="far-comments"
[message]="'addon.messages.nomessagesfound' | translate"></core-empty-box>
[message]="'addon.messages.nomessagesfound' | translate" />
<core-empty-box *ngIf="(!search.results || search.results.length <= 0) && search.showResults" icon="fas-magnifying-glass"
[message]="'core.noresults' | translate"></core-empty-box>
[message]="'core.noresults' | translate" />
</core-loading>
</core-split-view>
</ion-content>

View File

@ -1,34 +1,34 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
<ion-back-button [text]="'core.back' | translate" />
</ion-buttons>
<ion-title>
<h1>{{ 'addon.messages.messages' | translate }}</h1>
</ion-title>
<ion-buttons slot="end">
<ion-button fill="clear" (click)="gotoSearch()" [attr.aria-label]="'addon.messages.searchcombined' | translate">
<ion-icon name="fas-magnifying-glass" slot="icon-only" aria-hidden="true"></ion-icon>
<ion-icon name="fas-magnifying-glass" slot="icon-only" aria-hidden="true" />
</ion-button>
<ion-button (click)="gotoSettings()" [attr.aria-label]="'addon.messages.messagepreferences' | translate">
<ion-icon name="fas-gear" slot="icon-only" aria-hidden="true"></ion-icon>
<ion-icon name="fas-gear" slot="icon-only" aria-hidden="true" />
</ion-button>
<!-- Add an empty context menu so split view pages can add items, otherwise the menu disappears in some cases. -->
<core-context-menu></core-context-menu>
<core-user-menu-button></core-user-menu-button>
<core-context-menu />
<core-user-menu-button />
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content>
<core-split-view>
<ion-refresher slot="fixed" [disabled]="!loaded || !currentListEl" (ionRefresh)="refreshData($event.target)">
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}" />
</ion-refresher>
<core-loading [hideUntil]="loaded" [message]="loadingMessage">
<ion-list>
<ion-item class="ion-text-wrap addon-message-discussion" (click)="gotoContacts()" [detail]="true" button>
<ion-icon name="fas-address-book" slot="start" aria-hidden="true"></ion-icon>
<ion-icon name="fas-address-book" slot="start" aria-hidden="true" />
<ion-label>
<h2>{{ 'addon.messages.contacts' | translate }}</h2>
</ion-label>
@ -43,8 +43,7 @@
[attr.aria-expanded]="favourites.expanded" aria-controls="addon-messages-groupconversations-favourite" role="heading"
[detail]="false">
<ion-icon name="fas-chevron-right" flip-rtl slot="start" aria-hidden="true" class="expandable-status-icon"
[class.expandable-status-icon-expanded]="favourites.expanded">
</ion-icon>
[class.expandable-status-icon-expanded]="favourites.expanded" />
<ion-label>
<h2>{{ 'core.favourites' | translate }} ({{ favourites.count }})</h2>
</ion-label>
@ -55,11 +54,10 @@
</ion-item>
<div [hidden]="!favourites.conversations || !favourites.expanded || favourites.loading" #favlist
id="addon-messages-groupconversations-favourite">
<ng-container *ngTemplateOutlet="conversationsTemplate; context: {conversations: favourites.conversations}">
</ng-container>
<ng-container *ngTemplateOutlet="conversationsTemplate; context: {conversations: favourites.conversations}" />
<!-- The infinite loading cannot be inside the ng-template, it fails because it doesn't find ion-content. -->
<core-infinite-loading [enabled]="favourites.canLoadMore" (action)="loadMoreConversations(favourites, $event)"
[error]="favourites.loadMoreError"></core-infinite-loading>
[error]="favourites.loadMoreError" />
<ion-item class="ion-text-wrap" *ngIf="favourites.conversations && favourites.conversations.length === 0">
<ion-label>
<p>{{ 'addon.messages.nofavourites' | translate }}</p>
@ -68,7 +66,7 @@
</div>
<ion-item class="ion-text-center" *ngIf="favourites.loading">
<ion-label>
<ion-spinner [attr.aria-label]="'core.loading' | translate"></ion-spinner>
<ion-spinner [attr.aria-label]="'core.loading' | translate" />
</ion-label>
</ion-item>
@ -77,8 +75,7 @@
[attr.aria-label]="(group.expanded ? 'core.collapse' : 'core.expand') | translate" [attr.aria-expanded]="group.expanded"
aria-controls="addon-messages-groupconversations-group" role="heading" [detail]="false">
<ion-icon name="fas-chevron-right" flip-rtl slot="start" aria-hidden="true" class="expandable-status-icon"
[class.expandable-status-icon-expanded]="group.expanded">
</ion-icon>
[class.expandable-status-icon-expanded]="group.expanded" />
<ion-label>
<h2>{{ 'addon.messages.groupconversations' | translate }} ({{ group.count }})</h2>
</ion-label>
@ -89,11 +86,10 @@
</ion-item>
<div [hidden]="!group.conversations || !group.expanded || group.loading" #grouplist
id="addon-messages-groupconversations-group">
<ng-container *ngTemplateOutlet="conversationsTemplate; context: {conversations: group.conversations}">
</ng-container>
<ng-container *ngTemplateOutlet="conversationsTemplate; context: {conversations: group.conversations}" />
<!-- The infinite loading cannot be inside the ng-template, it fails because it doesn't find ion-content. -->
<core-infinite-loading [enabled]="group.canLoadMore" (action)="loadMoreConversations(group, $event)"
[error]="group.loadMoreError"></core-infinite-loading>
[error]="group.loadMoreError" />
<ion-item class="ion-text-wrap" *ngIf="group.conversations && group.conversations.length === 0">
<ion-label>
<p>{{ 'addon.messages.nogroupconversations' | translate }}</p>
@ -102,7 +98,7 @@
</div>
<ion-item class="ion-text-center" *ngIf="group.loading">
<ion-label>
<ion-spinner [attr.aria-label]="'core.loading' | translate"></ion-spinner>
<ion-spinner [attr.aria-label]="'core.loading' | translate" />
</ion-label>
</ion-item>
@ -111,8 +107,7 @@
[attr.aria-expanded]="individual.expanded" aria-controls="addon-messages-groupconversations-individual" role="heading"
[detail]="false">
<ion-icon name="fas-chevron-right" flip-rtl slot="start" aria-hidden="true" class="expandable-status-icon"
[class.expandable-status-icon-expanded]="individual.expanded">
</ion-icon>
[class.expandable-status-icon-expanded]="individual.expanded" />
<ion-label>
<h2>{{ 'addon.messages.individualconversations' | translate }} ({{ individual.count }})</h2>
</ion-label>
@ -123,11 +118,10 @@
</ion-item>
<div [hidden]="!individual.conversations || !individual.expanded || individual.loading" #indlist
id="addon-messages-groupconversations-individual">
<ng-container *ngTemplateOutlet="conversationsTemplate; context: {conversations: individual.conversations}">
</ng-container>
<ng-container *ngTemplateOutlet="conversationsTemplate; context: {conversations: individual.conversations}" />
<!-- The infinite loading cannot be inside the ng-template, it fails because it doesn't find ion-content. -->
<core-infinite-loading [enabled]="individual.canLoadMore" (action)="loadMoreConversations(individual, $event)"
[error]="individual.loadMoreError"></core-infinite-loading>
[error]="individual.loadMoreError" />
<ion-item class="ion-text-wrap" *ngIf="individual.conversations && individual.conversations.length === 0">
<ion-label>
<p>{{ 'addon.messages.noindividualconversations' | translate }}</p>
@ -136,7 +130,7 @@
</div>
<ion-item class="ion-text-center" *ngIf="individual.loading">
<ion-label>
<ion-spinner [attr.aria-label]="'core.loading' | translate"></ion-spinner>
<ion-spinner [attr.aria-label]="'core.loading' | translate" />
</ion-label>
</ion-item>
@ -148,9 +142,9 @@
<!-- Template to render a list of conversations. -->
<ng-template #conversationsTemplate let-conversations="conversations">
<ion-item class="ion-text-wrap addon-message-discussion" *ngFor="let conversation of conversations" button [detail]="false"
[attr.aria-current]="((conversation.id && conversation.id === selectedConversationId) ||
(conversation.userid && conversation.userid === selectedUserId)) ? 'page': 'false'"
(click)="gotoConversation(conversation.id, conversation.userid)"
[attr.aria-current]="((conversation.id &&
conversation.id === selectedConversationId) || (conversation.userid && conversation.userid === selectedUserId)) ? 'page': 'false'"
id="addon-message-conversation-{{ conversation.id ? conversation.id : 'user-' + conversation.userid }}"
[attr.aria-label]="conversation.name">
<!-- Group conversation image. -->
@ -161,16 +155,15 @@
<!-- Avatar for individual conversations. -->
<core-user-avatar *ngIf="conversation.type !== typeGroup" core-user-avatar [user]="conversation.otherUser" [linkProfile]="false"
[checkOnline]="conversation.showonlinestatus" slot="start"></core-user-avatar>
[checkOnline]="conversation.showonlinestatus" slot="start" />
<ion-label>
<div class="flex-row ion-justify-content-between">
<p class="item-heading">
<core-format-text [text]="conversation.name" contextLevel="system" [contextInstanceId]="0"></core-format-text>
<ion-icon name="fas-user-slash" *ngIf="conversation.isblocked" [title]="'addon.messages.contactblocked' | translate">
</ion-icon>
<ion-icon *ngIf="conversation.ismuted" name="fas-volume-xmark" [title]="'addon.messages.mutedconversation' | translate">
</ion-icon>
<core-format-text [text]="conversation.name" contextLevel="system" [contextInstanceId]="0" />
<ion-icon name="fas-user-slash" *ngIf="conversation.isblocked" [title]="'addon.messages.contactblocked' | translate" />
<ion-icon *ngIf="conversation.ismuted" name="fas-volume-xmark"
[title]="'addon.messages.mutedconversation' | translate" />
</p>
<ion-note *ngIf="conversation.lastmessagedate > 0 || conversation.unreadcount">
<span *ngIf="conversation.lastmessagedate > 0" class="addon-message-last-message-date">
@ -183,7 +176,7 @@
</ion-note>
</div>
<p *ngIf="conversation.subname">
<core-format-text [text]="conversation.subname" contextLevel="system" [contextInstanceId]="0"></core-format-text>
<core-format-text [text]="conversation.subname" contextLevel="system" [contextInstanceId]="0" />
</p>
<p *ngIf="conversation.lastmessage !== undefined" class="addon-message-last-message">
<span *ngIf="conversation.sentfromcurrentuser" class="addon-message-last-message-user">
@ -192,7 +185,7 @@
<span *ngIf="!conversation.sentfromcurrentuser && conversation.type === typeGroup && conversation.members[0]"
class="addon-message-last-message-user">{{ conversation.members[0].fullname + ':' }}</span>
<core-format-text clean="true" singleLine="true" [text]="conversation.lastmessage" class="addon-message-last-message-text"
contextLevel="system" [contextInstanceId]="0"></core-format-text>
contextLevel="system" [contextInstanceId]="0" />
</p>
</ion-label>
</ion-item>

View File

@ -1,35 +1,34 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
<ion-back-button [text]="'core.back' | translate" />
</ion-buttons>
<ion-title>
<h1>{{ 'addon.messages.searchcombined' | translate }}</h1>
</ion-title>
<ion-buttons slot="end">
<!-- Add an empty context menu so split view pages can add items, otherwise the menu disappears in some cases. -->
<core-context-menu></core-context-menu>
<core-context-menu />
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content>
<core-split-view>
<core-search-box (onSubmit)="search($event)" (onClear)="clearSearch()" [disabled]="disableSearch" autocorrect="off"
[spellcheck]="false" [autoFocus]="true" [lengthCheck]="1" searchArea="AddonMessagesSearch"></core-search-box>
[spellcheck]="false" [autoFocus]="true" [lengthCheck]="1" searchArea="AddonMessagesSearch" />
<core-loading [hideUntil]="!displaySearching" [message]="'core.searching' | translate">
<ion-list *ngIf="displayResults">
<ng-container *ngTemplateOutlet="resultsTemplate; context: {item: contacts}"></ng-container>
<ng-container *ngTemplateOutlet="resultsTemplate; context: {item: nonContacts}"></ng-container>
<ng-container *ngTemplateOutlet="resultsTemplate; context: {item: messages}"></ng-container>
<ng-container *ngTemplateOutlet="resultsTemplate; context: {item: contacts}" />
<ng-container *ngTemplateOutlet="resultsTemplate; context: {item: nonContacts}" />
<ng-container *ngTemplateOutlet="resultsTemplate; context: {item: messages}" />
<!-- The infinite loading cannot be inside the ng-template, it fails because it doesn't find ion-content. -->
<core-infinite-loading [enabled]="messages.canLoadMore" (action)="search(query, 'messages', $event)"
[error]="messages.loadMoreError"></core-infinite-loading>
[error]="messages.loadMoreError" />
</ion-list>
<core-empty-box *ngIf="displayResults && !contacts.results.length && !nonContacts.results.length && !messages.results.length"
icon="fas-magnifying-glass" [message]="'core.noresults' | translate">
</core-empty-box>
icon="fas-magnifying-glass" [message]="'core.noresults' | translate" />
</core-loading>
</core-split-view>
</ion-content>
@ -46,13 +45,11 @@
<!-- List of results -->
<ion-item class="addon-message-discussion ion-text-wrap" *ngFor="let result of item.results" [attr.aria-label]="result.fullname"
(click)="openConversation(result)" [attr.aria-current]="result === selectedResult ? 'page' : 'false'" [detail]="true" button>
<core-user-avatar slot="start" [user]="result" [checkOnline]="true" [linkProfile]="false"></core-user-avatar>
<core-user-avatar slot="start" [user]="result" [checkOnline]="true" [linkProfile]="false" />
<ion-label>
<p class="item-heading">
<core-format-text [text]="result.fullname" [highlight]="result.highlightName" [filter]="false">
</core-format-text>
<ion-icon name="fas-ban" *ngIf="result.isblocked" [attr.aria-label]="'addon.messages.contactblocked' | translate">
</ion-icon>
<core-format-text [text]="result.fullname" [highlight]="result.highlightName" [filter]="false" />
<ion-icon name="fas-ban" *ngIf="result.isblocked" [attr.aria-label]="'addon.messages.contactblocked' | translate" />
</p>
<ion-note *ngIf="result.lastmessagedate > 0">
{{result.lastmessagedate | coreDateDayOrTime}}
@ -62,7 +59,7 @@
{{ 'addon.messages.you' | translate }}
</span>
<core-format-text clean="true" singleLine="true" [text]="result.lastmessage" [highlight]="result.highlightMessage"
contextLevel="system" [contextInstanceId]="0" class="addon-message-last-message-text"></core-format-text>
contextLevel="system" [contextInstanceId]="0" class="addon-message-last-message-text" />
</p>
</ion-label>
</ion-item>
@ -75,7 +72,7 @@
</ion-button>
</div>
<div *ngIf="item.loadingMore" class="ion-padding ion-text-center">
<ion-spinner [attr.aria-label]="'core.loading' | translate"></ion-spinner>
<ion-spinner [attr.aria-label]="'core.loading' | translate" />
</div>
</ng-container>
</ng-container>

View File

@ -1,7 +1,7 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
<ion-back-button [text]="'core.back' | translate" />
</ion-buttons>
<ion-title>
<h1>{{ 'addon.messages.messages' | translate }}</h1>
@ -10,7 +10,7 @@
</ion-header>
<ion-content>
<ion-refresher slot="fixed" [disabled]="!preferencesLoaded" (ionRefresh)="refreshPreferences($event.target)">
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}" />
</ion-refresher>
<core-loading [hideUntil]="preferencesLoaded">
<!-- General settings. -->
@ -25,7 +25,7 @@
<ion-label>
<p>{{ 'addon.messages.useentertosend' | translate }}</p>
</ion-label>
<ion-toggle [(ngModel)]="sendOnEnter" (ngModelChange)="sendOnEnterChanged()" slot="end"></ion-toggle>
<ion-toggle [(ngModel)]="sendOnEnter" (ngModelChange)="sendOnEnterChanged()" slot="end" />
</ion-item>
</ion-list>
</ion-card>
@ -36,8 +36,7 @@
<ion-label class="ion-text-wrap">
<p>{{ 'addon.messages.blocknoncontacts' | translate }}</p>
</ion-label>
<ion-toggle [(ngModel)]="contactablePrivacy" (ngModelChange)="saveContactablePrivacy(contactablePrivacy)" slot="end">
</ion-toggle>
<ion-toggle [(ngModel)]="contactablePrivacy" (ngModelChange)="saveContactablePrivacy(contactablePrivacy)" slot="end" />
</ion-item>
<ion-list *ngIf="advancedContactable">
@ -51,19 +50,19 @@
<ion-label>
<p>{{ 'addon.messages.contactableprivacy_onlycontacts' | translate }}</p>
</ion-label>
<ion-radio slot="start" [value]="onlyContactsValue"></ion-radio>
<ion-radio slot="start" [value]="onlyContactsValue" />
</ion-item>
<ion-item class="ion-text-wrap">
<ion-label>
<p>{{ 'addon.messages.contactableprivacy_coursemember' | translate }}</p>
</ion-label>
<ion-radio slot="start" [value]="courseMemberValue"></ion-radio>
<ion-radio slot="start" [value]="courseMemberValue" />
</ion-item>
<ion-item *ngIf="allowSiteMessaging" class="ion-text-wrap">
<ion-label>
<p>{{ 'addon.messages.contactableprivacy_site' | translate }}</p>
</ion-label>
<ion-radio slot="start" [value]="siteValue"></ion-radio>
<ion-radio slot="start" [value]="siteValue" />
</ion-item>
</ion-radio-group>
</ion-list>
@ -72,10 +71,10 @@
<!-- Notifications. -->
<ng-container *ngIf="preferences">
<ng-container *ngIf="!groupMessagingEnabled">
<ng-container *ngTemplateOutlet="legacySettings; context: {preferences: preferences}"></ng-container>
<ng-container *ngTemplateOutlet="legacySettings; context: {preferences: preferences}" />
</ng-container>
<ng-container *ngIf="groupMessagingEnabled">
<ng-container *ngTemplateOutlet="settings; context: {preferences: preferences}"></ng-container>
<ng-container *ngTemplateOutlet="settings; context: {preferences: preferences}" />
</ng-container>
</ng-container>
</core-loading>
@ -109,8 +108,7 @@
<!-- If notifications enabled, show toggle. -->
<core-button-with-spinner *ngIf="!processor.locked" [loading]="notification['updating'+state]" slot="end">
<ion-toggle [(ngModel)]="processor[state].checked"
(ngModelChange)="changePreferenceLegacy(notification, processor, state)">
</ion-toggle>
(ngModelChange)="changePreferenceLegacy(notification, processor, state)" />
</core-button-with-spinner>
<span *ngIf="processor.locked && processor[state].checked" class="text-gray" slot="end">
{{'core.settings.forced' | translate }}
@ -145,8 +143,7 @@
<ng-container *ngIf="!preferences.disableall">
<!-- If notifications enabled, show toggle. -->
<core-button-with-spinner *ngIf="!processor.locked" [loading]="notification.updating" slot="end">
<ion-toggle [(ngModel)]="processor.enabled" (ngModelChange)="changePreference(notification, processor)">
</ion-toggle>
<ion-toggle [(ngModel)]="processor.enabled" (ngModelChange)="changePreference(notification, processor)" />
</core-button-with-spinner>
<span class="text-gray" *ngIf="processor.locked" slot="end">
{{ processor.lockedmessage }}

View File

@ -5,15 +5,14 @@
</ion-title>
<ion-buttons slot="end">
<ion-button fill="clear" (click)="closeModal()" [attr.aria-label]="'core.close' | translate">
<ion-icon slot="icon-only" name="fas-xmark" aria-hidden="true"></ion-icon>
<ion-icon slot="icon-only" name="fas-xmark" aria-hidden="true" />
</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content>
<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" />
<ion-button expand="block" (click)="done($event)">{{ 'core.done' | translate }}</ion-button>
</form>
</ion-content>

View File

@ -9,12 +9,10 @@
</ion-badge>
<p *ngIf="text">
<core-format-text [component]="component" [componentId]="assign.cmid" collapsible-item [text]="text"
contextLevel="module" [contextInstanceId]="assign.cmid" [courseId]="assign.course">
</core-format-text>
contextLevel="module" [contextInstanceId]="assign.cmid" [courseId]="assign.course" />
</p>
<core-file *ngFor="let file of files" [file]="file" [component]="component" [componentId]="assign.cmid"
[alwaysDownload]="true">
</core-file>
[alwaysDownload]="true" />
</ion-label>
</ion-item>
</core-loading>

View File

@ -1,7 +1,7 @@
<!-- Buttons to add to the header. -->
<core-navbar-buttons slot="end">
<ion-button fill="clear" (click)="openModuleSummary()" aria-haspopup="true" [attr.aria-label]="'core.info' | translate">
<ion-icon name="fas-circle-info" slot="icon-only" aria-hidden="true"></ion-icon>
<ion-icon name="fas-circle-info" slot="icon-only" aria-hidden="true" />
</ion-button>
</core-navbar-buttons>
@ -12,8 +12,7 @@
<core-course-module-info [module]="module" [description]="description" [component]="component" [componentId]="componentId"
[courseId]="courseId" [hasDataToSync]="hasOffline" (completionChanged)="onCompletionChange()">
<div description *ngIf="assign && assign.introattachments?.length && !assign.submissionattachments">
<core-file *ngFor="let file of assign.introattachments" [file]="file" [component]="component" [componentId]="componentId">
</core-file>
<core-file *ngFor="let file of assign.introattachments" [file]="file" [component]="component" [componentId]="componentId" />
</div>
</core-course-module-info>
@ -21,8 +20,7 @@
<ng-container *ngIf="assign && canViewAllSubmissions">
<ion-list class="core-list-align-detail-right">
<core-group-selector [groupInfo]="groupInfo" [(selected)]="group" (selectedChange)="setGroup(group)" [courseId]="courseId">
</core-group-selector>
<core-group-selector [groupInfo]="groupInfo" [(selected)]="group" (selectedChange)="setGroup(group)" [courseId]="courseId" />
<ion-item class="ion-text-wrap">
<ion-label>
@ -115,19 +113,17 @@
<!-- Ungrouped users. -->
<ion-card *ngIf="assign.teamsubmission && summary && summary.warnofungroupedusers" class="core-info-card">
<ion-item>
<ion-icon name="fas-circle-question" slot="start" aria-hidden="true"></ion-icon>
<ion-icon name="fas-circle-question" slot="start" aria-hidden="true" />
<ion-label>{{ 'addon.mod_assign.'+summary.warnofungroupedusers | translate }}</ion-label>
</ion-item>
</ion-card>
<div collapsible-footer *ngIf="!showLoading" slot="fixed">
<core-course-module-navigation [courseId]="courseId" [currentModuleId]="module.id">
</core-course-module-navigation>
<core-course-module-navigation [courseId]="courseId" [currentModuleId]="module.id" />
</div>
</ng-container>
<!-- If it's a student, display his submission. -->
<addon-mod-assign-submission *ngIf="!showLoading && !canViewAllSubmissions && canViewOwnSubmission" [courseId]="courseId"
[moduleId]="module.id">
</addon-mod-assign-submission>
[moduleId]="module.id" />
</core-loading>

View File

@ -9,12 +9,10 @@
</ion-badge>
<p *ngIf="text">
<core-format-text [component]="component" [componentId]="assign.cmid" collapsible-item [text]="text"
contextLevel="module" [contextInstanceId]="assign.cmid" [courseId]="assign.course">
</core-format-text>
contextLevel="module" [contextInstanceId]="assign.cmid" [courseId]="assign.course" />
</p>
<core-file *ngFor="let file of files" [file]="file" [component]="component" [componentId]="assign.cmid"
[alwaysDownload]="true">
</core-file>
[alwaysDownload]="true" />
</ion-label>
</ion-item>
</core-loading>

View File

@ -3,7 +3,7 @@
<!-- Time limit is over. -->
<ion-card *ngIf="timeLimitFinished && (canEdit || canSubmit)" class="core-danger-card">
<ion-item class="ion-text-wrap">
<ion-icon name="fas-triangle-exclamation" slot="start" aria-hidden="true"></ion-icon>
<ion-icon name="fas-triangle-exclamation" slot="start" aria-hidden="true" />
<ion-label>
<p>{{ 'addon.mod_assign.caneditsubmission' | translate }}</p>
</ion-label>
@ -13,10 +13,10 @@
<!-- User and status of the submission. -->
<ion-item class="ion-text-wrap" *ngIf="!blindMarking && user" core-user-link [userId]="submitId" [courseId]="courseId"
[attr.aria-label]="user!.fullname">
<core-user-avatar [user]="user" slot="start" [linkProfile]="false"></core-user-avatar>
<core-user-avatar [user]="user" slot="start" [linkProfile]="false" />
<ion-label>
<p class="item-heading">{{ user!.fullname }}</p>
<ng-container *ngTemplateOutlet="submissionStatus"></ng-container>
<ng-container *ngTemplateOutlet="submissionStatus" />
</ion-label>
</ion-item>
@ -24,7 +24,7 @@
<ion-item class="ion-text-wrap" *ngIf="blindMarking && !user">
<ion-label>
<p class="item-heading">{{ 'addon.mod_assign.hiddenuser' | translate }} {{blindId}}</p>
<ng-container *ngTemplateOutlet="submissionStatus"></ng-container>
<ng-container *ngTemplateOutlet="submissionStatus" />
</ion-label>
</ion-item>
@ -32,7 +32,7 @@
<ion-item class="ion-text-wrap" *ngIf="(blindMarking && user) || (!blindMarking && !user)">
<ion-label>
<p class="item-heading">{{ 'addon.mod_assign.submissionstatus' | translate }}</p>
<ng-container *ngTemplateOutlet="submissionStatus"></ng-container>
<ng-container *ngTemplateOutlet="submissionStatus" />
</ion-label>
</ion-item>
@ -103,8 +103,7 @@
<p class="item-heading">{{ 'addon.mod_assign.timeremaining' | translate }}</p>
<p *ngIf="!timeLimitEndTime" [innerHTML]="timeRemaining"></p>
<core-timer *ngIf="timeLimitEndTime > 0" [endTime]="timeLimitEndTime" mode="basic" timeUpText="00:00:00"
[timeLeftClassThreshold]="-1" [underTimeClassThresholds]="[300, 900]" (finished)="timeUp()">
</core-timer>
[timeLeftClassThreshold]="-1" [underTimeClassThresholds]="[300, 900]" (finished)="timeUp()" />
</ion-label>
</ion-item>
@ -136,8 +135,7 @@
</ion-item>
<addon-mod-assign-submission-plugin *ngFor="let plugin of submissionPlugins" [assign]="assign"
[submission]="userSubmission" [plugin]="plugin">
</addon-mod-assign-submission-plugin>
[submission]="userSubmission" [plugin]="plugin" />
<!-- Team members that need to submit it too. -->
<ion-item-divider class="ion-text-wrap" *ngIf="membersToSubmit && membersToSubmit.length > 0">
@ -149,7 +147,7 @@
<ng-container *ngFor="let user of membersToSubmit">
<ion-item class="ion-text-wrap" core-user-link [userId]="user.id" [courseId]="courseId"
[attr.aria-label]="user.fullname">
<core-user-avatar [user]="user" slot="start" [linkProfile]="false"></core-user-avatar>
<core-user-avatar [user]="user" slot="start" [linkProfile]="false" />
<ion-label>
<p class="item-heading">{{ user.fullname }}</p>
</ion-label>
@ -213,7 +211,7 @@
<ion-button expand="block" *ngIf="submissionUrl" [href]="submissionUrl" core-link
[showBrowserWarning]="false">
{{ 'core.openinbrowser' | translate }}
<ion-icon name="fas-up-right-from-square" slot="end" aria-hidden="true"></ion-icon>
<ion-icon name="fas-up-right-from-square" slot="end" aria-hidden="true" />
</ion-button>
</ng-container>
@ -229,10 +227,9 @@
<ng-container *ngIf="canSubmit">
<ion-item class="ion-text-wrap" *ngIf="submissionStatement">
<ion-label>
<core-format-text [text]="submissionStatement" [filter]="false"></core-format-text>
<core-format-text [text]="submissionStatement" [filter]="false" />
</ion-label>
<ion-checkbox slot="end" name="submissionstatement" [(ngModel)]="acceptStatement">
</ion-checkbox>
<ion-checkbox slot="end" name="submissionstatement" [(ngModel)]="acceptStatement" />
</ion-item>
<!-- Submit button. -->
<ion-item class="ion-text-wrap" *ngIf="!showErrorStatementSubmit">
@ -253,8 +250,7 @@
</ion-item>
</ng-container>
</div>
<core-course-module-navigation [courseId]="courseId" [currentModuleId]="moduleId">
</core-course-module-navigation>
<core-course-module-navigation [courseId]="courseId" [currentModuleId]="moduleId" />
</div>
</ng-template>
</core-tab>
@ -268,12 +264,12 @@
<ion-label>
<p class="item-heading">{{ 'addon.mod_assign.currentgrade' | translate }}</p>
<p>
<core-format-text [text]="feedback!.gradefordisplay" [filter]="false"></core-format-text>
<core-format-text [text]="feedback!.gradefordisplay" [filter]="false" />
</p>
</ion-label>
<ion-button slot="end" *ngIf="feedback!.advancedgrade" (click)="showAdvancedGrade()"
[attr.aria-label]="'core.showadvanced' |translate">
<ion-icon name="fas-magnifying-glass" slot="icon-only" aria-hidden="true"></ion-icon>
<ion-icon name="fas-magnifying-glass" slot="icon-only" aria-hidden="true" />
</ion-button>
</ion-item>
@ -285,8 +281,7 @@
<p class="item-heading">{{ 'addon.mod_assign.gradeoutof' | translate: {$a: gradeInfo!.grade} }}</p>
</ion-label>
<ion-input *ngIf="!grade.disabled" type="text" [(ngModel)]="grade.grade" min="0" [max]="gradeInfo!.grade"
[lang]="grade.lang">
</ion-input>
[lang]="grade.lang" />
<p *ngIf="grade.disabled">{{ 'addon.mod_assign.gradelocked' | translate }}</p>
</ion-item>
@ -345,8 +340,7 @@
<ng-container *ngIf="feedback">
<addon-mod-assign-feedback-plugin *ngFor="let plugin of feedback.plugins" [assign]="assign"
[submission]="userSubmission" [userId]="submitId" [plugin]="plugin" [canEdit]="canSaveGrades">
</addon-mod-assign-feedback-plugin>
[submission]="userSubmission" [userId]="submitId" [plugin]="plugin" [canEdit]="canSaveGrades" />
</ng-container>
<!-- Workflow status. -->
@ -363,7 +357,7 @@
<p class="item-heading">{{ 'addon.mod_assign.groupsubmissionsettings' | translate }}</p>
<p>{{ 'addon.mod_assign.applytoteam' | translate }}</p>
</ion-label>
<ion-toggle [(ngModel)]="grade.applyToAll" slot="end"></ion-toggle>
<ion-toggle [(ngModel)]="grade.applyToAll" slot="end" />
</ion-item>
<!-- Attempt status. -->
@ -387,14 +381,14 @@
</ion-item>
<ion-item *ngIf="canSaveGrades && allowAddAttempt">
<ion-label>{{ 'addon.mod_assign.addattempt' | translate }}</ion-label>
<ion-toggle [(ngModel)]="grade.addAttempt" slot="end"></ion-toggle>
<ion-toggle [(ngModel)]="grade.addAttempt" slot="end" />
</ion-item>
</ng-container>
<!-- Data about the grader (teacher who graded). -->
<ion-item class="ion-text-wrap" *ngIf="grader" core-user-link [userId]="grader!.id" [courseId]="courseId"
[attr.aria-label]="grader!.fullname" [detail]="true">
<core-user-avatar [user]="grader" slot="start" [linkProfile]="false"></core-user-avatar>
<core-user-avatar [user]="grader" slot="start" [linkProfile]="false" />
<ion-label>
<p class="item-heading">{{ 'addon.mod_assign.gradedby' | translate }}</p>
<p class="item-heading">{{ grader!.fullname }}</p>
@ -413,12 +407,12 @@
<!-- Warning message if cannot save grades. -->
<ion-card *ngIf="isGrading && !canSaveGrades" class="core-warning-card">
<ion-item>
<ion-icon name="fas-triangle-exclamation" slot="start" aria-hidden="true"></ion-icon>
<ion-icon name="fas-triangle-exclamation" slot="start" aria-hidden="true" />
<ion-label>
<p>{{ 'addon.mod_assign.cannotgradefromapp' | translate }}</p>
<ion-button expand="block" *ngIf="gradeUrl" [href]="gradeUrl" core-link [showBrowserWarning]="false">
{{ 'core.openinbrowser' | translate }}
<ion-icon name="fas-up-right-from-square" slot="end" aria-hidden="true"></ion-icon>
<ion-icon name="fas-up-right-from-square" slot="end" aria-hidden="true" />
</ion-button>
</ion-label>
</ion-item>
@ -434,8 +428,7 @@
<ng-container *ngIf="assign && assign!.teamsubmission && lastAttempt">
<p *ngIf="lastAttempt.submissiongroup && lastAttempt.submissiongroupname" class="core-groupname">
<core-format-text [text]="lastAttempt.submissiongroupname" contextLevel="course" [contextInstanceId]="courseId"
[wsNotFiltered]="true">
</core-format-text>
[wsNotFiltered]="true" />
</p>
<ng-container *ngIf="assign!.preventsubmissionnotingroup &&
!lastAttempt!.submissiongroup &&

View File

@ -4,18 +4,17 @@
<h2>{{ plugin.name }}</h2>
<p>
<core-format-text [component]="component" [componentId]="assign.cmid" collapsible-item [text]="text" contextLevel="module"
[contextInstanceId]="assign.cmid" [courseId]="assign.course">
</core-format-text>
[contextInstanceId]="assign.cmid" [courseId]="assign.course" />
</p>
</ion-label>
<div slot="end">
<div class="ion-text-end">
<ion-button fill="clear" *ngIf="canEdit" (click)="editComment()" [attr.aria-label]="'core.edit' | translate">
<ion-icon name="fas-pen" slot="icon-only" aria-hidden="true"></ion-icon>
<ion-icon name="fas-pen" slot="icon-only" aria-hidden="true" />
</ion-button>
</div>
<ion-note *ngIf="!isSent" color="dark">
<ion-icon name="fas-clock" aria-hidden="true"></ion-icon> {{ 'core.notsent' | translate }}
<ion-icon name="fas-clock" aria-hidden="true" /> {{ 'core.notsent' | translate }}
</ion-note>
</div>
</ion-item>
@ -25,6 +24,5 @@
<ion-label class="sr-only">{{ plugin.name }}</ion-label>
<core-rich-text-editor [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>
elementId="assignfeedbackcomments_editor" [draftExtraParams]="{userid: userId, action: 'grade'}" />
</ion-item>

View File

@ -3,8 +3,8 @@
<ion-label>
<h2>{{plugin.name}}</h2>
<ng-container>
<core-file *ngFor="let file of files" [file]="file" [component]="component" [componentId]="assign.cmid" [alwaysDownload]="true">
</core-file>
<core-file *ngFor="let file of files" [file]="file" [component]="component" [componentId]="assign.cmid"
[alwaysDownload]="true" />
</ng-container>
</ion-label>
</ion-item>

View File

@ -3,8 +3,8 @@
<ion-label>
<h2>{{plugin.name}}</h2>
<ng-container>
<core-file *ngFor="let file of files" [file]="file" [component]="component" [componentId]="assign.cmid" [alwaysDownload]="true">
</core-file>
<core-file *ngFor="let file of files" [file]="file" [component]="component" [componentId]="assign.cmid"
[alwaysDownload]="true" />
</ng-container>
</ion-label>
</ion-item>

View File

@ -1,12 +1,11 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
<ion-back-button [text]="'core.back' | translate" />
</ion-buttons>
<ion-title>
<h1>
<core-format-text [text]="title" contextLevel="module" [contextInstanceId]="moduleId" [courseId]="courseId">
</core-format-text>
<core-format-text [text]="title" contextLevel="module" [contextInstanceId]="moduleId" [courseId]="courseId" />
</h1>
</ion-title>
<ion-buttons slot="end">
@ -22,38 +21,33 @@
<!-- @todo plagiarism_print_disclosure -->
<core-timer *ngIf="timeLimitEndTime > 0" [endTime]="timeLimitEndTime" (finished)="timeUp()" timeUpText="00:00:00"
[timerText]="'addon.mod_assign.assigntimeleft' | translate" [align]="'center'" [timeLeftClassThreshold]="-1"
[underTimeClassThresholds]="[300, 900]">
</core-timer>
[underTimeClassThresholds]="[300, 900]" />
<!-- Assign activity instructions and attachments if needed. -->
<ion-item class="ion-text-wrap" *ngIf="activityInstructions">
<ion-label>
<core-format-text [text]="activityInstructions" [component]="component" [componentId]="moduleId" contextLevel="module"
[contextInstanceId]="moduleId" [courseId]="courseId">
</core-format-text>
[contextInstanceId]="moduleId" [courseId]="courseId" />
</ion-label>
</ion-item>
<ng-container *ngIf="assign?.submissionattachments">
<core-file *ngFor="let file of introAttachments" [file]="file" [component]="component" [componentId]="moduleId">
</core-file>
<core-file *ngFor="let file of introAttachments" [file]="file" [component]="component" [componentId]="moduleId" />
</ng-container>
<form name="addon-mod_assign-edit-form" #editSubmissionForm>
<!-- Submission statement. -->
<ion-item class="ion-text-wrap" *ngIf="submissionStatement">
<ion-label>
<core-format-text [text]="submissionStatement" [filter]="false">
</core-format-text>
<core-format-text [text]="submissionStatement" [filter]="false" />
</ion-label>
<ion-checkbox slot="end" name="submissionstatement" [(ngModel)]="submissionStatementAccepted"></ion-checkbox>
<ion-checkbox slot="end" name="submissionstatement" [(ngModel)]="submissionStatementAccepted" />
<!-- ion-checkbox doesn't use an input. Create a hidden input to hold the value. -->
<input type="hidden" [ngModel]="submissionStatementAccepted" name="submissionstatement">
</ion-item>
<addon-mod-assign-submission-plugin *ngFor="let plugin of userSubmission.plugins" [assign]="assign"
[submission]="userSubmission" [plugin]="plugin" [edit]="true" [allowOffline]="allowOffline">
</addon-mod-assign-submission-plugin>
[submission]="userSubmission" [plugin]="plugin" [edit]="true" [allowOffline]="allowOffline" />
</form>
</ion-list>
</core-loading>

View File

@ -1,24 +1,21 @@
<ion-header collapsible>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
<ion-back-button [text]="'core.back' | translate" />
</ion-buttons>
<ion-title>
<h1>
<core-format-text [text]="title" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
</core-format-text>
<core-format-text [text]="title" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId" />
</h1>
</ion-title>
<ion-buttons slot="end">
<!-- The buttons defined by the component will be added in here. -->
</ion-buttons>
<ion-buttons slot="end" />
</ion-toolbar>
</ion-header>
<ion-content class="limited-width">
<ion-refresher slot="fixed" [disabled]="activityComponent?.showLoading" (ionRefresh)="activityComponent?.doRefresh($event.target)">
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}" />
</ion-refresher>
<addon-mod-assign-index [module]="module" [courseId]="courseId" (dataRetrieved)="updateData($event)"></addon-mod-assign-index>
<addon-mod-assign-index [module]="module" [courseId]="courseId" (dataRetrieved)="updateData($event)" />
</ion-content>

View File

@ -1,35 +1,33 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
<ion-back-button [text]="'core.back' | translate" />
</ion-buttons>
<ion-title>
<h1>
<core-format-text [text]="title" contextLevel="module" [contextInstanceId]="moduleId" [courseId]="courseId">
</core-format-text>
<core-format-text [text]="title" contextLevel="module" [contextInstanceId]="moduleId" [courseId]="courseId" />
</h1>
</ion-title>
<ion-buttons slot="end"></ion-buttons>
<ion-buttons slot="end" />
</ion-toolbar>
</ion-header>
<ion-content>
<core-split-view>
<ion-refresher slot="fixed" [disabled]="!submissions.loaded" (ionRefresh)="refreshList($event.target)">
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}" />
</ion-refresher>
<core-loading [hideUntil]="submissions.loaded">
<ion-list>
<core-group-selector [groupInfo]="groupInfo" [(selected)]="groupId" (selectedChange)="reloadSubmissions()"
[courseId]="courseId">
</core-group-selector>
[courseId]="courseId" />
<!-- List of submissions. -->
<ng-container *ngFor="let submission of submissions.items">
<ion-item class="ion-text-wrap" (click)="submissions.select(submission)" button
[attr.aria-current]="submissions.getItemAriaCurrent(submission)" [detail]="true">
<core-user-avatar [user]="submission" [linkProfile]="false" slot="start"></core-user-avatar>
<core-user-avatar [user]="submission" [linkProfile]="false" slot="start" />
<ion-label>
<p class="item-heading" *ngIf="submission.userfullname">{{submission.userfullname}}</p>
<p class="item-heading" *ngIf="!submission.userfullname">
@ -37,8 +35,7 @@
</p>
<p *ngIf="assign && assign!.teamsubmission">
<span *ngIf="submission.groupname" class="core-groupname">
<core-format-text [text]="submission.groupname" contextLevel="course" [contextInstanceId]="courseId">
</core-format-text>
<core-format-text [text]="submission.groupname" contextLevel="course" [contextInstanceId]="courseId" />
</span>
<span *ngIf="assign!.preventsubmissionnotingroup && !submission.groupname && submission.noGroups
&& !submission.blindid" class="text-danger">
@ -68,8 +65,7 @@
</ion-list>
<core-empty-box *ngIf="!submissions || submissions.empty" icon="fas-file-signature"
[message]="'addon.mod_assign.submissionstatus_' | translate">
</core-empty-box>
[message]="'addon.mod_assign.submissionstatus_' | translate" />
</core-loading>
</core-split-view>
</ion-content>

View File

@ -1,16 +1,15 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
<ion-back-button [text]="'core.back' | translate" />
</ion-buttons>
<ion-title>
<h1>
<core-format-text [text]="title" contextLevel="module" [contextInstanceId]="moduleId" [courseId]="courseId">
</core-format-text>
<core-format-text [text]="title" contextLevel="module" [contextInstanceId]="moduleId" [courseId]="courseId" />
</h1>
</ion-title>
<ion-buttons slot="end"></ion-buttons>
<ion-buttons slot="end" />
</ion-toolbar>
<core-navbar-buttons slot="end">
@ -21,10 +20,9 @@
</ion-header>
<ion-content [core-swipe-navigation]="submissions">
<ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="refreshSubmission($event.target)">
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}" />
</ion-refresher>
<core-loading [hideUntil]="loaded">
<addon-mod-assign-submission *ngIf="loaded" [courseId]="courseId" [moduleId]="moduleId" [submitId]="submitId" [blindId]="blindId">
</addon-mod-assign-submission>
<addon-mod-assign-submission *ngIf="loaded" [courseId]="courseId" [moduleId]="moduleId" [submitId]="submitId" [blindId]="blindId" />
</core-loading>
</ion-content>

View File

@ -15,7 +15,7 @@
import { Injectable } from '@angular/core';
import { CoreFileUploader, CoreFileUploaderStoreFilesResult } from '@features/fileuploader/services/fileuploader';
import { CoreSites, CoreSitesCommonWSOptions } from '@services/sites';
import { FileEntry, DirectoryEntry } from '@ionic-native/file/ngx';
import { FileEntry, DirectoryEntry } from '@awesome-cordova-plugins/file/ngx';
import {
AddonModAssignProvider,
AddonModAssignAssign,

View File

@ -2,7 +2,6 @@
<ion-label>
<h2>{{plugin.name}}</h2>
<core-comments contextLevel="module" [instanceId]="assign.cmid" component="assignsubmission_comments" [itemId]="submission.id"
area="submission_comments" [title]="plugin.name" [courseId]="assign.course">
</core-comments>
area="submission_comments" [title]="plugin.name" [courseId]="assign.course" />
</ion-label>
</ion-item>

View File

@ -3,7 +3,7 @@
<ion-label>
<h2>{{ plugin.name }}</h2>
<div>
<core-files [files]="files" [component]="component" [componentId]="assign.cmid" [alwaysDownload]="true"></core-files>
<core-files [files]="files" [component]="component" [componentId]="assign.cmid" [alwaysDownload]="true" />
</div>
</ion-label>
</ion-item>
@ -16,6 +16,5 @@
</ion-label>
</ion-item-divider>
<core-attachments [files]="files" [maxSize]="maxSize" [maxSubmissions]="maxSubmissions" [courseId]="assign.course"
[component]="component" [componentId]="assign.cmid" [acceptedTypes]="acceptedTypes" [allowOffline]="allowOffline">
</core-attachments>
[component]="component" [componentId]="assign.cmid" [acceptedTypes]="acceptedTypes" [allowOffline]="allowOffline" />
</div>

View File

@ -20,7 +20,7 @@ import { CoreFileUploaderStoreFilesResult } from '@features/fileuploader/service
import { CoreFileSession } from '@services/file-session';
import { CoreUtils } from '@services/utils/utils';
import { AddonModAssignSubmissionFileHandlerService } from '../services/handler';
import { FileEntry } from '@ionic-native/file/ngx';
import { FileEntry } from '@awesome-cordova-plugins/file/ngx';
import { AddonModAssignSubmissionPluginBaseComponent } from '@addons/mod/assign/classes/base-submission-plugin-component';
import { CoreFileEntry } from '@services/file-helper';

View File

@ -30,7 +30,7 @@ import { CoreUtils } from '@services/utils/utils';
import { CoreWSFile } from '@services/ws';
import { makeSingleton } from '@singletons';
import { AddonModAssignSubmissionFileComponent } from '../component/file';
import { FileEntry } from '@ionic-native/file/ngx';
import { FileEntry } from '@awesome-cordova-plugins/file/ngx';
import type { AddonModAssignSubmissionPluginBaseComponent } from '@addons/mod/assign/classes/base-submission-plugin-component';
/**

View File

@ -5,8 +5,7 @@
<p *ngIf="words">{{ 'addon.mod_assign.numwords' | translate: {'$a': words} }}</p>
<p>
<core-format-text [component]="component" [componentId]="assign.cmid" collapsible-item [text]="text" contextLevel="module"
[contextInstanceId]="assign.cmid" [courseId]="assign.course">
</core-format-text>
[contextInstanceId]="assign.cmid" [courseId]="assign.course" />
</p>
</ion-label>
</ion-item>
@ -29,7 +28,6 @@
<core-rich-text-editor [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>
[draftExtraParams]="{userid: currentUserId, action: 'editsubmission'}" />
</ion-item>
</div>

View File

@ -1,7 +1,7 @@
<!-- Buttons to add to the header. -->
<core-navbar-buttons slot="end">
<ion-button fill="clear" (click)="openModuleSummary()" aria-haspopup="true" [attr.aria-label]="'core.info' | translate">
<ion-icon name="fas-circle-info" slot="icon-only" aria-hidden="true"></ion-icon>
<ion-icon name="fas-circle-info" slot="icon-only" aria-hidden="true" />
</ion-button>
</core-navbar-buttons>
@ -10,12 +10,10 @@
<!-- Activity info. -->
<core-course-module-info [module]="module" [description]="description" [component]="component" [componentId]="componentId"
[courseId]="courseId" (completionChanged)="onCompletionChange()">
</core-course-module-info>
[courseId]="courseId" (completionChanged)="onCompletionChange()" />
<core-group-selector [groupInfo]="groupInfo" [(selected)]="groupId" (selectedChange)="groupChanged()"
[multipleGroupsMessage]="'addon.mod_bigbluebuttonbn.view_groups_selection_warning' | translate" [courseId]="module.course">
</core-group-selector>
[multipleGroupsMessage]="'addon.mod_bigbluebuttonbn.view_groups_selection_warning' | translate" [courseId]="module.course" />
<ng-container *ngIf="meetingInfo && showRoom">
<ion-item class="ion-text-wrap" *ngIf="meetingInfo.openingtime">
@ -78,7 +76,7 @@
<ion-card *ngIf="!meetingInfo.canjoin" class="core-warning-card">
<ion-item class="ion-text-wrap">
<ion-icon name="fas-triangle-exclamation" slot="start" aria-hidden="true"></ion-icon>
<ion-icon name="fas-triangle-exclamation" slot="start" aria-hidden="true" />
<ion-label>{{ meetingInfo.statusmessage }}</ion-label>
</ion-item>
</ion-card>
@ -94,8 +92,7 @@
<ion-item button class="addon-mod_bbb-recording-title" [attr.aria-expanded]="recording.expanded" (click)="toggle(recording)"
[attr.aria-label]="(recording.expanded ? 'core.collapse' : 'core.expand') | translate" [detail]="false">
<ion-icon name="fas-chevron-right" flip-rtl slot="start" aria-hidden="true" class="expandable-status-icon"
[class.expandable-status-icon-expanded]="recording.expanded">
</ion-icon>
[class.expandable-status-icon-expanded]="recording.expanded" />
<ion-label>
<p>{{ recording.type }}</p>
<p>{{ recording.name }}</p>
@ -113,7 +110,7 @@
<ion-label>
<p>{{ playback.name }}</p>
</ion-label>
<ion-icon slot="end" [name]="playback.icon" aria-hidden="true"></ion-icon>
<ion-icon slot="end" [name]="playback.icon" aria-hidden="true" />
</ion-item>
</div>
<ion-item *ngFor="let data of recording.details" class="ion-text-wrap">
@ -121,7 +118,7 @@
<p class="item-heading">{{ data.label }}</p>
<p *ngIf="data.allowHTML">
<core-format-text [text]="data.value" [component]="component" [componentId]="module.id" contextLevel="module"
[contextInstanceId]="module.id" [courseId]="module.course"></core-format-text>
[contextInstanceId]="module.id" [courseId]="module.course" />
</p>
<p *ngIf="!data.allowHTML">{{ data.value }}</p>
</ion-label>
@ -130,8 +127,7 @@
</ng-container>
<core-empty-box *ngIf="recordings && !recordings.length" icon="far-file-video"
[message]="'addon.mod_bigbluebuttonbn.view_message_norecordings' | translate">
</core-empty-box>
[message]="'addon.mod_bigbluebuttonbn.view_message_norecordings' | translate" />
</ng-container>
<div collapsible-footer *ngIf="!showLoading" slot="fixed">
@ -146,7 +142,6 @@
</ion-button>
</div>
<core-course-module-navigation [courseId]="courseId" [currentModuleId]="module.id">
</core-course-module-navigation>
<core-course-module-navigation [courseId]="courseId" [currentModuleId]="module.id" />
</div>
</core-loading>

View File

@ -1,23 +1,20 @@
<ion-header collapsible>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
<ion-back-button [text]="'core.back' | translate" />
</ion-buttons>
<ion-title>
<h1>
<core-format-text [text]="title" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
</core-format-text>
<core-format-text [text]="title" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId" />
</h1>
</ion-title>
<ion-buttons slot="end">
<!-- The buttons defined by the component will be added in here. -->
</ion-buttons>
<ion-buttons slot="end" />
</ion-toolbar>
</ion-header>
<ion-content class="limited-width">
<ion-refresher slot="fixed" [disabled]="activityComponent?.showLoading" (ionRefresh)="activityComponent?.doRefresh($event.target)">
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}" />
</ion-refresher>
<addon-mod-bbb-index [module]="module" [courseId]="courseId" (dataRetrieved)="updateData($event)"></addon-mod-bbb-index>
<addon-mod-bbb-index [module]="module" [courseId]="courseId" (dataRetrieved)="updateData($event)" />
</ion-content>

View File

@ -1,7 +1,7 @@
<!-- Buttons to add to the header. -->
<core-navbar-buttons slot="end">
<ion-button fill="clear" (click)="openModuleSummary()" aria-haspopup="true" [attr.aria-label]="'core.info' | translate">
<ion-icon name="fas-circle-info" slot="icon-only" aria-hidden="true"></ion-icon>
<ion-icon name="fas-circle-info" slot="icon-only" aria-hidden="true" />
</ion-button>
</core-navbar-buttons>
@ -10,8 +10,7 @@
<!-- Activity info. -->
<core-course-module-info [module]="module" [description]="description" [component]="component" [componentId]="componentId"
[courseId]="courseId" (completionChanged)="onCompletionChange()">
</core-course-module-info>
[courseId]="courseId" (completionChanged)="onCompletionChange()" />
<ion-list>
<ion-item>
@ -26,8 +25,7 @@
<p [class.ion-padding-start]="addPadding && chapter.level === 1 ? true : null">
<span *ngIf="showNumbers" class="addon-mod-book-number">{{chapter.indexNumber}} </span>
<span *ngIf="showBullets" class="addon-mod-book-bullet">&bull; </span>
<core-format-text [text]="chapter.title" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
</core-format-text>
<core-format-text [text]="chapter.title" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId" />
</p>
</ion-label>
</ion-item>
@ -41,7 +39,6 @@
</ion-button>
</div>
<core-course-module-navigation [courseId]="courseId" [currentModuleId]="module.id">
</core-course-module-navigation>
<core-course-module-navigation [courseId]="courseId" [currentModuleId]="module.id" />
</div>
</core-loading>

View File

@ -5,7 +5,7 @@
</ion-title>
<ion-buttons slot="end">
<ion-button fill="clear" (click)="closeModal()" [attr.aria-label]="'core.close' | translate">
<ion-icon name="fas-xmark" slot="icon-only" aria-hidden=true></ion-icon>
<ion-icon name="fas-xmark" slot="icon-only" aria-hidden=true />
</ion-button>
</ion-buttons>
</ion-toolbar>
@ -20,8 +20,8 @@
<p [class.ion-padding-start]="addPadding && chapter.level === 1 ? true : null" class="item-heading">
<span *ngIf="showNumbers" class="addon-mod-book-number">{{chapter.indexNumber}} </span>
<span *ngIf="showBullets" class="addon-mod-book-bullet">&bull; </span>
<core-format-text [text]="chapter.title" contextLevel="module" [contextInstanceId]="moduleId" [courseId]="courseId">
</core-format-text>
<core-format-text [text]="chapter.title" contextLevel="module" [contextInstanceId]="moduleId"
[courseId]="courseId" />
</p>
</ion-label>
</ion-item>

View File

@ -1,43 +1,42 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
<ion-back-button [text]="'core.back' | translate" />
</ion-buttons>
<ion-title>
<h1>
<core-format-text [text]="title" contextLevel="module" [contextInstanceId]="cmId" [courseId]="courseId">
</core-format-text>
<core-format-text [text]="title" contextLevel="module" [contextInstanceId]="cmId" [courseId]="courseId" />
</h1>
</ion-title>
<ion-buttons slot="end">
<ion-button (click)="showToc()" [attr.aria-label]="'addon.mod_book.toc' | translate" aria-haspopup="true" *ngIf="loaded">
<ion-icon name="fas-bookmark" slot="icon-only" aria-hidden="true"></ion-icon>
<ion-icon name="fas-bookmark" slot="icon-only" aria-hidden="true" />
</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="doRefresh($event.target)">
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}" />
</ion-refresher>
<core-loading [hideUntil]="loaded">
<div class="safe-area-padding-horizontal core-swipe-slides-container">
<ion-card class="core-warning-card" *ngIf="warning">
<ion-item>
<ion-icon name="fas-triangle-exclamation" slot="start" aria-hidden="true"></ion-icon>
<ion-icon name="fas-triangle-exclamation" slot="start" aria-hidden="true" />
<ion-label><span [innerHTML]="warning"></span></ion-label>
</ion-item>
</ion-card>
<core-swipe-slides [manager]="manager" [options]="slidesOpts">
<core-swipe-slides [manager]="manager" [options]="swiperOpts">
<ng-template let-chapter="item" let-active="active">
<div class="ion-padding">
<core-format-text [component]="component" [componentId]="cmId" [text]="chapter.content" contextLevel="module"
[contextInstanceId]="cmId" [courseId]="courseId" [disabled]="!active"></core-format-text>
[contextInstanceId]="cmId" [courseId]="courseId" [disabled]="!active" />
<div class="ion-margin-top" *ngIf="chapter.tags?.length > 0">
<strong>{{ 'core.tag.tags' | translate }}: </strong>
<core-tag-list [tags]="chapter.tags"></core-tag-list>
<core-tag-list [tags]="chapter.tags" />
</div>
</div>
</ng-template>
@ -47,6 +46,5 @@
<core-navigation-bar collapsible-footer appearOnBottom *ngIf="loaded && displayNavBar && navigationItems.length > 1"
[items]="navigationItems" previousTranslate="addon.mod_book.navprevtitle" nextTranslate="addon.mod_book.navnexttitle"
(action)="changeChapter($event.id)" slot="fixed">
</core-navigation-bar>
(action)="changeChapter($event.id)" slot="fixed" />
</ion-content>

View File

@ -41,6 +41,7 @@ import {
} from '../../services/book';
import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics';
import { CoreUrlUtils } from '@services/utils/url';
import { IonicSlides } from '@ionic/angular';
/**
* Page that displays a book contents.
@ -52,7 +53,7 @@ import { CoreUrlUtils } from '@services/utils/url';
})
export class AddonModBookContentsPage implements OnInit, OnDestroy {
@ViewChild(CoreSwipeSlidesComponent) slides?: CoreSwipeSlidesComponent;
@ViewChild(CoreSwipeSlidesComponent) swipeSlidesComponent?: CoreSwipeSlidesComponent;
title = '';
cmId!: number;
@ -63,7 +64,8 @@ export class AddonModBookContentsPage implements OnInit, OnDestroy {
warning = '';
displayNavBar = true;
navigationItems: CoreNavigationBarItem<AddonModBookTocChapter>[] = [];
slidesOpts: CoreSwipeSlidesOptions = {
swiperOpts: CoreSwipeSlidesOptions = {
modules: [IonicSlides],
autoHeight: true,
observer: true,
observeParents: true,
@ -222,7 +224,7 @@ export class AddonModBookContentsPage implements OnInit, OnDestroy {
return;
}
this.slides?.slideToItem({ id: chapterId });
this.swipeSlidesComponent?.slideToItem({ id: chapterId });
}
/**

View File

@ -1,24 +1,20 @@
<ion-header collapsible>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
<ion-back-button [text]="'core.back' | translate" />
</ion-buttons>
<ion-title>
<h1>
<core-format-text [text]="title" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
</core-format-text>
<core-format-text [text]="title" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId" />
</h1>
</ion-title>
<ion-buttons slot="end">
<!-- The buttons defined by the component will be added in here. -->
</ion-buttons>
<ion-buttons slot="end" />
</ion-toolbar>
</ion-header>
<ion-content class="limited-width">
<ion-refresher slot="fixed" [disabled]="activityComponent?.showLoading" (ionRefresh)="activityComponent?.doRefresh($event.target)">
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}" />
</ion-refresher>
<addon-mod-book-index [module]="module" [courseId]="courseId" (dataRetrieved)="updateData($event)">
</addon-mod-book-index>
<addon-mod-book-index [module]="module" [courseId]="courseId" (dataRetrieved)="updateData($event)" />
</ion-content>

View File

@ -134,22 +134,22 @@ Feature: Test basic usage of book activity in app
But I should not find "This is the first chapter" in the app
# Navigate using swipe.
When I swipe to the left in "Chapt 3" "ion-slides" in the app
When I swipe to the left in "Chapt 3" "swiper-container" in the app
Then I should find "Chapt 3" in the app
And I should find "This is the third chapter" in the app
And I should find "4 / 4" in the app
When I swipe to the right in "Chapt 3" "ion-slides" in the app
When I swipe to the right in "Chapt 3" "swiper-container" in the app
Then I should find "Chapt 2" in the app
And I should find "This is the second chapter" in the app
And I should find "3 / 4" in the app
When I swipe to the right in "Chapt 2" "ion-slides" in the app
When I swipe to the right in "Chapt 2" "swiper-container" in the app
Then I should find "Chapt 1.1" in the app
And I should find "This is a subchapter" in the app
And I should find "2 / 4" in the app
When I swipe to the left in "Chapt 1.1" "ion-slides" in the app
When I swipe to the left in "Chapt 1.1" "swiper-container" in the app
Then I should find "Chapt 2" in the app
And I should find "This is the second chapter" in the app
And I should find "3 / 4" in the app
@ -208,22 +208,22 @@ Scenario: View and navigate book contents (teacher)
But I should not find "This is the first chapter" in the app
# Navigate using swipe.
When I swipe to the left in "Hidden subchapter" "ion-slides" in the app
When I swipe to the left in "Hidden subchapter" "swiper-container" in the app
Then I should find "Chapt 3" in the app
And I should find "This is the third chapter" in the app
And I should find "6 / 7" in the app
When I swipe to the left in "Chapt 3" "ion-slides" in the app
When I swipe to the left in "Chapt 3" "swiper-container" in the app
Then I should find "Last hidden" in the app
And I should find "Another hidden subchapter" in the app
And I should find "7 / 7" in the app
When I swipe to the left in "Last hidden" "ion-slides" in the app
When I swipe to the left in "Last hidden" "swiper-container" in the app
Then I should find "Last hidden" in the app
And I should find "Another hidden subchapter" in the app
And I should find "7 / 7" in the app
When I swipe to the right in "Last hidden" "ion-slides" in the app
When I swipe to the right in "Last hidden" "swiper-container" in the app
Then I should find "Chapt 3" in the app
And I should find "This is the third chapter" in the app
And I should find "6 / 7" in the app

View File

@ -1,7 +1,7 @@
<!-- Buttons to add to the header. -->
<core-navbar-buttons slot="end">
<ion-button fill="clear" (click)="openModuleSummary()" aria-haspopup="true" [attr.aria-label]="'core.info' | translate">
<ion-icon name="fas-circle-info" slot="icon-only" aria-hidden="true"></ion-icon>
<ion-icon name="fas-circle-info" slot="icon-only" aria-hidden="true" />
</ion-button>
</core-navbar-buttons>
@ -10,12 +10,11 @@
<!-- Activity info. -->
<core-course-module-info [module]="module" [description]="description" [component]="component" [componentId]="componentId"
[courseId]="courseId" (completionChanged)="onCompletionChange()">
</core-course-module-info>
[courseId]="courseId" (completionChanged)="onCompletionChange()" />
<ion-card *ngIf="chatTime" class="core-info-card">
<ion-item>
<ion-icon name="fas-clock" slot="start" aria-hidden="true"></ion-icon>
<ion-icon name="fas-clock" slot="start" aria-hidden="true" />
<ion-label>{{ 'addon.mod_chat.sessionstartsin' | translate:{$a: chatTime} }}</ion-label>
</ion-item>
</ion-card>
@ -31,7 +30,6 @@
</ion-button>
</div>
<core-course-module-navigation [courseId]="courseId" [currentModuleId]="module.id">
</core-course-module-navigation>
<core-course-module-navigation [courseId]="courseId" [currentModuleId]="module.id" />
</div>
</core-loading>

View File

@ -5,7 +5,7 @@
</ion-title>
<ion-buttons slot="end">
<ion-button fill="clear" (click)="closeModal()" [attr.aria-label]="'core.close' | translate">
<ion-icon slot="icon-only" name="fas-xmark" aria-hidden="true"></ion-icon>
<ion-icon slot="icon-only" name="fas-xmark" aria-hidden="true" />
</ion-button>
</ion-buttons>
</ion-toolbar>
@ -14,16 +14,16 @@
<core-loading [hideUntil]="usersLoaded">
<ion-item class="ion-text-wrap" *ngFor="let user of users" [class.addon-mod-chat-user]="currentUserId !== user.id && isOnline">
<core-user-avatar [user]="user" slot="start" [linkProfile]="false"></core-user-avatar>
<core-user-avatar [user]="user" slot="start" [linkProfile]="false" />
<ion-label>
<p class="item-heading">{{ user.fullname }}</p>
<ng-container *ngIf="currentUserId !== user.id && isOnline">
<ion-button fill="clear" (click)="talkTo(user)">
<ion-icon name="fas-comments" slot="start" aria-hidden="true"></ion-icon>
<ion-icon name="fas-comments" slot="start" aria-hidden="true" />
{{ 'addon.mod_chat.talk' | translate }}
</ion-button>
<ion-button fill="clear" (click)="beepTo(user)">
<ion-icon name="fas-bell" slot="start" aria-hidden="true"></ion-icon>
<ion-icon name="fas-bell" slot="start" aria-hidden="true" />
{{ 'addon.mod_chat.beep' | translate }}
</ion-button>
</ng-container>

View File

@ -1,17 +1,16 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
<ion-back-button [text]="'core.back' | translate" />
</ion-buttons>
<ion-title>
<h1>
<core-format-text [text]="title" contextLevel="module" [contextInstanceId]="cmId" [courseId]="courseId">
</core-format-text>
<core-format-text [text]="title" contextLevel="module" [contextInstanceId]="cmId" [courseId]="courseId" />
</h1>
</ion-title>
<ion-buttons slot="end">
<ion-button fill="clear" *ngIf="loaded" (click)="showChatUsers()" [attr.aria-label]="'core.users' | translate">
<ion-icon name="fas-users" slot="icon-only" aria-hidden="true"></ion-icon>
<ion-icon name="fas-users" slot="icon-only" aria-hidden="true" />
</ion-button>
</ion-buttons>
</ion-toolbar>
@ -28,7 +27,7 @@
<div class="ion-text-center addon-mod_chat-notice" *ngIf="message.special">
<ion-badge class="ion-text-wrap" color="success" *ngIf="message.system && message.message === 'enter'">
<span>
<ion-icon name="fas-right-to-bracket" aria-hidden="true"></ion-icon>
<ion-icon name="fas-right-to-bracket" aria-hidden="true" />
{{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }}
{{ 'addon.mod_chat.messageenter' | translate:{$a: message.userfullname} }}
</span>
@ -36,7 +35,7 @@
<ion-badge class="ion-text-wrap" color="danger" *ngIf="message.system && message.message === 'exit'">
<span>
<ion-icon name="fas-right-from-bracket" aria-hidden="true"></ion-icon>
<ion-icon name="fas-right-from-bracket" aria-hidden="true" />
{{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }}
{{ 'addon.mod_chat.messageexit' | translate:{$a: message.userfullname} }}
</span>
@ -44,7 +43,7 @@
<ion-badge class="ion-text-wrap" color="primary" *ngIf="message.beep === 'all'">
<span>
<ion-icon name="fas-bell" aria-hidden="true"></ion-icon>
<ion-icon name="fas-bell" aria-hidden="true" />
{{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }}
{{ 'addon.mod_chat.messagebeepseveryone' | translate:{$a: message.userfullname} }}
</span>
@ -53,7 +52,7 @@
<ion-badge class="ion-text-wrap" color="primary"
*ngIf="message.userid !== currentUserId && message.beep === currentUserId">
<span>
<ion-icon name="fas-bell" aria-hidden="true"></ion-icon>
<ion-icon name="fas-bell" aria-hidden="true" />
{{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }}
{{ 'addon.mod_chat.messagebeepsyou' | translate:{$a: message.userfullname} }}
</span>
@ -62,7 +61,7 @@
<ion-badge class="ion-text-wrap" color="light"
*ngIf="message.userid === currentUserId && message.beep && message.beep !== 'all'">
<span>
<ion-icon name="fas-bell" aria-hidden="true"></ion-icon>
<ion-icon name="fas-bell" aria-hidden="true" />
{{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }}
{{ 'addon.mod_chat.messageyoubeep' | translate:{$a: message.beepWho} }}
</span>
@ -70,12 +69,11 @@
<ion-badge class="ion-text-wrap" color="info" *ngIf="!message.system && !message.beep">
<span>
<ion-icon name="fas-asterisk" aria-hidden="true"></ion-icon>
<ion-icon name="fas-asterisk" aria-hidden="true" />
{{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }}
<strong>
{{ message.userfullname }} <core-format-text [text]="message.message" contextLevel="module"
[contextInstanceId]="cmId" [courseId]="courseId" (afterRender)="last && scrollToBottom()">
</core-format-text>
[contextInstanceId]="cmId" [courseId]="courseId" (afterRender)="last && scrollToBottom()" />
</strong>
</span>
</ion-badge>
@ -83,13 +81,11 @@
<core-message *ngIf="!message.special" [message]="message" [user]="message" [text]="message.message"
[time]="message.timestamp * 1000" (afterRender)="last && scrollToBottom()" contextLevel="module" [instanceId]="cmId"
[courseId]="courseId">
</core-message>
[courseId]="courseId" />
</ng-container>
</ion-list>
<core-empty-box *ngIf="!messages || messages.length <= 0" icon="far-comments" [message]="'addon.mod_chat.nomessages' | translate">
</core-empty-box>
<core-empty-box *ngIf="!messages || messages.length <= 0" icon="far-comments" [message]="'addon.mod_chat.nomessages' | translate" />
</core-loading>
</ion-content>
<ion-footer class="footer-adjustable">
@ -99,8 +95,7 @@
</p>
<core-send-message-form [sendDisabled]="sending" *ngIf="isOnline && polling && loaded" [message]="newMessage"
(onSubmit)="sendMessage($event)" [placeholder]="'addon.messages.newmessage' | translate">
</core-send-message-form>
(onSubmit)="sendMessage($event)" [placeholder]="'addon.messages.newmessage' | translate" />
<ion-button *ngIf="isOnline && !polling && loaded" (click)="reconnect()" expand="block" fill="outline">
{{ 'core.login.reconnect' | translate }}

View File

@ -1,23 +1,20 @@
<ion-header collapsible>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
<ion-back-button [text]="'core.back' | translate" />
</ion-buttons>
<ion-title>
<h1>
<core-format-text [text]="title" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
</core-format-text>
<core-format-text [text]="title" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId" />
</h1>
</ion-title>
<ion-buttons slot="end">
<!-- The buttons defined by the component will be added in here. -->
</ion-buttons>
<ion-buttons slot="end" />
</ion-toolbar>
</ion-header>
<ion-content class="limited-width">
<ion-refresher slot="fixed" [disabled]="activityComponent?.showLoading" (ionRefresh)="activityComponent?.doRefresh($event.target)">
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}" />
</ion-refresher>
<addon-mod-chat-index [module]="module" [courseId]="courseId" (dataRetrieved)="updateData($event)"></addon-mod-chat-index>
<addon-mod-chat-index [module]="module" [courseId]="courseId" (dataRetrieved)="updateData($event)" />
</ion-content>

View File

@ -1,7 +1,7 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
<ion-back-button [text]="'core.back' | translate" />
</ion-buttons>
<ion-title>
<h1>{{ 'addon.mod_chat.messages' | translate }}</h1>
@ -10,7 +10,7 @@
</ion-header>
<ion-content>
<ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="refreshMessages($event.target)">
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}" />
</ion-refresher>
<core-loading [hideUntil]="loaded">
<ion-list class="addon-messages-discussion-container">
@ -23,7 +23,7 @@
<div class="ion-text-center addon-mod_chat-notice" *ngIf="message.special">
<ion-badge class="ion-text-wrap" color="success" *ngIf="message.issystem && message.message === 'enter'">
<span>
<ion-icon name="fas-right-to-bracket" aria-hidden="true"></ion-icon>
<ion-icon name="fas-right-to-bracket" aria-hidden="true" />
{{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }}
{{ 'addon.mod_chat.messageenter' | translate:{$a: message.userfullname} }}
</span>
@ -31,7 +31,7 @@
<ion-badge class="ion-text-wrap" color="danger" *ngIf="message.issystem && message.message === 'exit'">
<span>
<ion-icon name="fas-right-from-bracket" aria-hidden="true"></ion-icon>
<ion-icon name="fas-right-from-bracket" aria-hidden="true" />
{{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }}
{{ 'addon.mod_chat.messageexit' | translate:{$a: message.userfullname} }}
</span>
@ -39,7 +39,7 @@
<ion-badge class="ion-text-wrap" color="primary" *ngIf="message.beep === 'all'">
<span>
<ion-icon name="fas-bell" aria-hidden="true"></ion-icon>
<ion-icon name="fas-bell" aria-hidden="true" />
{{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }}
{{ 'addon.mod_chat.messagebeepseveryone' | translate:{$a: message.userfullname} }}
</span>
@ -48,7 +48,7 @@
<ion-badge class="ion-text-wrap" color="primary"
*ngIf="message.userid !== currentUserId && message.beep === currentUserId">
<span>
<ion-icon name="fas-bell" aria-hidden="true"></ion-icon>
<ion-icon name="fas-bell" aria-hidden="true" />
{{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }}
{{ 'addon.mod_chat.messagebeepsyou' | translate:{$a: message.userfullname} }}
</span>
@ -57,7 +57,7 @@
<ion-badge class="ion-text-wrap" color="light"
*ngIf="message.userid === currentUserId && message.beep && message.beep !== 'all'">
<span>
<ion-icon name="fas-bell" aria-hidden="true"></ion-icon>
<ion-icon name="fas-bell" aria-hidden="true" />
{{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }}
{{ 'addon.mod_chat.messageyoubeep' | translate:{$a: message.beepWho} }}
</span>
@ -65,19 +65,18 @@
<ion-badge class="ion-text-wrap" color="info" *ngIf="!message.issystem && !message.beep">
<span>
<ion-icon name="fas-asterisk" aria-hidden="true"></ion-icon>
<ion-icon name="fas-asterisk" aria-hidden="true" />
{{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }}
<strong>
{{ message.userfullname }} <core-format-text [text]="message.message" contextLevel="module"
[contextInstanceId]="cmId" [courseId]="courseId"></core-format-text>
[contextInstanceId]="cmId" [courseId]="courseId" />
</strong>
</span>
</ion-badge>
</div>
<core-message *ngIf="!message.special" [message]="message" [user]="message" [text]="message.message"
[time]="message.timestamp * 1000" contextLevel="module" [instanceId]="cmId" [courseId]="courseId">
</core-message>
[time]="message.timestamp * 1000" contextLevel="module" [instanceId]="cmId" [courseId]="courseId" />
</ng-container>
</ion-list>
</core-loading>

View File

@ -1,7 +1,7 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
<ion-back-button [text]="'core.back' | translate" />
</ion-buttons>
<ion-title>
<h1>{{ 'addon.mod_chat.chatreport' | translate }}</h1>
@ -11,15 +11,14 @@
<ion-content>
<core-split-view>
<ion-refresher slot="fixed" [disabled]="!sessions.loaded" (ionRefresh)="refreshSessions($event.target)">
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}" />
</ion-refresher>
<core-loading [hideUntil]="sessions.loaded">
<core-group-selector [groupInfo]="groupInfo" [(selected)]="groupId" (selectedChange)="reloadSessions()" [courseId]="courseId">
</core-group-selector>
<core-group-selector [groupInfo]="groupInfo" [(selected)]="groupId" (selectedChange)="reloadSessions()" [courseId]="courseId" />
<ion-item>
<ion-label>{{ 'addon.mod_chat.showincompletesessions' | translate }}</ion-label>
<ion-toggle [(ngModel)]="showAll" (ionChange)="reloadSessions()" slot="end"></ion-toggle>
<ion-toggle [(ngModel)]="showAll" (ionChange)="reloadSessions()" slot="end" />
</ion-item>
<ion-card *ngFor="let session of sessions.items" (click)="sessions.select(session)" button
@ -45,8 +44,7 @@
</ion-button>
</ion-card>
<core-empty-box *ngIf="sessions.empty" icon="far-comments" [message]="'addon.mod_chat.nosessionsfound' | translate">
</core-empty-box>
<core-empty-box *ngIf="sessions.empty" icon="far-comments" [message]="'addon.mod_chat.nosessionsfound' | translate" />
</core-loading>
</core-split-view>
</ion-content>

View File

@ -1,7 +1,7 @@
<!-- Buttons to add to the header. -->
<core-navbar-buttons slot="end">
<ion-button fill="clear" (click)="openModuleSummary()" aria-haspopup="true" [attr.aria-label]="'core.info' | translate">
<ion-icon name="fas-circle-info" slot="icon-only" aria-hidden="true"></ion-icon>
<ion-icon name="fas-circle-info" slot="icon-only" aria-hidden="true" />
</ion-button>
</core-navbar-buttons>
@ -9,13 +9,12 @@
<core-loading [hideUntil]="!showLoading">
<!-- Activity info. -->
<core-course-module-info [module]="module" [description]="description" [component]="component" [componentId]="componentId"
[courseId]="courseId" [hasDataToSync]="hasOffline" (completionChanged)="onCompletionChange()">
</core-course-module-info>
[courseId]="courseId" [hasDataToSync]="hasOffline" (completionChanged)="onCompletionChange()" />
<!-- Activity availability messages -->
<ion-card class="core-info-card" *ngIf="choiceNotOpenYet">
<ion-item>
<ion-icon name="fas-circle-info" slot="start" aria-hidden="true"></ion-icon>
<ion-icon name="fas-circle-info" slot="start" aria-hidden="true" />
<ion-label>
<p *ngIf="options.length">{{ 'addon.mod_choice.previewonly' | translate:{$a: openTimeReadable} }}</p>
<p *ngIf="!options.length">{{ 'addon.mod_choice.notopenyet' | translate:{$a: openTimeReadable} }}</p>
@ -25,12 +24,12 @@
<ion-card class="core-info-card" *ngIf="choiceClosed">
<ion-item>
<ion-icon name="fas-circle-info" slot="start" aria-hidden="true"></ion-icon>
<ion-icon name="fas-circle-info" slot="start" aria-hidden="true" />
<ion-label>
<p *ngIf="options.length">
{{ 'addon.mod_choice.yourselection' | translate }}
<core-format-text [text]="options[0].text" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
</core-format-text>
<core-format-text [text]="options[0].text" contextLevel="module" [contextInstanceId]="module.id"
[courseId]="courseId" />
</p>
<p>{{ 'addon.mod_choice.expired' | translate:{$a: closeTimeReadable} }}</p>
</ion-label>
@ -40,7 +39,7 @@
<!-- Inform what will happen with the choices. -->
<ion-card class="core-info-card" *ngIf="canEdit && publishInfo && options.length">
<ion-item>
<ion-icon name="fas-circle-info" slot="start" aria-hidden="true"></ion-icon>
<ion-icon name="fas-circle-info" slot="start" aria-hidden="true" />
<ion-label>{{ publishInfo | translate }}</ion-label>
</ion-item>
</ion-card>
@ -50,17 +49,17 @@
<ng-container *ngIf="choice.allowmultiple">
<ion-item class="ion-text-wrap" *ngFor="let option of options">
<ion-label>
<ng-container *ngTemplateOutlet="optionLabelTemplate; context: {option: option}"></ng-container>
<ng-container *ngTemplateOutlet="optionLabelTemplate; context: {option: option}" />
</ion-label>
<ion-checkbox slot="end" [(ngModel)]="option.checked" [disabled]="option.disabled || !canEdit"></ion-checkbox>
<ion-checkbox slot="end" [(ngModel)]="option.checked" [disabled]="option.disabled || !canEdit" />
</ion-item>
</ng-container>
<ion-radio-group *ngIf="!choice.allowmultiple" [(ngModel)]="selectedOption.id">
<ion-item class="ion-text-wrap" *ngFor="let option of options">
<ion-label>
<ng-container *ngTemplateOutlet="optionLabelTemplate; context: {option: option}"></ng-container>
<ng-container *ngTemplateOutlet="optionLabelTemplate; context: {option: option}" />
</ion-label>
<ion-radio slot="end" [value]="option.id" [disabled]="option.disabled || !canEdit"></ion-radio>
<ion-radio slot="end" [value]="option.id" [disabled]="option.disabled || !canEdit" />
</ion-item>
</ion-radio-group>
</ion-card>
@ -75,14 +74,13 @@
<ion-row>
<ion-col size="12" size-lg="5">
<ion-item class="ion-text-wrap core-warning-item" *ngIf="hasOffline">
<ion-icon slot="start" name="fas-triangle-exclamation" color="warning" aria-hidden="true"></ion-icon>
<ion-icon slot="start" name="fas-triangle-exclamation" color="warning" aria-hidden="true" />
<ion-label>{{ 'addon.mod_choice.resultsnotsynced' | translate }}</ion-label>
</ion-item>
<ion-item>
<ion-label>
<core-chart type="pie" [data]="data" [labels]="labels" height="300" contextLevel="module"
[contextInstanceId]="module.id" [courseId]="courseId">
</core-chart>
[contextInstanceId]="module.id" [courseId]="courseId" />
</ion-label>
</ion-item>
</ion-col>
@ -91,13 +89,11 @@
<ion-item [button]="result.numberofuser > 0" class="divider ion-text-wrap" (click)="toggle(result)"
[attr.aria-label]="(result.expanded ? 'core.collapse' : 'core.expand') | translate" [detail]="false">
<ion-icon [name]="result.numberofuser > 0 ? 'fas-chevron-right' : ''" flip-rtl slot="start" aria-hidden="true"
class="expandable-status-icon" [class.expandable-status-icon-expanded]="result.expanded">
</ion-icon>
class="expandable-status-icon" [class.expandable-status-icon-expanded]="result.expanded" />
<ion-label>
<h3 class="item-heading">
<core-format-text [text]="result.text" contextLevel="module" [contextInstanceId]="module.id"
[courseId]="courseId">
</core-format-text>
[courseId]="courseId" />
</h3>
<p>
{{ 'addon.mod_choice.numberofuser' | translate }}: {{ result.numberofuser }}
@ -111,7 +107,7 @@
<ng-container *ngIf="result.expanded">
<ion-item *ngFor="let user of result.userresponses" core-user-link [courseId]="courseId" [userId]="user.userid"
[attr.aria-label]="user.fullname" class="ion-text-wrap">
<core-user-avatar [user]="user" slot="start" [courseId]="courseId" [linkProfile]="false"></core-user-avatar>
<core-user-avatar [user]="user" slot="start" [courseId]="courseId" [linkProfile]="false" />
<ion-label>
<p>{{user.fullname}}</p>
</ion-label>
@ -125,7 +121,7 @@
<ion-card class="core-info-card" *ngIf="!canSeeResults && !choiceNotOpenYet">
<ion-item>
<ion-icon name="fas-circle-info" slot="start" aria-hidden="true"></ion-icon>
<ion-icon name="fas-circle-info" slot="start" aria-hidden="true" />
<ion-label>
<p>{{ 'addon.mod_choice.noresultsviewable' | translate }}</p>
</ion-label>
@ -143,8 +139,7 @@
</ion-button>
</div>
<core-course-module-navigation [courseId]="courseId" [currentModuleId]="module.id">
</core-course-module-navigation>
<core-course-module-navigation [courseId]="courseId" [currentModuleId]="module.id" />
</div>
</core-loading>
@ -153,8 +148,7 @@
<!-- Template to render a choice option label. -->
<ng-template #optionLabelTemplate let-option="option">
<p>
<core-format-text [text]="option.text" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
</core-format-text>
<core-format-text [text]="option.text" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId" />
<span *ngIf="choice!.limitanswers && option.countanswers >= option.maxanswers">
{{ 'addon.mod_choice.full' | translate }}
</span>

View File

@ -1,23 +1,20 @@
<ion-header collapsible>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
<ion-back-button [text]="'core.back' | translate" />
</ion-buttons>
<ion-title>
<h1>
<core-format-text [text]="title" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
</core-format-text>
<core-format-text [text]="title" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId" />
</h1>
</ion-title>
<ion-buttons slot="end">
<!-- The buttons defined by the component will be added in here. -->
</ion-buttons>
<ion-buttons slot="end" />
</ion-toolbar>
</ion-header>
<ion-content class="limited-width">
<ion-refresher slot="fixed" [disabled]="activityComponent?.showLoading" (ionRefresh)="activityComponent?.doRefresh($event.target)">
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}" />
</ion-refresher>
<addon-mod-choice-index [module]="module" [courseId]="courseId" (dataRetrieved)="updateData($event)"></addon-mod-choice-index>
<addon-mod-choice-index [module]="module" [courseId]="courseId" (dataRetrieved)="updateData($event)" />
</ion-content>

View File

@ -1,49 +1,48 @@
<ion-button size="small" *ngIf="action === 'actionsmenu'" fill="clear" (click)="actionsMenu($event)"
[attr.aria-label]="'addon.mod_data.actionsmenu' | translate">
<ion-icon name="fas-ellipsis-vertical" slot="icon-only" aria-hidden="true"></ion-icon>
<ion-icon name="fas-ellipsis-vertical" slot="icon-only" aria-hidden="true" />
</ion-button>
<ion-button size="small" *ngIf="action === 'more'" fill="clear" (click)="viewEntry()"
[attr.aria-label]="'addon.mod_data.showmore' | translate">
<ion-icon name="fas-magnifying-glass-plus" slot="icon-only" aria-hidden="true"></ion-icon>
<ion-icon name="fas-magnifying-glass-plus" slot="icon-only" aria-hidden="true" />
</ion-button>
<ion-button size="small" *ngIf="action === 'edit'" fill="clear" (click)="editEntry()" [attr.aria-label]="'core.edit' | translate">
<ion-icon name="fas-pen" slot="icon-only" aria-hidden="true"></ion-icon>
<ion-icon name="fas-pen" slot="icon-only" aria-hidden="true" />
</ion-button>
<ion-button size="small" *ngIf="action === 'delete' && !entry.deleted" fill="clear" color="danger" (click)="deleteEntry()"
[attr.aria-label]="'core.delete' | translate">
<ion-icon name="fas-trash" slot="icon-only" aria-hidden="true"></ion-icon>
<ion-icon name="fas-trash" slot="icon-only" aria-hidden="true" />
</ion-button>
<ion-button size="small" *ngIf="action === 'delete' && entry.deleted" fill="clear" color="danger" (click)="undoDelete()"
[attr.aria-label]="'core.restore' | translate">
<ion-icon name="fas-rotate-left" slot="icon-only" aria-hidden="true"></ion-icon>
<ion-icon name="fas-rotate-left" slot="icon-only" aria-hidden="true" />
</ion-button>
<ion-button size="small" *ngIf="action === 'approve'" fill="clear" (click)="approveEntry()"
[attr.aria-label]="'addon.mod_data.approve' | translate">
<ion-icon name="fas-thumbs-up" slot="icon-only" aria-hidden="true"></ion-icon>
<ion-icon name="fas-thumbs-up" slot="icon-only" aria-hidden="true" />
</ion-button>
<ion-button size="small" *ngIf="action === 'disapprove'" fill="clear" (click)="disapproveEntry()"
[attr.aria-label]="'addon.mod_data.disapprove' | translate">
<ion-icon name="far-thumbs-down" slot="icon-only" aria-hidden="true"></ion-icon>
<ion-icon name="far-thumbs-down" slot="icon-only" aria-hidden="true" />
</ion-button>
<core-comments *ngIf="action === 'comments' && mode === 'list'" contextLevel="module" [instanceId]="database.coursemodule"
component="mod_data" [itemId]="entry.id" area="database_entry" [courseId]="database.course">
</core-comments>
component="mod_data" [itemId]="entry.id" area="database_entry" [courseId]="database.course" />
<span *ngIf="action === 'timeadded'">{{ entry.timecreated * 1000 | coreFormatDate }}</span>
<span *ngIf="action === 'timemodified'">{{ entry.timemodified * 1000 | coreFormatDate }}</span>
<core-user-avatar *ngIf="action === 'userpicture'" [user]="entry" slot="start" [courseId]="database.course" [userId]="entry.userid"
[profileUrl]="userPicture"></core-user-avatar>
[profileUrl]="userPicture" />
<a *ngIf="action === 'user' && entry" core-user-link [courseId]="database.course" [userId]="entry.userid" [title]="entry.fullname">
{{entry.fullname}}
</a>
<core-tag-list *ngIf="tagsEnabled && action === 'tags' && entry" [tags]="entry.tags"></core-tag-list>
<core-tag-list *ngIf="tagsEnabled && action === 'tags' && entry" [tags]="entry.tags" />

View File

@ -5,7 +5,7 @@
<ion-label>
<p class="item-heading">{{ item.text | translate }}</p>
</ion-label>
<ion-icon [name]="item.icon" slot="end" aria-hidden="true"></ion-icon>
<ion-icon [name]="item.icon" slot="end" aria-hidden="true" />
</ion-item>
</ion-list>

View File

@ -1,4 +1,4 @@
<core-dynamic-component [component]="fieldComponent" [data]="pluginData">
<!-- This content will be replaced by the component if found. -->
<core-loading [hideUntil]="fieldLoaded" [fullscreen]="false"></core-loading>
<core-loading [hideUntil]="fieldLoaded" [fullscreen]="false" />
</core-dynamic-component>

View File

@ -1,19 +1,17 @@
<!-- Buttons to add to the header. -->
<core-navbar-buttons slot="end">
<ion-button *ngIf="canSearch" (click)="showSearch()" [attr.aria-label]="'addon.mod_data.search' | translate">
<ion-icon name="fas-magnifying-glass" slot="icon-only" aria-hidden="true"></ion-icon>
<ion-icon name="fas-magnifying-glass" slot="icon-only" aria-hidden="true" />
</ion-button>
<core-context-menu>
<core-context-menu-item [priority]="500" *ngIf="canAdd" [content]="'addon.mod_data.addentries' | translate" iconAction="fas-plus"
(action)="gotoAddEntries()">
</core-context-menu-item>
(action)="gotoAddEntries()" />
<core-context-menu-item [priority]="400" *ngIf="firstEntry" [content]="'addon.mod_data.single' | translate" iconAction="fas-file"
(action)="gotoEntry(firstEntry)">
</core-context-menu-item>
(action)="gotoEntry(firstEntry)" />
</core-context-menu>
<ion-button fill="clear" (click)="openModuleSummary()" aria-haspopup="true" [attr.aria-label]="'core.info' | translate">
<ion-icon name="fas-circle-info" slot="icon-only" aria-hidden="true"></ion-icon>
<ion-icon name="fas-circle-info" slot="icon-only" aria-hidden="true" />
</ion-button>
</core-navbar-buttons>
@ -22,30 +20,28 @@
<!-- Activity info. -->
<core-course-module-info [module]="module" [description]="description" [component]="component" [componentId]="componentId"
[courseId]="courseId" [hasDataToSync]="hasOffline || hasOfflineRatings" (completionChanged)="onCompletionChange()">
</core-course-module-info>
[courseId]="courseId" [hasDataToSync]="hasOffline || hasOfflineRatings" (completionChanged)="onCompletionChange()" />
<core-group-selector [groupInfo]="groupInfo" [(selected)]="selectedGroup" (selectedChange)="setGroup(selectedGroup)"
[courseId]="database?.course">
</core-group-selector>
[courseId]="database?.course" />
<ion-card class="core-info-card" *ngIf="!access?.timeavailable && timeAvailableFrom">
<ion-item>
<ion-icon name="fas-circle-info" slot="start" aria-hidden="true"></ion-icon>
<ion-icon name="fas-circle-info" slot="start" aria-hidden="true" />
<ion-label>{{ 'addon.mod_data.notopenyet' | translate:{$a: timeAvailableFromReadable} }}</ion-label>
</ion-item>
</ion-card>
<ion-card class="core-info-card" *ngIf="!access?.timeavailable && timeAvailableTo">
<ion-item>
<ion-icon name="fas-circle-info" slot="start" aria-hidden="true"></ion-icon>
<ion-icon name="fas-circle-info" slot="start" aria-hidden="true" />
<ion-label>{{ 'addon.mod_data.expired' | translate:{$a: timeAvailableToReadable} }}</ion-label>
</ion-item>
</ion-card>
<ion-card class="core-info-card" *ngIf="access && access.entrieslefttoview">
<ion-item>
<ion-icon name="fas-circle-info" slot="start" aria-hidden="true"></ion-icon>
<ion-icon name="fas-circle-info" slot="start" aria-hidden="true" />
<ion-label>
{{ 'addon.mod_data.entrieslefttoaddtoview' | translate:{$a: {entrieslefttoview: access.entrieslefttoview} } }}
</ion-label>
@ -54,7 +50,7 @@
<ion-card class="core-info-card" *ngIf="access && access.entrieslefttoadd">
<ion-item>
<ion-icon name="fas-circle-info" slot="start" aria-hidden="true"></ion-icon>
<ion-icon name="fas-circle-info" slot="start" aria-hidden="true" />
<ion-label>
{{ 'addon.mod_data.entrieslefttoadd' | translate:{$a: {entriesleft: access.entrieslefttoadd} } }}
</ion-label>
@ -79,30 +75,29 @@
</ng-container>
<div class="addon-data-contents addon-data-entries-{{database.id}}" *ngIf="!isEmpty && database">
<core-style [css]="database.csstemplate" prefix=".addon-data-entries-{{database.id}}"></core-style>
<core-style [css]="database.csstemplate" prefix=".addon-data-entries-{{database.id}}" />
<core-compile-html [text]="entriesRendered" [jsData]="jsData" [extraImports]="extraImports"></core-compile-html>
<core-compile-html [text]="entriesRendered" [jsData]="jsData" [extraImports]="extraImports" />
</div>
<ion-grid *ngIf="search.page > 0 || hasNextPage">
<ion-row class="ion-align-items-center">
<ion-col *ngIf="search.page > 0">
<ion-button expand="block" fill="outline" (click)="searchEntries(search.page - 1)">
<ion-icon name="fas-chevron-left" slot="start" aria-hidden="true"></ion-icon>
<ion-icon name="fas-chevron-left" slot="start" aria-hidden="true" />
{{ 'core.previous' | translate }}
</ion-button>
</ion-col>
<ion-col *ngIf="hasNextPage">
<ion-button expand="block" (click)="searchEntries(search.page + 1)">
{{ 'core.next' | translate }}
<ion-icon name="fas-chevron-right" slot="end" aria-hidden="true"></ion-icon>
<ion-icon name="fas-chevron-right" slot="end" aria-hidden="true" />
</ion-button>
</ion-col>
</ion-row>
</ion-grid>
<core-empty-box *ngIf="isEmpty && !search.searching" icon="fas-database" [message]="'addon.mod_data.norecords' | translate">
</core-empty-box>
<core-empty-box *ngIf="isEmpty && !search.searching" icon="fas-database" [message]="'addon.mod_data.norecords' | translate" />
<core-empty-box *ngIf="isEmpty && search.searching" icon="fas-database" [message]="'addon.mod_data.nomatch' | translate"
class="core-empty-box-clickable">
@ -111,12 +106,11 @@
</core-loading>
<core-course-module-navigation collapsible-footer [hidden]="showLoading" [courseId]="courseId" [currentModuleId]="module.id">
</core-course-module-navigation>
<core-course-module-navigation collapsible-footer [hidden]="showLoading" [courseId]="courseId" [currentModuleId]="module.id" />
<ion-fab slot="fixed" core-fab vertical="bottom" horizontal="end" *ngIf="canAdd">
<ion-fab-button (click)="gotoAddEntries()" [attr.aria-label]="'addon.mod_data.addentries' | translate">
<ion-icon name="fas-plus" aria-hidden="true"></ion-icon>
<ion-icon name="fas-plus" aria-hidden="true" />
<span class="sr-only">{{ 'addon.mod_data.addentries' | translate }}</span>
</ion-fab-button>
</ion-fab>

View File

@ -5,7 +5,7 @@
</ion-title>
<ion-buttons slot="end">
<ion-button fill="clear" (click)="closeModal()" [attr.aria-label]="'core.close' | translate">
<ion-icon name="fas-xmark" slot="icon-only" aria-hidden=true></ion-icon>
<ion-icon name="fas-xmark" slot="icon-only" aria-hidden=true />
</ion-button>
</ion-buttons>
</ion-toolbar>
@ -13,15 +13,14 @@
<ion-content>
<ion-item>
<ion-label>{{ 'addon.mod_data.advancedsearch' | translate }}</ion-label>
<ion-toggle [(ngModel)]="search.searchingAdvanced" slot="end"></ion-toggle>
<ion-toggle [(ngModel)]="search.searchingAdvanced" slot="end" />
</ion-item>
<form (ngSubmit)="searchEntries($event)" [formGroup]="searchForm" #searchFormEl>
<ion-list class="ion-no-margin">
<ion-item [hidden]="search.searchingAdvanced">
<ion-label class="sr-only">{{ 'addon.mod_data.search' | translate}}</ion-label>
<ion-input type="text" placeholder="{{ 'addon.mod_data.search' | translate}}" [(ngModel)]="search.text" name="text"
formControlName="text">
</ion-input>
formControlName="text" />
</ion-item>
<ion-item class="ion-text-wrap">
<ion-label position="stacked">{{ 'core.sortby' | translate }}</ion-label>
@ -45,21 +44,21 @@
<ion-radio-group [(ngModel)]="search.sortDirection" name="sortDirection" formControlName="sortDirection">
<ion-item>
<ion-label>{{ 'addon.mod_data.ascending' | translate }}</ion-label>
<ion-radio slot="start" value="ASC"></ion-radio>
<ion-radio slot="start" value="ASC" />
</ion-item>
<ion-item>
<ion-label>{{ 'addon.mod_data.descending' | translate }}</ion-label>
<ion-radio slot="start" value="DESC"></ion-radio>
<ion-radio slot="start" value="DESC" />
</ion-item>
</ion-radio-group>
</ion-list>
<div class="ion-padding addon-data-advanced-search" [hidden]="!advancedSearch || !search.searchingAdvanced">
<core-compile-html [text]="advancedSearch" [jsData]="jsData" [extraImports]="extraImports"></core-compile-html>
<core-compile-html [text]="advancedSearch" [jsData]="jsData" [extraImports]="extraImports" />
</div>
</ion-list>
<div class="ion-padding">
<ion-button expand="block" type="submit">
<ion-icon name="fas-magnifying-glass" slot="start" aria-hidden="true"></ion-icon>
<ion-icon name="fas-magnifying-glass" slot="start" aria-hidden="true" />
{{ 'addon.mod_data.search' | translate }}
</ion-button>
</div>

View File

@ -5,13 +5,12 @@
interface="alert">
<ion-select-option *ngFor="let option of options" [value]="option.value">{{option.key}}</ion-select-option>
</ion-select>
<core-input-errors *ngIf="error && editMode" [control]="form.controls['f_'+field.id]" [errorText]="error"></core-input-errors>
<core-input-errors *ngIf="error && editMode" [control]="form.controls['f_'+field.id]" [errorText]="error" />
<ion-item *ngIf="searchMode">
<ion-label>{{ 'addon.mod_data.selectedrequired' | translate }}</ion-label>
<ion-checkbox slot="end" [formControlName]="'f_'+field.id+'_allreq'" [(ngModel)]="searchFields!['f_'+field.id+'_allreq']">
</ion-checkbox>
<ion-checkbox slot="end" [formControlName]="'f_'+field.id+'_allreq'" [(ngModel)]="searchFields!['f_'+field.id+'_allreq']" />
</ion-item>
</span>
<core-format-text *ngIf="displayMode && value && value.content" [text]="value.content" [filter]="false"></core-format-text>
<core-format-text *ngIf="displayMode && value && value.content" [text]="value.content" [filter]="false" />

View File

@ -1,14 +1,12 @@
<span *ngIf="inputMode && form" [formGroup]="form">
<span *ngIf="editMode" [core-mark-required]="field.required" class="core-mark-required"></span>
<ion-datetime [formControlName]="'f_'+field.id" [placeholder]="'core.date' | translate" [max]="maxDate" [min]="minDate"
[disabled]="searchMode && !searchFields!['f_'+field.id+'_z']" [displayFormat]="format" [displayTimezone]="displayTimezone">
</ion-datetime>
<core-input-errors *ngIf="error && editMode" [control]="form.controls['f_'+field.id]" [errorText]="error"></core-input-errors>
[disabled]="searchMode && !searchFields!['f_'+field.id+'_z']" [displayFormat]="format" [displayTimezone]="displayTimezone" />
<core-input-errors *ngIf="error && editMode" [control]="form.controls['f_'+field.id]" [errorText]="error" />
<ion-item *ngIf="searchMode">
<ion-label>{{ 'addon.mod_data.usedate' | translate }}</ion-label>
<ion-checkbox slot="end" [formControlName]="'f_'+field.id+'_z'" [(ngModel)]="searchFields!['f_'+field.id+'_z']">
</ion-checkbox>
<ion-checkbox slot="end" [formControlName]="'f_'+field.id+'_z'" [(ngModel)]="searchFields!['f_'+field.id+'_z']" />
</ion-item>
</span>

View File

@ -1,17 +1,16 @@
<span *ngIf="editMode && form">
<span [core-mark-required]="field.required" class="core-mark-required"></span>
<core-attachments [files]="files" [maxSize]="maxSizeBytes" maxSubmissions="1" [component]="component" [componentId]="componentId"
[allowOffline]="true" [courseId]="database?.course">
</core-attachments>
<core-input-errors *ngIf="error" [errorText]="error"></core-input-errors>
[allowOffline]="true" [courseId]="database?.course" />
<core-input-errors *ngIf="error" [errorText]="error" />
</span>
<span *ngIf="searchMode && form" [formGroup]="form">
<ion-input type="text" [formControlName]="'f_'+field.id" [placeholder]="field.name"></ion-input>
<ion-input type="text" [formControlName]="'f_'+field.id" [placeholder]="field.name" />
</span>
<ng-container *ngIf="displayMode">
<div>
<core-files [files]="files" [component]="component" [componentId]="componentId" [alwaysDownload]="true"></core-files>
<core-files [files]="files" [component]="component" [componentId]="componentId" [alwaysDownload]="true" />
</div>
</ng-container>

View File

@ -22,7 +22,7 @@ import {
import { AddonModDataFieldHandler } from '@addons/mod/data/services/data-fields-delegate';
import { Injectable, Type } from '@angular/core';
import { CoreFileUploader, CoreFileUploaderStoreFilesResult } from '@features/fileuploader/services/fileuploader';
import { FileEntry } from '@ionic-native/file/ngx';
import { FileEntry } from '@awesome-cordova-plugins/file/ngx';
import { CoreFileSession } from '@services/file-session';
import { CoreFormFields } from '@singletons/form';
import { makeSingleton, Translate } from '@singletons';

View File

@ -1,23 +1,23 @@
<span *ngIf="inputMode && form" [formGroup]="form">
<ion-input *ngIf="searchMode" type="text" [placeholder]="field.name" [formControlName]="'f_'+field.id"></ion-input>
<ion-input *ngIf="searchMode" type="text" [placeholder]="field.name" [formControlName]="'f_'+field.id" />
<ng-container *ngIf="editMode">
<span [core-mark-required]="field.required" class="core-mark-required"></span>
<div class="addon-data-latlong">
<ion-input type="text" [formControlName]="'f_'+field.id+'_0'" maxlength="10"></ion-input>
<ion-input type="text" [formControlName]="'f_'+field.id+'_0'" maxlength="10" />
<span class="placeholder-icon" item-right>°N</span>
</div>
<div class="addon-data-latlong">
<ion-input type="text" [formControlName]="'f_'+field.id+'_1'" maxlength="10"></ion-input>
<ion-input type="text" [formControlName]="'f_'+field.id+'_1'" maxlength="10" />
<span class="placeholder-icon" item-right>°E</span>
</div>
<div class="addon-data-latlong" *ngIf="locationServicesEnabled">
<ion-button (click)="getLocation($event)">
<ion-icon name="fas-crosshairs" slot="start" aria-hidden="true"></ion-icon>
<ion-icon name="fas-crosshairs" slot="start" aria-hidden="true" />
{{ 'addon.mod_data.mylocation' | translate }}
</ion-button>
</div>
<core-input-errors *ngIf="error" [control]="form.controls['f_'+field.id]" [errorText]="error"></core-input-errors>
<core-input-errors *ngIf="error" [control]="form.controls['f_'+field.id]" [errorText]="error" />
</ng-container>
</span>

View File

@ -5,7 +5,7 @@
<ion-select-option value="">{{ 'addon.mod_data.menuchoose' | translate }}</ion-select-option>
<ion-select-option *ngFor="let option of options" [value]="option">{{option}}</ion-select-option>
</ion-select>
<core-input-errors *ngIf="error && editMode" [control]="form.controls['f_'+field.id]" [errorText]="error"></core-input-errors>
<core-input-errors *ngIf="error && editMode" [control]="form.controls['f_'+field.id]" [errorText]="error" />
</span>
<span *ngIf="displayMode && value && value.content">{{ value.content }}</span>

View File

@ -5,14 +5,13 @@
interface="alert">
<ion-select-option *ngFor="let option of options" [value]="option.value">{{option.key}}</ion-select-option>
</ion-select>
<core-input-errors *ngIf="error && editMode" [control]="form.controls['f_'+field.id]" [errorText]="error"></core-input-errors>
<core-input-errors *ngIf="error && editMode" [control]="form.controls['f_'+field.id]" [errorText]="error" />
<ion-item *ngIf="searchMode">
<ion-label>{{ 'addon.mod_data.selectedrequired' | translate }}</ion-label>
<ion-checkbox slot="end" [formControlName]="'f_'+field.id+'_allreq'" [(ngModel)]="searchFields!['f_'+field.id+'_allreq']">
</ion-checkbox>
<ion-checkbox slot="end" [formControlName]="'f_'+field.id+'_allreq'" [(ngModel)]="searchFields!['f_'+field.id+'_allreq']" />
</ion-item>
</span>
<core-format-text *ngIf="displayMode && value && value.content" [text]="value.content" [filter]="false"></core-format-text>
<core-format-text *ngIf="displayMode && value && value.content" [text]="value.content" [filter]="false" />

View File

@ -1,7 +1,7 @@
<span *ngIf="inputMode && form" [formGroup]="form">
<span *ngIf="editMode" [core-mark-required]="field.required" class="core-mark-required"></span>
<ion-input type="number" [formControlName]="'f_'+field.id" [placeholder]="field.name"></ion-input>
<core-input-errors *ngIf="error && editMode" [control]="form.controls['f_'+field.id]" [errorText]="error"></core-input-errors>
<ion-input type="number" [formControlName]="'f_'+field.id" [placeholder]="field.name" />
<core-input-errors *ngIf="error && editMode" [control]="form.controls['f_'+field.id]" [errorText]="error" />
</span>
<span *ngIf="displayMode && value && value.content">{{ value.content }}</span>

View File

@ -1,17 +1,15 @@
<span *ngIf="editMode && form" [formGroup]="form">
<span [core-mark-required]="field.required" class="core-mark-required"></span>
<core-attachments [files]="files" [maxSize]="maxSizeBytes" maxSubmissions="1" [component]="component" [componentId]="componentId"
[allowOffline]="true" acceptedTypes="image" [courseId]="database?.course">
</core-attachments>
<core-input-errors *ngIf="error" [errorText]="error"></core-input-errors>
[allowOffline]="true" acceptedTypes="image" [courseId]="database?.course" />
<core-input-errors *ngIf="error" [errorText]="error" />
<ion-label position="stacked">{{ 'addon.mod_data.alttext' | translate }}</ion-label>
<ion-input type="text" [formControlName]="'f_'+field.id+'_alttext'" [placeholder]=" 'addon.mod_data.alttext' | translate">
</ion-input>
<ion-input type="text" [formControlName]="'f_'+field.id+'_alttext'" [placeholder]=" 'addon.mod_data.alttext' | translate" />
</span>
<span *ngIf="searchMode && form" [formGroup]="form">
<ion-input type="text" [formControlName]="'f_'+field.id" [placeholder]="field.name"></ion-input>
<ion-input type="text" [formControlName]="'f_'+field.id" [placeholder]="field.name" />
</span>
<button class="as-link" *ngIf="listMode && imageUrl" (click)="navigateEntry()">

View File

@ -22,7 +22,7 @@ import {
import { AddonModDataFieldHandler } from '@addons/mod/data/services/data-fields-delegate';
import { Injectable, Type } from '@angular/core';
import { CoreFileUploader, CoreFileUploaderStoreFilesResult } from '@features/fileuploader/services/fileuploader';
import { FileEntry } from '@ionic-native/file/ngx';
import { FileEntry } from '@awesome-cordova-plugins/file/ngx';
import { CoreFileSession } from '@services/file-session';
import { CoreFormFields } from '@singletons/form';
import { makeSingleton, Translate } from '@singletons';

View File

@ -6,7 +6,7 @@
<ion-select-option value="">{{ 'addon.mod_data.menuchoose' | translate }}</ion-select-option>
<ion-select-option *ngFor="let option of options" [value]="option">{{option}}</ion-select-option>
</ion-select>
<core-input-errors *ngIf="error && editMode" [control]="form.controls['f_'+field.id]" [errorText]="error"></core-input-errors>
<core-input-errors *ngIf="error && editMode" [control]="form.controls['f_'+field.id]" [errorText]="error" />
</span>
<span *ngIf="displayMode && value && value.content">{{ value.content }}</span>

View File

@ -1,7 +1,7 @@
<span *ngIf="inputMode && form" [formGroup]="form">
<span *ngIf="editMode" [core-mark-required]="field.required" class="core-mark-required"></span>
<ion-input type="text" [formControlName]="'f_'+field.id" [placeholder]="field.name"></ion-input>
<core-input-errors *ngIf="error && editMode" [control]="form.controls['f_'+field.id]" [errorText]="error"></core-input-errors>
<ion-input type="text" [formControlName]="'f_'+field.id" [placeholder]="field.name" />
<core-input-errors *ngIf="error && editMode" [control]="form.controls['f_'+field.id]" [errorText]="error" />
</span>
<span *ngIf="displayMode && value && value.content">{{ value.content }}</span>

View File

@ -1,14 +1,12 @@
<span *ngIf="inputMode && form" [formGroup]="form">
<ion-input *ngIf="searchMode" type="text" [placeholder]="field.name" [formControlName]="'f_'+field.id"></ion-input>
<ion-input *ngIf="searchMode" type="text" [placeholder]="field.name" [formControlName]="'f_'+field.id" />
<span *ngIf="editMode" [core-mark-required]="field.required" class="core-mark-required"></span>
<core-rich-text-editor *ngIf="editMode" [control]="form.controls['f_'+field.id]" [placeholder]="field.name" [name]="'f_'+field.id"
[component]="component" [componentId]="componentId" [autoSave]="true" contextLevel="module" [contextInstanceId]="componentId"
[elementId]="'field_'+field.id" ngDefaultControl>
</core-rich-text-editor>
<core-input-errors *ngIf="error && editMode" [control]="form.controls['f_'+field.id]" [errorText]="error"></core-input-errors>
[elementId]="'field_'+field.id" ngDefaultControl />
<core-input-errors *ngIf="error && editMode" [control]="form.controls['f_'+field.id]" [errorText]="error" />
</span>
<core-format-text *ngIf="displayMode && value" [text]="format(value)" [component]="component" [componentId]="componentId"
contextLevel="module" [contextInstanceId]="componentId" [courseId]="database!.course">
</core-format-text>
contextLevel="module" [contextInstanceId]="componentId" [courseId]="database!.course" />

View File

@ -1,7 +1,7 @@
<span *ngIf="inputMode && form" [formGroup]="form">
<span *ngIf="editMode" [core-mark-required]="field.required" class="core-mark-required"></span>
<ion-input type="url" [formControlName]="'f_'+field.id" [placeholder]="field.name"></ion-input>
<core-input-errors *ngIf="error && editMode" [control]="form.controls['f_'+field.id]" [errorText]="error"></core-input-errors>
<ion-input type="url" [formControlName]="'f_'+field.id" [placeholder]="field.name" />
<core-input-errors *ngIf="error && editMode" [control]="form.controls['f_'+field.id]" [errorText]="error" />
</span>
<ng-container *ngIf="displayMode && value && value.content">

View File

@ -1,12 +1,11 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
<ion-back-button [text]="'core.back' | translate" />
</ion-buttons>
<ion-title>
<h1>
<core-format-text [text]="title" contextLevel="module" [contextInstanceId]="moduleId" [courseId]="courseId">
</core-format-text>
<core-format-text [text]="title" contextLevel="module" [contextInstanceId]="moduleId" [courseId]="courseId" />
</h1>
</ion-title>
<ion-buttons slot="end">
@ -19,14 +18,13 @@
<ion-content>
<core-loading [hideUntil]="loaded">
<core-group-selector [groupInfo]="groupInfo" [(selected)]="selectedGroup" (selectedChange)="setGroup(selectedGroup)"
[courseId]="database?.course">
</core-group-selector>
[courseId]="database?.course" />
<div class="addon-data-contents {{cssClass}}" *ngIf="database">
<core-style [css]="database.csstemplate" prefix=".{{cssClass}}"></core-style>
<core-style [css]="database.csstemplate" prefix=".{{cssClass}}" />
<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" />
</form>
</div>
</core-loading>

View File

@ -1,12 +1,11 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
<ion-back-button [text]="'core.back' | translate" />
</ion-buttons>
<ion-title>
<h1>
<core-format-text [text]="title" contextLevel="module" [contextInstanceId]="moduleId" [courseId]="courseId">
</core-format-text>
<core-format-text [text]="title" contextLevel="module" [contextInstanceId]="moduleId" [courseId]="courseId" />
</h1>
</ion-title>
</ion-toolbar>
@ -14,57 +13,51 @@
<ion-content class="limited-width">
<ion-refresher slot="fixed" [disabled]="!entryLoaded || !(isPullingToRefresh || !renderingEntry && !loadingRating && !loadingComments)"
(ionRefresh)="refreshDatabase($event.target)">
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}" />
</ion-refresher>
<core-loading [hideUntil]="entryLoaded && (isPullingToRefresh || !renderingEntry && !loadingRating && !loadingComments)">
<!-- Database entries found to be synchronized -->
<ion-card class="core-warning-card" *ngIf="entry && entry.hasOffline">
<ion-item>
<ion-icon name="fas-triangle-exclamation" slot="start" aria-hidden="true"></ion-icon>
<ion-icon name="fas-triangle-exclamation" slot="start" aria-hidden="true" />
<ion-label>{{ 'core.hasdatatosync' | translate: {$a: moduleName} }}</ion-label>
</ion-item>
</ion-card>
<core-group-selector [groupInfo]="groupInfo" [(selected)]="selectedGroup" (selectedChange)="setGroup(selectedGroup)"
[courseId]="courseId">
</core-group-selector>
[courseId]="courseId" />
<div class="addon-data-contents addon-data-entry addon-data-entries-{{database.id}}" *ngIf="database && entry">
<core-style [css]="database.csstemplate" prefix=".addon-data-entries-{{database.id}}"></core-style>
<core-style [css]="database.csstemplate" prefix=".addon-data-entries-{{database.id}}" />
<core-compile-html [text]="entryHtml" [jsData]="jsData" [extraImports]="extraImports" (compiling)="setRenderingEntry($event)">
</core-compile-html>
<core-compile-html [text]="entryHtml" [jsData]="jsData" [extraImports]="extraImports" (compiling)="setRenderingEntry($event)" />
</div>
<core-rating-rate *ngIf="database && entry && ratingInfo && (!database.approval || entry.approved)" [ratingInfo]="ratingInfo"
contextLevel="module" [instanceId]="database.coursemodule" [itemId]="entry.id" [itemSetId]="0" [courseId]="courseId"
[aggregateMethod]="database.assessed" [scaleId]="database.scale" [userId]="entry.userid" (onLoading)="setLoadingRating($event)"
(onUpdate)="ratingUpdated()">
</core-rating-rate>
(onUpdate)="ratingUpdated()" />
<core-rating-aggregate *ngIf="database && entry && ratingInfo" [ratingInfo]="ratingInfo" contextLevel="module"
[instanceId]="database.coursemodule" [itemId]="entry.id" [courseId]="courseId" [aggregateMethod]="database.assessed"
[scaleId]="database.scale">
</core-rating-aggregate>
[scaleId]="database.scale" />
<core-comments *ngIf="database && database.comments && entry && entry.id > 0 && commentsEnabled" contextLevel="module"
[instanceId]="database.coursemodule" component="mod_data" [itemId]="entry.id" area="database_entry" [courseId]="courseId"
(onLoading)="setLoadingComments($event)" [showItem]="true">
</core-comments>
(onLoading)="setLoadingComments($event)" [showItem]="true" />
</core-loading>
<div collapsible-footer *ngIf="entryLoaded && hasPrevious || hasNext" slot="fixed" appearOnBottom>
<ion-row class="ion-justify-content-between ion-align-items-center ion-no-padding ion-wrap">
<ion-col class="ion-text-start ion-no-padding core-navigation-arrow" size="auto">
<ion-button [disabled]="!hasPrevious" fill="clear" [attr.aria-label]="'core.previous' | translate"
(click)="gotoEntry(offset! -1)">
<ion-icon name="fas-chevron-left" slot="icon-only" aria-hidden="true"></ion-icon>
<ion-icon name="fas-chevron-left" slot="icon-only" aria-hidden="true" />
</ion-button>
</ion-col>
<ion-col class="ion-text-center">
</ion-col>
<ion-col class="ion-text-center" />
<ion-col class="ion-text-end ion-no-padding core-navigation-arrow" size="auto">
<ion-button [disabled]="!hasNext" fill="clear" [attr.aria-label]=" 'core.next' | translate"
(click)="gotoEntry(offset! + 1)">
<ion-icon name="fas-chevron-right" slot="icon-only" aria-hidden="true"></ion-icon>
<ion-icon name="fas-chevron-right" slot="icon-only" aria-hidden="true" />
</ion-button>
</ion-col>
</ion-row>

View File

@ -1,25 +1,21 @@
<ion-header collapsible>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
<ion-back-button [text]="'core.back' | translate" />
</ion-buttons>
<ion-title>
<h1>
<core-format-text [text]="title" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
</core-format-text>
<core-format-text [text]="title" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId" />
</h1>
</ion-title>
<ion-buttons slot="end">
<!-- The buttons defined by the component will be added in here. -->
</ion-buttons>
<ion-buttons slot="end" />
</ion-toolbar>
</ion-header>
<ion-content class="limited-width">
<ion-refresher slot="fixed" [disabled]="activityComponent?.showLoading" (ionRefresh)="activityComponent?.doRefresh($event.target)">
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}" />
</ion-refresher>
<addon-mod-data-index [module]="module" [courseId]="courseId" [group]="group" (dataRetrieved)="updateData($event)">
</addon-mod-data-index>
<addon-mod-data-index [module]="module" [courseId]="courseId" [group]="group" (dataRetrieved)="updateData($event)" />
</ion-content>

View File

@ -22,7 +22,7 @@ import { AddonModDataEntryField,
AddonModDataSubfieldData,
} from './data';
import { CoreFormFields } from '@singletons/form';
import { FileEntry } from '@ionic-native/file/ngx';
import { FileEntry } from '@awesome-cordova-plugins/file/ngx';
import { CoreFileEntry } from '@services/file-helper';
import type { AddonModDataFieldPluginBaseComponent } from '@addons/mod/data/classes/base-field-plugin-component';

Some files were not shown because too many files have changed in this diff Show More