Merge pull request #2699 from crazyserver/MOBILE-3627

Mobile 3627
main
Dani Palou 2021-03-10 08:17:09 +01:00 committed by GitHub
commit b5b8e566ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
115 changed files with 1220 additions and 261 deletions

View File

@ -204,7 +204,7 @@ const appConfig = {
'no-fallthrough': 'off',
'no-invalid-this': 'error',
'no-irregular-whitespace': 'error',
'no-multiple-empty-lines': 'error',
'no-multiple-empty-lines': ['error', { "max": 1 }],
'no-new-wrappers': 'error',
'no-sequences': 'error',
'no-trailing-spaces': 'error',

244
package-lock.json generated
View File

@ -2039,9 +2039,9 @@
}
},
"@eslint/eslintrc": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.1.3.tgz",
"integrity": "sha512-4YVwPkANLeNtRjMekzux1ci8hIaH5eGKktGqR0d3LWsKNn5B2X/1Z6Trxy7jQXl9EBGE6Yj02O+t09FMeRllaA==",
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.0.tgz",
"integrity": "sha512-2ZPCc+uNbjV5ERJr+aKSPRwZgKd2z11x0EgLvb1PURmUrn9QNRXFqje0Ldq454PfAVyaJYyrDvvIKSFP4NnBog==",
"dev": true,
"requires": {
"ajv": "^6.12.4",
@ -2051,15 +2051,14 @@
"ignore": "^4.0.6",
"import-fresh": "^3.2.1",
"js-yaml": "^3.13.1",
"lodash": "^4.17.19",
"minimatch": "^3.0.4",
"strip-json-comments": "^3.1.1"
},
"dependencies": {
"debug": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz",
"integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==",
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
"integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
"dev": true,
"requires": {
"ms": "2.1.2"
@ -4915,9 +4914,9 @@
}
},
"astral-regex": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz",
"integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
"integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==",
"dev": true
},
"async": {
@ -8780,13 +8779,13 @@
}
},
"eslint": {
"version": "7.10.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-7.10.0.tgz",
"integrity": "sha512-BDVffmqWl7JJXqCjAK6lWtcQThZB/aP1HXSH1JKwGwv0LQEdvpR7qzNrUT487RM39B5goWuboFad5ovMBmD8yA==",
"version": "7.21.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-7.21.0.tgz",
"integrity": "sha512-W2aJbXpMNofUp0ztQaF40fveSsJBjlSCSWpy//gzfTvwC+USs/nceBrKmlJOiM8r1bLwP2EuYkCqArn/6QTIgg==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.0.0",
"@eslint/eslintrc": "^0.1.3",
"@babel/code-frame": "7.12.11",
"@eslint/eslintrc": "^0.4.0",
"ajv": "^6.10.0",
"chalk": "^4.0.0",
"cross-spawn": "^7.0.2",
@ -8795,11 +8794,11 @@
"enquirer": "^2.3.5",
"eslint-scope": "^5.1.1",
"eslint-utils": "^2.1.0",
"eslint-visitor-keys": "^1.3.0",
"espree": "^7.3.0",
"esquery": "^1.2.0",
"eslint-visitor-keys": "^2.0.0",
"espree": "^7.3.1",
"esquery": "^1.4.0",
"esutils": "^2.0.2",
"file-entry-cache": "^5.0.1",
"file-entry-cache": "^6.0.1",
"functional-red-black-tree": "^1.0.1",
"glob-parent": "^5.0.0",
"globals": "^12.1.0",
@ -8810,7 +8809,7 @@
"js-yaml": "^3.13.1",
"json-stable-stringify-without-jsonify": "^1.0.1",
"levn": "^0.4.1",
"lodash": "^4.17.19",
"lodash": "^4.17.20",
"minimatch": "^3.0.4",
"natural-compare": "^1.4.0",
"optionator": "^0.9.1",
@ -8819,11 +8818,20 @@
"semver": "^7.2.1",
"strip-ansi": "^6.0.0",
"strip-json-comments": "^3.1.0",
"table": "^5.2.3",
"table": "^6.0.4",
"text-table": "^0.2.0",
"v8-compile-cache": "^2.0.3"
},
"dependencies": {
"@babel/code-frame": {
"version": "7.12.11",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz",
"integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==",
"dev": true,
"requires": {
"@babel/highlight": "^7.10.4"
}
},
"ansi-regex": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
@ -8841,9 +8849,9 @@
}
},
"debug": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz",
"integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==",
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
"integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
"dev": true,
"requires": {
"ms": "2.1.2"
@ -8859,12 +8867,6 @@
"estraverse": "^4.1.1"
}
},
"eslint-visitor-keys": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
"integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
"dev": true
},
"globals": {
"version": "12.4.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz",
@ -9069,13 +9071,13 @@
"dev": true
},
"espree": {
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/espree/-/espree-7.3.0.tgz",
"integrity": "sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz",
"integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==",
"dev": true,
"requires": {
"acorn": "^7.4.0",
"acorn-jsx": "^5.2.0",
"acorn-jsx": "^5.3.1",
"eslint-visitor-keys": "^1.3.0"
},
"dependencies": {
@ -9100,9 +9102,9 @@
"dev": true
},
"esquery": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz",
"integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==",
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz",
"integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==",
"dev": true,
"requires": {
"estraverse": "^5.1.0"
@ -9548,12 +9550,12 @@
}
},
"file-entry-cache": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz",
"integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==",
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
"integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
"dev": true,
"requires": {
"flat-cache": "^2.0.1"
"flat-cache": "^3.0.4"
}
},
"file-loader": {
@ -9811,31 +9813,19 @@
"dev": true
},
"flat-cache": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz",
"integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==",
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
"integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==",
"dev": true,
"requires": {
"flatted": "^2.0.0",
"rimraf": "2.6.3",
"write": "1.0.3"
},
"dependencies": {
"rimraf": {
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
"integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
"dev": true,
"requires": {
"glob": "^7.1.3"
}
}
"flatted": "^3.1.0",
"rimraf": "^3.0.2"
}
},
"flatted": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz",
"integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==",
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz",
"integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==",
"dev": true
},
"flush-write-stream": {
@ -18084,6 +18074,12 @@
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
"dev": true
},
"require-from-string": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
"dev": true
},
"require-main-filename": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
@ -19109,38 +19105,20 @@
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="
},
"slice-ansi": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz",
"integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==",
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz",
"integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==",
"dev": true,
"requires": {
"ansi-styles": "^3.2.0",
"astral-regex": "^1.0.0",
"is-fullwidth-code-point": "^2.0.0"
"ansi-styles": "^4.0.0",
"astral-regex": "^2.0.0",
"is-fullwidth-code-point": "^3.0.0"
},
"dependencies": {
"ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dev": true,
"requires": {
"color-convert": "^1.9.0"
}
},
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"dev": true,
"requires": {
"color-name": "1.1.3"
}
},
"color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
"is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true
}
}
@ -20297,26 +20275,71 @@
"integrity": "sha512-3ozUwGSf5jmrhGgOXlX/O6hk1KQ28XPb7d3NiPZX267QmimuDq3TuIgnkw+vICUrGJGKWPLKmXVASnuJ3w07nw=="
},
"table": {
"version": "5.4.6",
"resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz",
"integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==",
"version": "6.0.7",
"resolved": "https://registry.npmjs.org/table/-/table-6.0.7.tgz",
"integrity": "sha512-rxZevLGTUzWna/qBLObOe16kB2RTnnbhciwgPbMMlazz1yZGVEgnZK762xyVdVznhqxrfCeBMmMkgOOaPwjH7g==",
"dev": true,
"requires": {
"ajv": "^6.10.2",
"lodash": "^4.17.14",
"slice-ansi": "^2.1.0",
"string-width": "^3.0.0"
"ajv": "^7.0.2",
"lodash": "^4.17.20",
"slice-ansi": "^4.0.0",
"string-width": "^4.2.0"
},
"dependencies": {
"string-width": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
"integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
"ajv": {
"version": "7.2.1",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-7.2.1.tgz",
"integrity": "sha512-+nu0HDv7kNSOua9apAVc979qd932rrZeb3WOvoiD31A/p1mIE5/9bN2027pE2rOPYEdS3UHzsvof4hY+lM9/WQ==",
"dev": true,
"requires": {
"emoji-regex": "^7.0.1",
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^5.1.0"
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2",
"uri-js": "^4.2.2"
}
},
"ansi-regex": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
"dev": true
},
"emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true
},
"is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true
},
"json-schema-traverse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"dev": true
},
"string-width": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz",
"integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==",
"dev": true,
"requires": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.0"
}
},
"strip-ansi": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
"dev": true,
"requires": {
"ansi-regex": "^5.0.0"
}
}
}
@ -21384,9 +21407,9 @@
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
},
"v8-compile-cache": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz",
"integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==",
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz",
"integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==",
"dev": true
},
"v8-to-istanbul": {
@ -22850,15 +22873,6 @@
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
},
"write": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz",
"integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==",
"dev": true,
"requires": {
"mkdirp": "^0.5.1"
}
},
"write-file-atomic": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz",

View File

@ -26,7 +26,7 @@
"test:ci": "NODE_ENV=testing gulp && jest -ci --runInBand --verbose",
"test:watch": "NODE_ENV=testing gulp watch & jest --watch",
"test:coverage": "NODE_ENV=testing gulp && jest --coverage",
"lint": "ng lint",
"lint": "NODE_OPTIONS=--max-old-space-size=4096 ng lint",
"ionic:serve:before": "gulp",
"ionic:serve": "gulp watch & ng serve",
"ionic:build:before": "gulp"
@ -138,7 +138,7 @@
"@types/webpack-env": "^1.16.0",
"@typescript-eslint/eslint-plugin": "4.3.0",
"@typescript-eslint/parser": "4.3.0",
"eslint": "^7.6.0",
"eslint": "^7.21.0",
"eslint-config-prettier": "^6.12.0",
"eslint-plugin-header": "^3.1.0",
"eslint-plugin-import": "^2.22.1",

View File

@ -26,11 +26,13 @@ import { AddonMessagesModule } from './messages/messages.module';
import { AddonModModule } from './mod/mod.module';
import { AddonQbehaviourModule } from './qbehaviour/qbehaviour.module';
import { AddonQtypeModule } from './qtype/qtype.module';
import { AddonBlogModule } from './blog/blog.module';
@NgModule({
imports: [
AddonBlockModule,
AddonBadgesModule,
AddonBlogModule,
AddonCalendarModule,
AddonMessagesModule,
AddonPrivateFilesModule,

View File

@ -20,7 +20,6 @@ import { CoreNavigator } from '@services/navigator';
import { makeSingleton } from '@singletons';
import { AddonBadges } from '../badges';
/**
* Handler to treat links to user participants page.
*/

View File

@ -41,7 +41,6 @@ export class AddonBlockRecentlyAccessedItemsComponent extends CoreBlockBaseCompo
super('AddonBlockRecentlyAccessedItemsComponent');
}
/**
* Perform the invalidate content function.
*

View File

@ -77,7 +77,6 @@ export class AddonBlockRecentlyAccessedItemsProvider {
}
export const AddonBlockRecentlyAccessedItems = makeSingleton(AddonBlockRecentlyAccessedItemsProvider);
/**
* Result of WS block_recentlyaccesseditems_get_recent_items.
*/

View File

@ -19,7 +19,6 @@ import { CoreCourseComponentsModule } from '@features/course/components/componen
import { AddonBlockSiteMainMenuComponent } from './sitemainmenu/sitemainmenu';
@NgModule({
declarations: [
AddonBlockSiteMainMenuComponent,

View File

@ -0,0 +1,52 @@
// (C) Copyright 2015 Moodle Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { Injector, NgModule } from '@angular/core';
import { RouterModule, ROUTES, Routes } from '@angular/router';
import { CoreSharedModule } from '@/core/shared.module';
import { AddonBlogEntriesPage } from './pages/entries/entries';
import { CoreCommentsComponentsModule } from '@features/comments/components/components.module';
import { CoreTagComponentsModule } from '@features/tag/components/components.module';
import { buildTabMainRoutes } from '@features/mainmenu/mainmenu-tab-routing.module';
function buildRoutes(injector: Injector): Routes {
return [
...buildTabMainRoutes(injector, {
component: AddonBlogEntriesPage,
}),
];
}
@NgModule({
imports: [
CoreSharedModule,
CoreCommentsComponentsModule,
CoreTagComponentsModule,
],
exports: [RouterModule],
providers: [
{
provide: ROUTES,
multi: true,
deps: [Injector],
useFactory: buildRoutes,
},
],
declarations: [
AddonBlogEntriesPage,
],
})
export class AddonBlogLazyModule {}

View File

@ -0,0 +1,65 @@
// (C) Copyright 2015 Moodle Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { APP_INITIALIZER, NgModule, Type } from '@angular/core';
import { Routes } from '@angular/router';
import { CoreContentLinksDelegate } from '@features/contentlinks/services/contentlinks-delegate';
import { CoreCourseIndexRoutingModule } from '@features/course/pages/index/index-routing.module';
import { CoreCourseOptionsDelegate } from '@features/course/services/course-options-delegate';
import { CoreMainMenuRoutingModule } from '@features/mainmenu/mainmenu-routing.module';
import { CoreMainMenuTabRoutingModule } from '@features/mainmenu/mainmenu-tab-routing.module';
import { CoreMainMenuDelegate } from '@features/mainmenu/services/mainmenu-delegate';
import { CoreTagAreaDelegate } from '@features/tag/services/tag-area-delegate';
import { CoreUserDelegate } from '@features/user/services/user-delegate';
import { AddonBlogProvider } from './services/blog';
import { AddonBlogCourseOptionHandler } from './services/handlers/course-option';
import { AddonBlogIndexLinkHandler } from './services/handlers/index-link';
import { AddonBlogMainMenuHandler, AddonBlogMainMenuHandlerService } from './services/handlers/mainmenu';
import { AddonBlogTagAreaHandler } from './services/handlers/tag-area';
import { AddonBlogUserHandler } from './services/handlers/user';
export const ADDON_BLOG_SERVICES: Type<unknown>[] = [
AddonBlogProvider,
];
const routes: Routes = [
{
path: AddonBlogMainMenuHandlerService.PAGE_NAME,
loadChildren: () => import('@addons/blog/blog-lazy.module').then(m => m.AddonBlogLazyModule),
},
];
@NgModule({
imports: [
CoreMainMenuTabRoutingModule.forChild(routes),
CoreMainMenuRoutingModule.forChild({ children: routes }),
CoreCourseIndexRoutingModule.forChild({ children: routes }),
],
exports: [CoreMainMenuRoutingModule],
providers: [
{
provide: APP_INITIALIZER,
multi: true,
deps: [],
useFactory: () => async () => {
CoreContentLinksDelegate.registerHandler(AddonBlogIndexLinkHandler.instance);
CoreMainMenuDelegate.registerHandler(AddonBlogMainMenuHandler.instance);
CoreUserDelegate.registerHandler(AddonBlogUserHandler.instance);
CoreTagAreaDelegate.registerHandler(AddonBlogTagAreaHandler.instance);
CoreCourseOptionsDelegate.registerHandler(AddonBlogCourseOptionHandler.instance);
},
},
],
})
export class AddonBlogModule {}

View File

@ -0,0 +1,12 @@
{
"blog": "Blog",
"blogentries": "Blog entries",
"errorloadentries": "Error loading blog entries.",
"linktooriginalentry": "Link to original blog entry",
"noentriesyet": "No visible entries here",
"publishtonoone": "Yourself (draft)",
"publishtosite": "Anyone on this site",
"publishtoworld": "Anyone in the world",
"showonlyyourentries": "Show only your entries",
"siteblogheading": "Site blog"
}

View File

@ -0,0 +1,82 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button [attr.aria-label]="'core.back' | translate"></ion-back-button>
</ion-buttons>
<ion-title>{{ title | translate }}</ion-title>
<ion-buttons slot="end"></ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="refresh($event)">
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
</ion-refresher>
<core-loading [hideUntil]="loaded" class="core-loading-center">
<ion-item *ngIf="showMyEntriesToggle">
<ion-label>{{ 'addon.blog.showonlyyourentries' | translate }}</ion-label>
<ion-toggle [(ngModel)]="onlyMyEntries" (ionChange)="onlyMyEntriesToggleChanged(onlyMyEntries)"></ion-toggle>
</ion-item>
<core-empty-box *ngIf="entries && entries.length == 0" icon="far-newspaper"
[message]="'addon.blog.noentriesyet' | translate">
</core-empty-box>
<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>
<ion-label>
<h2>
<core-format-text [text]="entry.subject" [contextLevel]="contextLevel"
[contextInstanceId]="contextInstanceId">
</core-format-text>
<ion-note class="ion-float-end ion-padding-left ion-text-end">
{{ 'addon.blog.' + entry.publishTranslated! | translate}}
</ion-note>
</h2>
<p>
<ion-note class="ion-float-end ion-padding-left ion-text-end">
{{entry.created | coreDateDayOrTime}}
</ion-note>
{{entry.user && entry.user!.fullname}}
</p>
</ion-label>
</ion-item>
<ion-card-content>
<ion-item>
<ion-label>
<core-format-text [text]="entry.summary" [component]="this.component" [componentId]="entry.id"
[contextLevel]="contextLevel" [contextInstanceId]="contextInstanceId">
</core-format-text>
</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>
</ion-label>
</ion-item>
<ion-item *ngIf="commentsEnabled" detail>
<ion-label>
<core-comments [component]="this.component" [itemId]="entry.id" area="format_blog"
[instanceId]="entry.userid" contextLevel="user">
</core-comments>
</ion-label>
</ion-item>
<core-file *ngFor="let file of entry.attachmentfiles" [file]="file" [component]="this.component"
[componentId]="entry.id">
</core-file>
<ion-item *ngIf="entry.uniquehash" [href]="entry.uniquehash" core-link>
<ion-label>{{ 'addon.blog.linktooriginalentry' | translate }}</ion-label>
</ion-item>
</ion-card-content>
<ion-row class="ion-text-center">
<ion-col *ngIf="entry.lastmodified > entry.created">
<ion-note>
<ion-icon name="fas-clock"></ion-icon> {{entry.lastmodified | coreTimeAgo}}
</ion-note>
</ion-col>
</ion-row>
</ion-card>
</ng-container>
<core-infinite-loading [enabled]="canLoadMore" (action)="loadMore($event)" [error]="loadMoreError"></core-infinite-loading>
</core-loading>
</ion-content>

View File

@ -0,0 +1,288 @@
// (C) Copyright 2015 Moodle Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { ContextLevel } from '@/core/constants';
import { AddonBlog, AddonBlogFilter, AddonBlogPost, AddonBlogProvider } from '@addons/blog/services/blog';
import { Component, OnInit } from '@angular/core';
import { CoreComments } from '@features/comments/services/comments';
import { CoreTag } from '@features/tag/services/tag';
import { CoreUser, CoreUserProfile } from '@features/user/services/user';
import { IonRefresher } from '@ionic/angular';
import { CoreNavigator } from '@services/navigator';
import { CoreSites } from '@services/sites';
import { CoreDomUtils } from '@services/utils/dom';
import { CoreTextUtils } from '@services/utils/text';
import { CoreUtils } from '@services/utils/utils';
/**
* Page that displays the list of blog entries.
*/
@Component({
selector: 'page-addon-blog-entries',
templateUrl: 'entries.html',
})
export class AddonBlogEntriesPage implements OnInit {
title = '';
protected filter: AddonBlogFilter = {};
protected pageLoaded = 0;
protected userPageLoaded = 0;
protected canLoadMoreEntries = false;
protected canLoadMoreUserEntries = true;
protected siteHomeId: number;
loaded = false;
canLoadMore = false;
loadMoreError = false;
entries: AddonBlogPostFormatted[] = [];
currentUserId: number;
showMyEntriesToggle = false;
onlyMyEntries = false;
component = AddonBlogProvider.COMPONENT;
commentsEnabled = false;
tagsEnabled = false;
contextLevel: ContextLevel = ContextLevel.SYSTEM;
contextInstanceId = 0;
constructor() {
this.currentUserId = CoreSites.getCurrentSiteUserId();
this.siteHomeId = CoreSites.getCurrentSiteHomeId();
}
/**
* View loaded.
*/
async ngOnInit(): Promise<void> {
const userId = CoreNavigator.getRouteNumberParam('userId');
const courseId = CoreNavigator.getRouteNumberParam('courseId');
const cmId = CoreNavigator.getRouteNumberParam('cmId');
const entryId = CoreNavigator.getRouteNumberParam('entryId');
const groupId = CoreNavigator.getRouteNumberParam('groupId');
const tagId = CoreNavigator.getRouteNumberParam('tagId');
if (!userId && !courseId && !cmId && !entryId && !groupId && !tagId) {
this.title = 'addon.blog.siteblogheading';
} else {
this.title = 'addon.blog.blogentries';
}
if (userId) {
this.filter.userid = userId;
}
this.showMyEntriesToggle = !userId;
if (courseId) {
this.filter.courseid = courseId;
}
if (cmId) {
this.filter.cmid = cmId;
}
if (entryId) {
this.filter.entryid = entryId;
}
if (groupId) {
this.filter.groupid = groupId;
}
if (tagId) {
this.filter.tagid = tagId;
}
// Calculate the context level.
if (userId && !courseId && !cmId) {
this.contextLevel = ContextLevel.USER;
this.contextInstanceId = userId;
} else if (courseId && courseId != this.siteHomeId) {
this.contextLevel = ContextLevel.COURSE;
this.contextInstanceId = courseId;
} else {
this.contextLevel = ContextLevel.SYSTEM;
this.contextInstanceId = 0;
}
this.commentsEnabled = !CoreComments.areCommentsDisabledInSite();
this.tagsEnabled = CoreTag.areTagsAvailableInSite();
await this.fetchEntries();
CoreUtils.ignoreErrors(AddonBlog.logView(this.filter));
}
/**
* Fetch blog entries.
*
* @param refresh Empty events array first.
* @return Promise with the entries.
*/
protected async fetchEntries(refresh: boolean = false): Promise<void> {
this.loadMoreError = false;
if (refresh) {
this.pageLoaded = 0;
this.userPageLoaded = 0;
}
const loadPage = this.onlyMyEntries ? this.userPageLoaded : this.pageLoaded;
try {
const result = await AddonBlog.getEntries(this.filter, loadPage);
const promises = result.entries.map(async (entry: AddonBlogPostFormatted) => {
switch (entry.publishstate) {
case 'draft':
entry.publishTranslated = 'publishtonoone';
break;
case 'site':
entry.publishTranslated = 'publishtosite';
break;
case 'public':
entry.publishTranslated = 'publishtoworld';
break;
default:
entry.publishTranslated = 'privacy:unknown';
break;
}
// Calculate the context. This code was inspired by calendar events, Moodle doesn't do this for blogs.
if (entry.moduleid || entry.coursemoduleid) {
entry.contextLevel = ContextLevel.MODULE;
entry.contextInstanceId = entry.moduleid || entry.coursemoduleid;
} else if (entry.courseid) {
entry.contextLevel = ContextLevel.COURSE;
entry.contextInstanceId = entry.courseid;
} else {
entry.contextLevel = ContextLevel.USER;
entry.contextInstanceId = entry.userid;
}
entry.summary = CoreTextUtils.instance.replacePluginfileUrls(entry.summary, entry.summaryfiles || []);
return CoreUser.getProfile(entry.userid, entry.courseid, true).then((user) => {
entry.user = user;
return;
}).catch(() => {
// Ignore errors.
});
});
if (refresh) {
this.entries = result.entries;
} else {
this.entries = CoreUtils.uniqueArray(this.entries
.concat(result.entries), 'id')
.sort((a, b) => b.created - a.created);
}
if (this.onlyMyEntries) {
const count = this.entries.filter((entry) => entry.userid == this.currentUserId).length;
this.canLoadMoreUserEntries = result.totalentries > count;
this.canLoadMore = this.canLoadMoreUserEntries;
this.userPageLoaded++;
} else {
this.canLoadMoreEntries = result.totalentries > this.entries.length;
this.canLoadMore = this.canLoadMoreEntries;
this.pageLoaded++;
}
await Promise.all(promises);
} catch (error) {
CoreDomUtils.showErrorModalDefault(error, 'addon.blog.errorloadentries', true);
this.loadMoreError = true; // Set to prevent infinite calls with infinite-loading.
} finally {
this.loaded = true;
}
}
/**
* Toggle between showing only my entries or not.
*
* @param enabled If true, filter my entries. False otherwise.
*/
onlyMyEntriesToggleChanged(enabled: boolean): void {
this.canLoadMore = enabled ? this.canLoadMoreUserEntries : this.canLoadMoreEntries;
if (!enabled) {
delete this.filter.userid;
return;
}
const count = this.entries.filter((entry) => entry.userid == this.currentUserId).length;
this.userPageLoaded = Math.floor(count / AddonBlogProvider.ENTRIES_PER_PAGE);
this.filter.userid = this.currentUserId;
if (count == 0 && this.canLoadMoreUserEntries) {
// First time but no entry loaded. Try to load some.
this.loadMore();
}
}
/**
* Function to load more entries.
*
* @param infiniteComplete Infinite scroll complete function. Only used from core-infinite-loading.
* @return Resolved when done.
*/
loadMore(infiniteComplete?: () => void): Promise<void> {
return this.fetchEntries().finally(() => {
infiniteComplete && infiniteComplete();
});
}
/**
* Refresh blog entries on PTR.
*
* @param refresher Refresher instance.
*/
refresh(refresher?: CustomEvent<IonRefresher>): void {
const promises = this.entries.map((entry) =>
CoreComments.invalidateCommentsData('user', entry.userid, this.component, entry.id, 'format_blog'));
promises.push(AddonBlog.invalidateEntries(this.filter));
if (this.showMyEntriesToggle) {
this.filter['userid'] = this.currentUserId;
promises.push(AddonBlog.invalidateEntries(this.filter));
if (!this.onlyMyEntries) {
delete this.filter['userid'];
}
}
CoreUtils.allPromises(promises).finally(() => {
this.fetchEntries(true).finally(() => {
if (refresher) {
refresher?.detail.complete();
}
});
});
}
}
/**
* Blog post with some calculated data.
*/
type AddonBlogPostFormatted = AddonBlogPost & {
publishTranslated?: string; // Calculated in the app. Key of the string to translate the publish state of the post.
user?: CoreUserProfile; // Calculated in the app. Data of the user that wrote the post.
contextLevel?: string; // Calculated in the app. The context level of the entry.
contextInstanceId?: number; // Calculated in the app. The context instance id.
};

View File

@ -0,0 +1,204 @@
// (C) Copyright 2015 Moodle Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { Injectable } from '@angular/core';
import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
import { CorePushNotifications } from '@features/pushnotifications/services/pushnotifications';
import { CoreTagItem } from '@features/tag/services/tag';
import { CoreSites } from '@services/sites';
import { CoreUtils } from '@services/utils/utils';
import { CoreStatusWithWarningsWSResponse, CoreWSExternalFile, CoreWSExternalWarning } from '@services/ws';
import { makeSingleton } from '@singletons';
const ROOT_CACHE_KEY = 'addonBlog:';
/**
* Service to handle blog entries.
*/
@Injectable({ providedIn: 'root' })
export class AddonBlogProvider {
static readonly ENTRIES_PER_PAGE = 10;
static readonly COMPONENT = 'blog';
/**
* Returns whether or not the blog plugin is enabled for a certain site.
*
* This method is called quite often and thus should only perform a quick
* check, we should not be calling WS from here.
*
* @param siteId Site ID. If not defined, current site.
* @return Promise resolved with true if enabled, resolved with false or rejected otherwise.
*/
async isPluginEnabled(siteId?: string): Promise<boolean> {
const site = await CoreSites.getSite(siteId);
return site.wsAvailable('core_blog_get_entries') &&site.canUseAdvancedFeature('enableblogs');
}
/**
* Get the cache key for the blog entries.
*
* @param filter Filter to apply on search.
* @return Cache key.
*/
getEntriesCacheKey(filter: AddonBlogFilter = {}): string {
return ROOT_CACHE_KEY + CoreUtils.sortAndStringify(filter);
}
/**
* Get blog entries.
*
* @param filter Filter to apply on search.
* @param page Page of the blog entries to fetch.
* @param siteId Site ID. If not defined, current site.
* @return Promise to be resolved when the entries are retrieved.
*/
async getEntries(filter: AddonBlogFilter = {}, page: number = 0, siteId?: string): Promise<CoreBlogGetEntriesWSResponse> {
const site = await CoreSites.getSite(siteId);
const data: CoreBlogGetEntriesWSParams = {
filters: CoreUtils.objectToArrayOfObjects(filter, 'name', 'value'),
page: page,
perpage: AddonBlogProvider.ENTRIES_PER_PAGE,
};
const preSets: CoreSiteWSPreSets = {
cacheKey: this.getEntriesCacheKey(filter),
updateFrequency: CoreSite.FREQUENCY_SOMETIMES,
};
return site.read('core_blog_get_entries', data, preSets);
}
/**
* Invalidate blog entries WS call.
*
* @param filter Filter to apply on search
* @param siteId Site ID. If not defined, current site.
* @return Promise resolved when data is invalidated.
*/
async invalidateEntries(filter: AddonBlogFilter = {}, siteId?: string): Promise<void> {
const site = await CoreSites.getSite(siteId);
await site.invalidateWsCacheForKey(this.getEntriesCacheKey(filter));
}
/**
* Trigger the blog_entries_viewed event.
*
* @param filter Filter to apply on search.
* @param siteId Site ID. If not defined, current site.
* @return Promise to be resolved when done.
*/
async logView(filter: AddonBlogFilter = {}, siteId?: string): Promise<CoreStatusWithWarningsWSResponse> {
CorePushNotifications.logViewListEvent('blog', 'core_blog_view_entries', filter, siteId);
const site = await CoreSites.getSite(siteId);
const data: AddonBlogViewEntriesWSParams = {
filters: CoreUtils.objectToArrayOfObjects(filter, 'name', 'value'),
};
return site.write('core_blog_view_entries', data);
}
}
export const AddonBlog = makeSingleton(AddonBlogProvider);
/**
* Params of core_blog_get_entries WS.
*/
type CoreBlogGetEntriesWSParams = {
filters?: { // Parameters to filter blog listings.
name: string; // The expected keys (value format) are:
// tag PARAM_NOTAGS blog tag
// tagid PARAM_INT blog tag id
// userid PARAM_INT blog author (userid)
// cmid PARAM_INT course module id
// entryid PARAM_INT entry id
// groupid PARAM_INT group id
// courseid PARAM_INT course id
// search PARAM_RAW search term.
value: string; // The value of the filter.
}[];
page?: number; // The blog page to return.
perpage?: number; // The number of posts to return per page.
};
/**
* Data returned by core_blog_get_entries WS.
*/
export type CoreBlogGetEntriesWSResponse = {
entries: AddonBlogPost[];
totalentries: number; // The total number of entries found.
warnings?: CoreWSExternalWarning[];
};
/**
* Data returned by blog's post_exporter.
*/
export type AddonBlogPost = {
id: number; // Post/entry id.
module: string; // Where it was published the post (blog, blog_external...).
userid: number; // Post author.
courseid: number; // Course where the post was created.
groupid: number; // Group post was created for.
moduleid: number; // Module id where the post was created (not used anymore).
coursemoduleid: number; // Course module id where the post was created.
subject: string; // Post subject.
summary: string; // Post summary.
summaryformat?: number; // Summary format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN).
content: string; // Post content.
uniquehash: string; // Post unique hash.
rating: number; // Post rating.
format: number; // Post content format.
attachment: string; // Post atachment.
publishstate: string; // Post publish state.
lastmodified: number; // When it was last modified.
created: number; // When it was created.
usermodified: number; // User that updated the post.
summaryfiles: CoreWSExternalFile[]; // Summaryfiles.
attachmentfiles?: CoreWSExternalFile[]; // Attachmentfiles.
tags?: CoreTagItem[]; // @since 3.7. Tags.
};
/**
* Params of core_blog_view_entries WS.
*/
type AddonBlogViewEntriesWSParams = {
filters?: { // Parameters used in the filter of view_entries.
name: string; // The expected keys (value format) are:
// tag PARAM_NOTAGS blog tag
// tagid PARAM_INT blog tag id
// userid PARAM_INT blog author (userid)
// cmid PARAM_INT course module id
// entryid PARAM_INT entry id
// groupid PARAM_INT group id
// courseid PARAM_INT course id
// search PARAM_RAW search term.
value: string; // The value of the filter.
}[];
};
export type AddonBlogFilter = {
tag?: string; // Blog tag
tagid?: number; // Blog tag id
userid?: number; // Blog author (userid)
cmid?: number; // Course module id
entryid?: number; // Entry id
groupid?: number; // Group id
courseid?: number; // Course id
search?: string; // Search term.
};

View File

@ -0,0 +1,109 @@
// (C) Copyright 2015 Moodle Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { Injectable } from '@angular/core';
import { CoreCourse } from '@features/course/services/course';
import { CoreCourseHelper } from '@features/course/services/course-helper';
import {
CoreCourseAccess,
CoreCourseOptionsHandler,
CoreCourseOptionsHandlerData,
} from '@features/course/services/course-options-delegate';
import { CoreCourseUserAdminOrNavOptionIndexed } from '@features/courses/services/courses';
import { CoreEnrolledCourseDataWithExtraInfoAndOptions } from '@features/courses/services/courses-helper';
import { CoreFilepool } from '@services/filepool';
import { CoreSites } from '@services/sites';
import { CoreWSExternalFile } from '@services/ws';
import { makeSingleton } from '@singletons';
import { AddonBlog } from '../blog';
import { AddonBlogMainMenuHandlerService } from './mainmenu';
/**
* Course nav handler.
*/
@Injectable({ providedIn: 'root' })
export class AddonBlogCourseOptionHandlerService implements CoreCourseOptionsHandler {
name = 'AddonBlog';
priority = 100;
/**
* @inheritdoc
*/
invalidateEnabledForCourse(courseId: number): Promise<void> {
return CoreCourse.invalidateCourseBlocks(courseId);
}
/**
* @inheritdoc
*/
isEnabled(): Promise<boolean> {
return AddonBlog.isPluginEnabled();
}
/**
* @inheritdoc
*/
async isEnabledForCourse(
courseId: number,
accessData: CoreCourseAccess,
navOptions?: CoreCourseUserAdminOrNavOptionIndexed,
): Promise<boolean> {
const enabled = await CoreCourseHelper.hasABlockNamed(courseId, 'blog_menu');
if (enabled && navOptions && typeof navOptions.blogs != 'undefined') {
return navOptions.blogs;
}
return enabled;
}
/**
* @inheritdoc
*/
getDisplayData(): CoreCourseOptionsHandlerData | Promise<CoreCourseOptionsHandlerData> {
return {
title: 'addon.blog.blog',
class: 'addon-blog-handler',
page: AddonBlogMainMenuHandlerService.PAGE_NAME,
};
}
/**
* @inheritdoc
*/
async prefetch(course: CoreEnrolledCourseDataWithExtraInfoAndOptions): Promise<void> {
const siteId = CoreSites.getCurrentSiteId();
const result = await AddonBlog.getEntries({ courseid: course.id });
await Promise.all(result.entries.map(async (entry) => {
let files: CoreWSExternalFile[] = [];
if (entry.attachmentfiles && entry.attachmentfiles.length) {
files = entry.attachmentfiles;
}
if (entry.summaryfiles && entry.summaryfiles.length) {
files = files.concat(entry.summaryfiles);
}
if (files.length > 0) {
await CoreFilepool.addFilesToQueue(siteId, files, entry.module, entry.id);
}
}));
}
}
export const AddonBlogCourseOptionHandler = makeSingleton(AddonBlogCourseOptionHandlerService);

View File

@ -0,0 +1,61 @@
// (C) Copyright 2015 Moodle Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { Injectable } from '@angular/core';
import { Params } from '@angular/router';
import { CoreContentLinksHandlerBase } from '@features/contentlinks/classes/base-handler';
import { CoreContentLinksAction } from '@features/contentlinks/services/contentlinks-delegate';
import { CoreNavigator } from '@services/navigator';
import { makeSingleton } from '@singletons';
import { AddonBlog } from '../blog';
/**
* Handler to treat links to blog page.
*/
@Injectable({ providedIn: 'root' })
export class AddonBlogIndexLinkHandlerService extends CoreContentLinksHandlerBase {
name = 'AddonBlogIndexLinkHandler';
featureName = 'CoreUserDelegate_AddonBlog:blogs';
pattern = /\/blog\/index\.php/;
/**
* @inheritdoc
*/
getActions(siteIds: string[], url: string, params: Record<string, string>): CoreContentLinksAction[] {
const pageParams: Params = {};
params.userid ? pageParams['userId'] = parseInt(params.userid, 10) : null;
params.modid ? pageParams['cmId'] = parseInt(params.modid, 10) : null;
params.courseid ? pageParams['courseId'] = parseInt(params.courseid, 10) : null;
params.entryid ? pageParams['entryId'] = parseInt(params.entryid, 10) : null;
params.groupid ? pageParams['groupId'] = parseInt(params.groupid, 10) : null;
params.tagid ? pageParams['tagId'] = parseInt(params.tagid, 10) : null;
return [{
action: (siteId: string): void => {
CoreNavigator.navigateToSitePath('/blog', { params: pageParams, siteId });
},
}];
}
/**
* @inheritdoc
*/
isEnabled(siteId: string): Promise<boolean> {
return AddonBlog.isPluginEnabled(siteId);
}
}
export const AddonBlogIndexLinkHandler = makeSingleton(AddonBlogIndexLinkHandlerService);

View File

@ -0,0 +1,51 @@
// (C) Copyright 2015 Moodle Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { Injectable } from '@angular/core';
import { CoreMainMenuHandler, CoreMainMenuHandlerData } from '@features/mainmenu/services/mainmenu-delegate';
import { makeSingleton } from '@singletons';
import { AddonBlog } from '../blog';
/**
* Handler to inject an option into main menu.
*/
@Injectable({ providedIn: 'root' })
export class AddonBlogMainMenuHandlerService implements CoreMainMenuHandler {
static readonly PAGE_NAME = 'blog';
name = 'AddonBlog';
priority = 450;
/**
* @inheritdoc
*/
async isEnabled(): Promise<boolean> {
return AddonBlog.isPluginEnabled();
}
/**
* @inheritdoc
*/
getDisplayData(): CoreMainMenuHandlerData {
return {
icon: 'far-newspaper',
title: 'addon.blog.siteblogheading',
page: AddonBlogMainMenuHandlerService.PAGE_NAME,
class: 'addon-blog-handler',
};
}
}
export const AddonBlogMainMenuHandler = makeSingleton(AddonBlogMainMenuHandlerService);

View File

@ -0,0 +1,53 @@
// (C) Copyright 2015 Moodle Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { Injectable, Type } from '@angular/core';
import { CoreTagFeedComponent } from '@features/tag/components/feed/feed';
import { CoreTagAreaHandler } from '@features/tag/services/tag-area-delegate';
import { CoreTagFeedElement, CoreTagHelper } from '@features/tag/services/tag-helper';
import { makeSingleton } from '@singletons';
import { AddonBlog } from '../blog';
/**
* Handler to support tags.
*/
@Injectable({ providedIn: 'root' })
export class AddonBlogTagAreaHandlerService implements CoreTagAreaHandler {
name = 'AddonBlogTagAreaHandler';
type = 'core/post';
/**
* @inheritdoc
*/
isEnabled(): Promise<boolean> {
return AddonBlog.isPluginEnabled();
}
/**
* @inheritdoc
*/
async parseContent(content: string): Promise<CoreTagFeedElement[]> {
return CoreTagHelper.parseFeedContent(content);
}
/**
* @inheritdoc
*/
getComponent(): Type<unknown> | Promise<Type<unknown>> {
return CoreTagFeedComponent;
}
}
export const AddonBlogTagAreaHandler = makeSingleton(AddonBlogTagAreaHandlerService);

View File

@ -0,0 +1,64 @@
// (C) Copyright 2015 Moodle Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { Injectable } from '@angular/core';
import { CoreUserProfileHandler, CoreUserProfileHandlerData, CoreUserDelegateService } from '@features/user/services/user-delegate';
import { CoreNavigator } from '@services/navigator';
import { makeSingleton } from '@singletons';
import { AddonBlog } from '../blog';
/**
* Profile item handler.
*/
@Injectable({ providedIn: 'root' })
export class AddonBlogUserHandlerService implements CoreUserProfileHandler {
name = 'AddonBlog:blogs';
priority = 300;
type = CoreUserDelegateService.TYPE_NEW_PAGE;
/**
* @inheritdoc
*/
isEnabled(): Promise<boolean> {
return AddonBlog.isPluginEnabled();
}
/**
* @inheritdoc
*/
async isEnabledForUser(): Promise<boolean> {
return true;
}
/**
* @inheritdoc
*/
getDisplayData(): CoreUserProfileHandlerData {
return {
icon: 'far-newspaper',
title: 'addon.blog.blogentries',
class: 'addon-blog-handler',
action: (event, user, courseId): void => {
event.preventDefault();
event.stopPropagation();
CoreNavigator.navigateToSitePath('/blog', {
params: { courseId, userId: user.id },
});
},
};
}
}
export const AddonBlogUserHandler = makeSingleton(AddonBlogUserHandlerService);

View File

@ -10,6 +10,7 @@
.addon-calendar-months {
background-color: var(--contrast-background);
padding: 0;
font-size: 14px;
}
.addon-calendar-day {

View File

@ -139,7 +139,6 @@ export class AddonCalendarCalendarComponent implements OnInit, DoCheck, OnDestro
this.fetchData();
}
/**
* Detect and act upon changes that Angular cant or wont detect on its own (objects and arrays).
*/

View File

@ -29,7 +29,6 @@ import { AddonCalendarFilter, AddonCalendarEventIcons } from '../../services/cal
})
export class AddonCalendarFilterPopoverComponent implements OnInit {
@Input() filter: AddonCalendarFilter = {
filtered: false,
courseId: -1,
@ -54,7 +53,6 @@ export class AddonCalendarFilterPopoverComponent implements OnInit {
this.types.push(value);
});
}
/**

View File

@ -266,7 +266,6 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave {
// Ignore errors.
});
if (this.showAll) {
// Remove site home from the list of courses.
const siteHomeId = CoreSites.getCurrentSiteHomeId();
@ -285,7 +284,6 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave {
await Promise.all((courses as CoreEnrolledCourseData[]).map(courseFillterFullname));
}
// Sort courses by name.
this.courses = courses.sort((a, b) => {
const compareA = a.fullname.toLowerCase();

View File

@ -34,7 +34,6 @@ import { CoreNavigator } from '@services/navigator';
import { CoreLocalNotifications } from '@services/local-notifications';
import { CoreConstants } from '@/core/constants';
/**
* Page that displays the calendar events.
*/

View File

@ -47,7 +47,6 @@ const routes: Routes = [
...conditionalRoutes(tabletRoutes, () => CoreScreen.isTablet),
];
@NgModule({
imports: [
RouterModule.forChild(routes),

View File

@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

View File

@ -55,7 +55,6 @@ export class AddonCalendarHelperProvider {
protected eventTypeIcons: string[] = [];
/**
* Returns event icon based on event type.
*

View File

@ -722,7 +722,6 @@ export class AddonCalendarProvider {
const originalEvent = record as AddonCalendarGetEventsEvent;
const recordAsRecord = record as AddonCalendarEventDBRecord;
// Calculate data to match the new WS.
eventConverted.descriptionformat = originalEvent.format;
eventConverted.iscourseevent = originalEvent.eventtype == AddonCalendarEventType.COURSE;
@ -947,7 +946,6 @@ export class AddonCalendarProvider {
params.events!.courseids = courses.map((course) => course.id);
params.events!.courseids.push(site.getSiteHomeId()); // Add front page.
return;
}));
@ -1941,7 +1939,6 @@ export type AddonCalendarCalendarDay = {
rarrow: string; // Rarrow.
};
/**
* Params of core_calendar_get_calendar_monthly_view WS.
*/
@ -2035,7 +2032,6 @@ export type AddonCalendarDayName = {
fullname: string; // Fullname.
};
/**
* Params of core_calendar_get_calendar_upcoming_view WS.
*/
@ -2090,7 +2086,6 @@ export type AddonCalendarGetAllowedEventTypesWSResponse = {
warnings?: CoreWSExternalWarning[];
};
/**
* Params of core_calendar_get_calendar_events WS.
*/
@ -2187,7 +2182,6 @@ type AddonCalendarGetCalendarDayViewWSParams = {
categoryid?: number; // Category being viewed.
};
/**
* Params of core_calendar_submit_create_update_form WS.
*/

View File

@ -25,7 +25,6 @@ export class AddonCalendarMainMenuHandlerService implements CoreMainMenuHandler
static readonly PAGE_NAME = 'calendar';
name = 'AddonCalendar';
priority = 900;

View File

@ -18,7 +18,6 @@ import { CoreSharedModule } from '@/core/shared.module';
import { AddonMessagesConversationInfoComponent } from './conversation-info/conversation-info';
@NgModule({
declarations: [
AddonMessagesConversationInfoComponent,

View File

@ -11,12 +11,12 @@
}
}
.note {
position: absolute;
top: 0;
right: 0;
margin: 4px 8px;
font-size: 1.3rem;
ion-note {
ion-badge {
margin-left: 6px;
margin-right: 6px;
vertical-align: middle;
}
}
.addon-message-last-message {
@ -43,6 +43,11 @@
margin-top: 10px;
}
}
ion-item-divider ion-badge {
margin-left: 16px;
margin-right: 16px;
}
}
:host-context([dir=rtl]) {
@ -53,11 +58,6 @@
margin-left: 0;
}
.note {
left: 0;
right: unset;
}
.addon-message-last-message-user {
margin-left: 2px;
margin-right: 0;

View File

@ -62,7 +62,6 @@ export class AddonMessagesContacts35Page implements OnInit, OnDestroy {
searchString = '';
constructor(
protected route: ActivatedRoute,
) {

View File

@ -45,7 +45,6 @@ const routes: Routes = [
...conditionalRoutes(tabletRoutes, () => CoreScreen.isTablet),
];
@NgModule({
imports: [
RouterModule.forChild(routes),

View File

@ -58,7 +58,6 @@ export class AddonMessagesContactsPage implements OnInit, OnDestroy {
protected contactRequestsCountObserver: CoreEventObserver;
protected memberInfoObserver: CoreEventObserver;
constructor() {
this.siteId = CoreSites.getCurrentSiteId();

View File

@ -326,7 +326,6 @@ export class AddonMessagesDiscussionPage implements OnInit, OnDestroy, AfterView
return;
}));
}
// Fetch the messages for the first time.
@ -596,7 +595,6 @@ export class AddonMessagesDiscussionPage implements OnInit, OnDestroy, AfterView
}
}
// Retrieve the conversation. Invalidate data first to get the right unreadcount.
await AddonMessages.invalidateConversation(conversationId!);
@ -1191,7 +1189,6 @@ export class AddonMessagesDiscussionPage implements OnInit, OnDestroy, AfterView
data = await AddonMessages.sendMessage(this.userId!, text);
}
this.messagesBeingSent--;
let failure = false;
if (data.sent) {

View File

@ -124,7 +124,6 @@ export class AddonMessagesDiscussions35Page implements OnInit, OnDestroy {
this.refreshData();
});
// If a message push notification is received, refresh the view.
this.pushObserver = CorePushNotificationsDelegate.on<CorePushNotificationsNotificationBasicData>('receive')
.subscribe((notification) => {

View File

@ -123,10 +123,6 @@
<ion-icon *ngIf="conversation.ismuted" name="fas-volume-mute"
[title]="'addon.messages.mutedconversation' | translate"></ion-icon>
</h2>
<ion-note *ngIf="conversation.lastmessagedate > 0 || conversation.unreadcount">
<ion-badge *ngIf="conversation.unreadcount > 0">{{ conversation.unreadcount }}</ion-badge>
<span *ngIf="conversation.lastmessagedate > 0">{{conversation.lastmessagedate | coreDateDayOrTime}}</span>
</ion-note>
<p *ngIf="conversation.subname"><core-format-text [text]="conversation.subname" contextLevel="system"
[contextInstanceId]="0"></core-format-text></p>
<p class="addon-message-last-message">
@ -139,5 +135,9 @@
class="addon-message-last-message-text" contextLevel="system" [contextInstanceId]="0"></core-format-text>
</p>
</ion-label>
<ion-note *ngIf="conversation.lastmessagedate > 0 || conversation.unreadcount" slot="end">
<ion-badge *ngIf="conversation.unreadcount > 0">{{ conversation.unreadcount }}</ion-badge>
<span *ngIf="conversation.lastmessagedate > 0">{{conversation.lastmessagedate | coreDateDayOrTime}}</span>
</ion-note>
</ion-item>
</ng-template>

View File

@ -29,7 +29,6 @@ export class AddonMessagesIndexLinkHandlerService extends CoreContentLinksHandle
name = 'AddonMessagesIndexLinkHandler';
pattern = /\/message\/index\.php((?![?&](id|user1|user2)=\d+).)*$/;
/**
* Get the list of actions for a link (url).
*

View File

@ -106,7 +106,6 @@ export class AddonMessagesOfflineProvider {
),
]);
const messageResult:
AddonMessagesOfflineAnyMessagesFormatted[] =
this.parseMessages(messages);
@ -379,6 +378,5 @@ export type AddonMessagesOfflineConversationMessagesDBRecordFormatted =
useridfrom?: number; // User Id who send the message, will be likely us.
};
export type AddonMessagesOfflineAnyMessagesFormatted =
AddonMessagesOfflineConversationMessagesDBRecordFormatted | AddonMessagesOfflineMessagesDBRecordFormatted;

View File

@ -1413,7 +1413,6 @@ export class AddonMessagesProvider {
return AddonMessagesMainMenuHandlerService.PAGE_NAME + ( enabled ? '/group-conversations' : '');
}
/**
* Get messages according to the params.
*
@ -1749,7 +1748,6 @@ export class AddonMessagesProvider {
const site = await CoreSites.getSite(siteId);
userId = userId || site.getUserId();
await site.invalidateWsCacheForKey(this.getCacheKeyForConversationBetweenUsers(userId, otherUserId));
}
@ -3065,7 +3063,6 @@ export type AddonMessagesConversationMessageFormatted =
showTail?: boolean; // Calculated in the app. Whether to show a "tail" in the message.
};
/**
* Data returned by core_message_get_user_message_preferences WS.
*/
@ -3559,7 +3556,6 @@ export type AddonMessagesGetUserContactsWSResponse = {
}[];
}[];
/**
* Params of core_message_get_contact_requests WS.
*/

View File

@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { Component, Input, ViewChild, ElementRef } from '@angular/core';
import { CoreSites } from '@services/sites';
import { CoreDomUtils } from '@services/utils/dom';

View File

@ -275,7 +275,6 @@ export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityCo
cmId: this.module!.id,
});
this.summary = submissionStatus.gradingsummary;
if (!this.summary) {
this.needsGradingAvalaible = false;

View File

@ -1190,7 +1190,6 @@ type AddonModAssignSubmissionFeedbackFormatted = AddonModAssignSubmissionFeedbac
advancedgrade?: boolean; // Calculated in the app. Whether it uses advanced grading.
};
type AddonModAssignSubmissionGrade = {
method: string;
grade?: number | string;

View File

@ -346,7 +346,6 @@ export class AddonModAssignSubmissionListPage implements AfterViewInit, OnDestro
}
/**
* Helper class to manage submissions.
*/

View File

@ -46,7 +46,6 @@ export class AddonModAssignSubmissionReviewPage implements OnInit, CanLeave {
protected blindMarking = false; // Whether it uses blind marking.
protected forceLeave = false; // To allow leaving the page without checking for changes.
constructor(
protected route: ActivatedRoute,
) { }

View File

@ -212,7 +212,6 @@ export class AddonModAssignHelperProvider {
return false;
}
// If all the plugins were empty (or there were no plugins), we consider the submission to be empty.
return true;
}
@ -709,7 +708,6 @@ export class AddonModAssignHelperProvider {
}
export const AddonModAssignHelper = makeSingleton(AddonModAssignHelperProvider);
/**
* Assign submission with some calculated data.
*/

View File

@ -290,7 +290,6 @@ export class AddonModAssignOfflineProvider {
const promises:
Promise<AddonModAssignSubmissionsDBRecordFormatted[] | AddonModAssignSubmissionsGradingDBRecordFormatted[]>[] = [];
promises.push(this.getAssignSubmissions(assignId, siteId));
promises.push(this.getAssignSubmissionsGrade(assignId, siteId));

View File

@ -178,7 +178,6 @@ export class AddonModAssignSyncProvider extends CoreCourseActivitySyncBaseProvid
throw new CoreSyncBlockedError(Translate.instant('core.errorsyncblocked', { $a: this.componentTranslate }));
}
this.logger.debug('Try to sync assign ' + assignId + ' in site ' + siteId);
const syncPromise = this.performSyncAssign(assignId, siteId);
@ -553,7 +552,6 @@ export type AddonModAssignSyncResult = {
gradesBlocked: number[]; // Whether some grade couldn't be synced because it was blocked. UserId fields of the blocked grade.
};
/**
* Data passed to AUTO_SYNCED event.
*/

View File

@ -761,7 +761,6 @@ export class AddonModAssignProvider {
promises.push(this.invalidateAssignmentData(courseId, siteId));
promises.push(CoreGrades.invalidateAllCourseGradesData(courseId));
await Promise.all(promises);
}
@ -1562,7 +1561,6 @@ export type AddonModAssignSubmissionFeedback = {
plugins?: AddonModAssignPlugin[]; // Plugins info.
};
/**
* Params of mod_assign_list_participants WS.
*/
@ -1692,7 +1690,6 @@ type AddonModAssignGetSubmissionStatusWSParams = {
groupid?: number; // Filter by users in group (used for generating the grading summary). Empty or 0 for all groups information.
};
/**
* Result of WS mod_assign_get_submission_status.
*/
@ -1812,7 +1809,6 @@ type AddonModAssignSubmitGradingFormWSParams = {
jsonformdata: string; // The data from the grading form, encoded as a json array.
};
/**
* Params of mod_assign_save_grade WS.
*/

View File

@ -49,7 +49,6 @@ export interface AddonModAssignFeedbackHandler extends CoreDelegateHandler {
*/
getComponent?(plugin: AddonModAssignPlugin): Type<unknown> | undefined | Promise<Type<unknown> | undefined>;
/**
* Return the draft saved data of the feedback plugin.
*

View File

@ -29,7 +29,6 @@ export class AddonModAssignSubmissionCommentsHandlerService implements AddonModA
name = 'AddonModAssignSubmissionCommentsHandler';
type = 'comments';
/**
* Whether the plugin can be edited in offline for existing submissions. In general, this should return false if the
* plugin uses Moodle filters. The reason is that the app only prefetches filtered data, and the user should edit

View File

@ -22,7 +22,6 @@ import { CoreTextUtils } from '@services/utils/text';
import { CoreUtils } from '@services/utils/utils';
import { AddonModAssignSubmissionOnlineTextPluginData } from '../services/handler';
/**
* Component to render an onlinetext submission plugin.
*/
@ -74,7 +73,6 @@ export class AddonModAssignSubmissionOnlineTextComponent extends AddonModAssignS
this.text = AddonModAssign.getSubmissionPluginText(this.plugin);
}
// Set the text.
if (!this.edit) {
// Not editing, see full text when clicked.

View File

@ -100,7 +100,6 @@ export class AddonModBookIndexComponent extends CoreCourseModuleMainResourceComp
// @todo leaveAnimation: 'core-modal-lateral-transition',
});
await modal.present();
const result = await modal.onDidDismiss();

View File

@ -48,7 +48,6 @@ export const enum AddonModBookNavStyle {
const ROOT_CACHE_KEY = 'mmaModBook:';
/**
* Service that provides some features for books.
*/

View File

@ -160,7 +160,6 @@ export class AddonModFolderProvider {
}
export const AddonModFolder = makeSingleton(AddonModFolderProvider);
/**
* Folder returned by mod_folder_get_folders_by_courses.
*/

View File

@ -8,7 +8,7 @@
[priority]="800" [content]="'core.moduleintro' | translate" (action)="expandDescription()" [iconAction]="'arrow-forward'">
</core-context-menu-item>
<core-context-menu-item *ngIf="blog"
[priority]="750" content="{{'addon.blog.blog' | translate}}" [iconAction]="'fa-newspaper-o'" (action)="gotoBlog()">
[priority]="750" content="{{'addon.blog.blog' | translate}}" [iconAction]="'far-newspaper'" (action)="gotoBlog()">
</core-context-menu-item>
<core-context-menu-item *ngIf="discussions.loaded && !(hasOffline || hasOfflineRatings) && isOnline"
[priority]="700" [content]="'addon.mod_forum.refreshdiscussions' | translate" [iconAction]="refreshIcon" [closeOnClick]="false"

View File

@ -55,9 +55,8 @@
flex-grow: 1;
}
.addon-mod-forum-discussion-more-info {
font-size: 1.4rem;
clear: both;
.addon-mod-forum-discussion-more-info.ios {
font-size: 0.9rem;
}
}

View File

@ -1928,7 +1928,6 @@ export type AddonModForumDeletePostWSParams = {
*/
export type AddonModForumDeletePostWSResponse = CoreStatusWithWarningsWSResponse;
/**
* Params of mod_forum_get_discussion_post WS.
*/
@ -1944,7 +1943,6 @@ export type AddonModForumGetDiscussionPostWSResponse = {
warnings?: CoreWSExternalWarning[];
};
/**
* Params of mod_forum_get_discussion_posts WS.
*/

View File

@ -46,7 +46,6 @@ export class AddonModImscpIndexComponent extends CoreCourseModuleMainResourceCom
previousItem = '';
nextItem = '';
constructor(@Optional() courseContentsPage?: CoreCourseContentsPage) {
super('AddonModImscpIndexComponent', courseContentsPage);
}

View File

@ -343,7 +343,6 @@ export class AddonModImscpProvider {
}
export const AddonModImscp = makeSingleton(AddonModImscpProvider);
/**
* Params of mod_imscp_view_imscp WS.
*/
@ -387,7 +386,6 @@ type AddonModImscpGetImscpsByCoursesWSResponse = {
warnings?: CoreWSExternalWarning[];
};
export type AddonModImscpTocItem = {
href: string;
title: string;

View File

@ -165,7 +165,6 @@ export class AddonModLabelProvider {
}
export const AddonModLabel = makeSingleton(AddonModLabelProvider);
/**
* Label returned by mod_label_get_labels_by_courses.
*/

View File

@ -19,7 +19,6 @@ import { CoreSites } from '@services/sites';
import { CoreDomUtils } from '@services/utils/dom';
import { ModalController } from '@singletons';
/**
* Modal that asks the password for a lesson.
*/

View File

@ -175,7 +175,6 @@ export class AddonModPageProvider {
export const AddonModPage = makeSingleton(AddonModPageProvider);
/**
* Page returned by mod_page_get_pages_by_courses.
*/

View File

@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { CoreSharedModule } from '@/core/shared.module';
import { APP_INITIALIZER, NgModule } from '@angular/core';

View File

@ -251,7 +251,6 @@ export class AddonModResourceModuleHandlerService implements CoreCourseModuleHan
}
export const AddonModResourceModuleHandler = makeSingleton(AddonModResourceModuleHandlerService);
type AddonResourceHandlerData = {
icon: string;
extra: string;

View File

@ -258,7 +258,6 @@ type AddonModUrlViewUrlWSParams = {
urlid: number; // Url instance id.
};
/**
* URL returnd by mod_url_get_urls_by_courses.
*/

View File

@ -13,9 +13,7 @@
// limitations under the License.
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { IonicModule } from '@ionic/angular';
import { TranslateModule } from '@ngx-translate/core';
import { CoreSharedModule } from '@/core/shared.module';
import { AddonNotificationsActionsComponent } from './actions/actions';
@ -24,9 +22,7 @@ import { AddonNotificationsActionsComponent } from './actions/actions';
AddonNotificationsActionsComponent,
],
imports: [
CommonModule,
IonicModule,
TranslateModule.forChild(),
CoreSharedModule,
],
exports: [
AddonNotificationsActionsComponent,

View File

@ -176,7 +176,6 @@ export class AddonQbehaviourDeferredFeedbackHandlerService implements CoreQuesti
export const AddonQbehaviourDeferredFeedbackHandler = makeSingleton(AddonQbehaviourDeferredFeedbackHandlerService);
/**
* Check if a response is complete.
*

View File

@ -871,7 +871,6 @@ export class AddonQtypeDdMarkerQuestion {
}
/**
* Encapsulates operations on dd area.
*/

View File

@ -17,7 +17,6 @@ import { APP_INITIALIZER, NgModule } from '@angular/core';
import { CoreQuestionDelegate } from '@features/question/services/question-delegate';
import { AddonQtypeNumericalHandler } from './services/handlers/numerical';
@NgModule({
declarations: [
],

View File

@ -128,7 +128,6 @@ export class CoreAttachmentsComponent implements OnInit {
*/
async delete(index: number, askConfirm?: boolean): Promise<void> {
if (askConfirm) {
try {
await CoreDomUtils.showDeleteConfirm('core.confirmdeletefile');

View File

@ -44,7 +44,6 @@ export class CoreContextMenuComponent implements OnInit, OnDestroy {
protected instanceId: string;
protected parentContextMenu?: CoreContextMenuComponent;
constructor(
elementRef: ElementRef,
) {

View File

@ -77,6 +77,10 @@ export class CoreInfiniteLoadingComponent implements OnChanges {
// Calculate distance from edge.
const content = this.element.nativeElement.closest('ion-content') as IonContent;
if (!content) {
return;
}
const scrollElement = await content.getScrollElement();
const infiniteHeight = this.element.nativeElement.getBoundingClientRect().height;

View File

@ -70,7 +70,7 @@
--menu-border-width: 0;
--menu-box-shadow: none;
--menu-z: 0;
--selected-item-border-width: 0;
--selected-item-color: transparent;
}
:host(.content-only) {

View File

@ -518,7 +518,6 @@ export type CoreCommentsDataWithUser = CoreCommentsData & {
deleted?: boolean;
};
export type CoreCommentsOfflineWithUser = CoreCommentsDBRecord & {
profileimageurl?: string;
fullname?: string;

View File

@ -322,7 +322,6 @@ export type CoreCommentsSyncResult = {
updated: boolean; // Whether some data was sent to the server or offline data was updated.
};
/**
* Data passed to AUTO_SYNCED event.
*/

View File

@ -530,7 +530,6 @@ export type CoreCommentsArea = {
canpostorhascomments: boolean; // Canpostorhascomments.
};
/**
* Params of core_comment_add_comments WS.
*/

View File

@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

View File

@ -32,7 +32,6 @@ export class CoreCourseModuleMainActivityPage<ActivityType extends CoreCourseMod
module!: CoreCourseAnyModuleData;
courseId!: number;
/**
* Component being initialized.
*/

View File

@ -13,9 +13,13 @@
// limitations under the License.
import { CoreConstants } from '@/core/constants';
import { AddonBlog } from '@addons/blog/services/blog';
import { AddonBlogMainMenuHandlerService } from '@addons/blog/services/handlers/mainmenu';
import { OnInit, OnDestroy, Input, Output, EventEmitter, Component, Optional, Inject } from '@angular/core';
import { Params } from '@angular/router';
import { IonRefresher } from '@ionic/angular';
import { CoreApp } from '@services/app';
import { CoreNavigator } from '@services/navigator';
import { CoreSites } from '@services/sites';
import { CoreDomUtils } from '@services/utils/dom';
@ -90,7 +94,7 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy,
this.componentId = this.module?.id;
this.externalUrl = this.module?.url;
this.courseId = this.courseId || this.module?.course;
// @todo this.blog = await this.blogProvider.isPluginEnabled();
this.blog = await AddonBlog.isPluginEnabled();
}
/**
@ -223,8 +227,9 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy,
* Go to blog posts.
*/
async gotoBlog(): Promise<void> {
// const params: Params = { cmId: this.module?.id };
// @todo return CoreNavigator.navigateToSitePath('AddonBlogEntriesPage', { params });
const params: Params = { cmId: this.module?.id };
CoreNavigator.navigateToSitePath(AddonBlogMainMenuHandlerService.PAGE_NAME, { params });
}
/**

View File

@ -208,7 +208,6 @@ export class CoreCourseProvider {
preSets.emergencyCache = false;
}
const data = await site.read<CoreCourseCompletionActivityStatusWSResponse>(
'core_completion_get_activities_completion_status',
params,
@ -551,7 +550,6 @@ export class CoreCourseProvider {
return grade;
}
}
/**

View File

@ -508,7 +508,6 @@ export class CoreCourseModulePrefetchDelegateService extends CoreDelegate<CoreCo
return downloadedSize;
}
const cachedSize = await site.getComponentCacheSize(handler.component, module.id);
return cachedSize + downloadedSize;

View File

@ -146,7 +146,6 @@ export class CoreCourseSyncProvider extends CoreSyncBaseProvider<CoreCourseSyncR
<CoreCourseManualCompletionDBRecord[]> [],
);
if (!completions || !completions.length) {
// Nothing to sync, set sync time.
await this.setSyncTime(courseId, siteId);

View File

@ -20,7 +20,6 @@ import { CoreCoursesComponentsModule } from '../../components/components.module'
import { CoreCoursesAvailableCoursesPage } from './available-courses';
const routes: Routes = [
{
path: '',

View File

@ -20,7 +20,6 @@ import { CoreCoursesComponentsModule } from '../../components/components.module'
import { CoreCoursesCategoriesPage } from './categories';
const routes: Routes = [
{
path: '',

View File

@ -138,7 +138,6 @@ export class CoreCoursesDashboardPage implements OnInit, OnDestroy {
});
}
/**
* Toggle download enabled.
*/
@ -179,5 +178,4 @@ export class CoreCoursesDashboardPage implements OnInit, OnDestroy {
this.updateSiteObserver?.off();
}
}

View File

@ -122,7 +122,6 @@ export class CoreCoursesDashboardProvider {
export const CoreCoursesDashboard = makeSingleton(CoreCoursesDashboardProvider);
/**
* Params of core_block_get_dashboard_blocks WS.
*/

View File

@ -25,7 +25,6 @@ import { CoreUtils } from '@services/utils/utils';
import { makeSingleton } from '@singletons';
import { CoreCourses } from '../courses';
/**
* Handler for course request push notifications clicks.
*/

View File

@ -370,7 +370,6 @@ export class CoreEmulatorCaptureMediaComponent implements OnInit, OnDestroy {
const height = this.streamVideo?.nativeElement.videoHeight;
const loadingModal = await CoreDomUtils.showModalLoading();
this.imgCanvas.nativeElement.width = width;
this.imgCanvas.nativeElement.height = height;
this.imgCanvas.nativeElement.getContext('2d').drawImage(this.streamVideo?.nativeElement, 0, 0, width, height);

View File

@ -51,7 +51,6 @@ export class CoreEmulatorHelperProvider {
return;
}));
return CoreUtils.allPromises(promises);
}

View File

@ -29,6 +29,7 @@
height: 100%;
flex-direction: column;
ion-tab-button {
display: contents;
width: 100%;
ion-badge {
top: calc(50% - 20px);

View File

@ -109,7 +109,6 @@ export const SITE_SCHEMA: CoreSiteSchema = {
],
};
/**
* Data stored in DB for badge.
*/

View File

@ -128,7 +128,6 @@ export class CoreQuestionComponent implements OnInit {
return;
}
// Load local answers if offline is enabled.
if (this.offlineEnabled && this.component && this.attemptId) {
await CoreQuestionHelper.loadLocalAnswers(this.question, this.component, this.attemptId);

View File

@ -28,7 +28,6 @@ import {
QUESTION_TABLE_NAME,
} from './database/question';
const QUESTION_PREFIX_REGEX = /q\d+:(\d+)_/;
const STATES: Record<string, CoreQuestionState> = {
todo: {

View File

@ -76,7 +76,6 @@ export const RATINGS_SITE_SCHEMA: CoreSiteSchema = {
],
};
/**
* Primary data to identify a stored rating.
*/

View File

@ -25,7 +25,6 @@ import { CoreEvents } from '@singletons/events';
import { CoreRating } from './rating';
import { CoreRatingItemSet, CoreRatingOffline } from './rating-offline';
/**
* Service to sync ratings.
*/

View File

@ -513,7 +513,6 @@ export type CoreRatingItemRating = {
timemodified: number; // Time modified (timestamp).
};
/**
* Params of core_rating_get_item_ratings WS.
*/
@ -535,7 +534,6 @@ export type CoreRatingGetItemRatingsWSResponse = {
warnings?: CoreWSExternalWarning[];
};
/**
* Params of core_rating_add_rating WS.
*/

View File

@ -17,7 +17,6 @@ import { NgModule } from '@angular/core';
import { CoreSharedModule } from '@/core/shared.module';
import { CoreSearchBoxComponent } from './search-box/search-box';
@NgModule({
declarations: [
CoreSearchBoxComponent,

View File

@ -159,7 +159,6 @@ export class CoreSearchBoxComponent implements OnInit {
if (!this.formElement) {
this.formElement = event.detail.target.closest('form');
this.formElement?.addEventListener('blur', () => {
// Wait the new element to be focused.
setTimeout(() => {

View File

@ -94,7 +94,6 @@ export class CoreSettingsGeneralPage {
selected: value === this.selectedZoomLevel,
}));
this.richTextEditor = await CoreConfig.get(CoreConstants.SETTINGS_RICH_TEXT_EDITOR, true);
this.debugDisplay = await CoreConfig.get(CoreConstants.SETTINGS_DEBUG_DISPLAY, false);

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