diff --git a/.github/workflows/close-pull-requests.yml b/.github/workflows/close-pull-requests.yml index eb439f8380a3a..839af0b02422c 100644 --- a/.github/workflows/close-pull-requests.yml +++ b/.github/workflows/close-pull-requests.yml @@ -16,6 +16,7 @@ jobs: with: process-only: prs close-pr: true + skip-closed-pr-comment: true pr-comment: > **Please, don't open pull requests via GitHub.** diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index cf983d5606f08..dd1a04048c748 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -16,7 +16,7 @@ on: default: '' env: - php: 8.2 + php: 8.3 jobs: Grunt: @@ -68,7 +68,7 @@ jobs: db: mysqli # PostgreSQL builds always run with the highest PHP supported version. - os: ubuntu-22.04 - php: 8.2 + php: 8.3 db: pgsql steps: diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index d2777d00268f5..4179ff8ad8d1a 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -8,7 +8,7 @@ on: required: false default: '' env: - php: 8.2 + php: 8.3 jobs: Grunt: @@ -51,7 +51,7 @@ jobs: matrix: include: - os: windows-latest - php: 8.2 + php: 8.3 # Ideally we should use mysql/mariadb, but they are 4x slower without tweaks and configuration # so let's run only postgres (1.5h vs 6h) only, If some day we want to improve the mysql runs, # this is the place to enable them. diff --git a/.grunt/tasks/javascript.js b/.grunt/tasks/javascript.js index 34b57bc755b95..23c9caab58149 100644 --- a/.grunt/tasks/javascript.js +++ b/.grunt/tasks/javascript.js @@ -21,7 +21,7 @@ */ /** - * Function to generate the destination for the uglify task + * Function to generate the destination for the minification task * (e.g. build/file.min.js). This function will be passed to * the rename property of files array when building dynamically: * http://gruntjs.com/configuring-tasks#building-the-files-object-dynamically @@ -60,7 +60,6 @@ module.exports = grunt => { grunt.registerTask('js', ['amd', 'yui']); // Register NPM tasks. - grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-contrib-watch'); grunt.loadNpmTasks('grunt-rollup'); @@ -104,6 +103,7 @@ module.exports = grunt => { // The queue runner will run the next `size` items in the queue. const runQueue = (size = 1) => { queue.splice(0, size).forEach(resolve => { + grunt.log.debug(`Item resolved. Kicking off next one.`); resolve(); }); }; @@ -113,15 +113,17 @@ module.exports = grunt => { // The options hook is run in parallel. // We can return an unresolved Promise which is queued for later resolution. - options: async() => { + options: async(options) => { return new Promise(resolve => { queue.push(resolve); startQueue(); + return options; }); }, // When an item in the queue completes, start the next item in the queue. - buildEnd: () => { + generateBundle: (options, bundle) => { + grunt.log.debug(`Finished output phase for ${Object.keys(bundle).join(', ')}`); runQueue(); }, }; @@ -155,16 +157,6 @@ module.exports = grunt => { ], presets: [ ['@babel/preset-env', { - targets: { - browsers: [ - ">0.3%", - "last 2 versions", - "not ie >= 0", - "not op_mini all", - "not Opera > 0", - "not dead" - ] - }, modules: false, useBuiltIns: false }] diff --git a/.grunt/tasks/jsdoc.js b/.grunt/tasks/jsdoc.js index e2f0680d17beb..e4ea713e76caf 100644 --- a/.grunt/tasks/jsdoc.js +++ b/.grunt/tasks/jsdoc.js @@ -20,17 +20,33 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -module.exports = grunt => { - // Project configuration. - grunt.config.merge({ - jsdoc: { - dist: { - options: { - configure: ".grunt/jsdoc/jsdoc.conf.js", - }, - }, - }, - }); +module.exports = (grunt) => { + const path = require('path'); + + grunt.registerTask('jsdoc', 'Generate JavaScript documentation using jsdoc', function() { + const done = this.async(); + const configuration = path.resolve('.grunt/jsdoc/jsdoc.conf.js'); - grunt.loadNpmTasks('grunt-jsdoc'); + grunt.util.spawn({ + cmd: 'jsdoc', + args: [ + '--configure', + configuration, + ] + }, function(error, result, code) { + if (result.stdout) { + grunt.log.write(result.stdout); + } + + if (result.stderr) { + grunt.log.error(result.stderr); + } + if (error) { + grunt.fail.fatal(`JSDoc failed with error code ${code}`); + } else { + grunt.log.write('JSDoc completed successfully'.green); + } + done(); + }); + }); }; diff --git a/.grunt/tasks/stylelint.js b/.grunt/tasks/stylelint.js index 0891d4231418f..864e46151d150 100644 --- a/.grunt/tasks/stylelint.js +++ b/.grunt/tasks/stylelint.js @@ -29,6 +29,7 @@ module.exports = grunt => { // Use a fully-qualified path. src: files, options: { + quietDeprecationWarnings: true, configOverrides: { rules: { // These rules have to be disabled in .stylelintrc for scss compat. @@ -45,7 +46,10 @@ module.exports = grunt => { return { stylelint: { scss: { - options: {syntax: 'scss'}, + options: { + quietDeprecationWarnings: true, + customSyntax: 'postcss-scss', + }, src: files, }, }, diff --git a/.nvmrc b/.nvmrc index 53d838af2152f..9de2256827aef 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -lts/gallium +lts/iron diff --git a/.stylelintrc b/.stylelintrc index ae818b0bf39a3..c3c4a4ad730c0 100644 --- a/.stylelintrc +++ b/.stylelintrc @@ -1,9 +1,14 @@ { + "customSyntax": "postcss-scss", "plugins": [ "stylelint-csstree-validator" ], "rules": { - "csstree/validator": true, + "csstree/validator": { + "syntaxExtensions": [ + "sass" + ] + }, "at-rule-empty-line-before": [ "always", {"except": [ "blockless-after-blockless"], ignore: ["after-comment", "inside-block"]} ], @@ -45,7 +50,7 @@ "function-name-case": "lower", "function-parentheses-newline-inside": "always-multi-line", "function-parentheses-space-inside": "never-single-line", - "function-url-scheme-blacklist": ["data"], + "function-url-scheme-disallowed-list": ["data"], "function-whitespace-after": "always", "indentation": 4, "keyframe-declaration-no-important": true, @@ -84,12 +89,19 @@ "selector-type-no-unknown": true, "string-no-newline": true, "time-min-milliseconds": 100, - "unit-blacklist": ["pt"], + "unit-disallowed-list": ["pt"], "unit-case": "lower", "unit-no-unknown": true, "value-keyword-case": ["lower", {"ignoreKeywords": ["/(@|$)/"]}], "value-list-comma-newline-after": "always-multi-line", "value-list-comma-space-after": "always-single-line", "value-list-comma-space-before": "never", - } + }, + "overrides": [ + { + "files": ["**/yui/**/*.css"], + "rules": { + } + } + ] } diff --git a/admin/amd/build/bulk_user_actions.min.js b/admin/amd/build/bulk_user_actions.min.js new file mode 100644 index 0000000000000..df8e7b09bb03a --- /dev/null +++ b/admin/amd/build/bulk_user_actions.min.js @@ -0,0 +1,10 @@ +define("core_admin/bulk_user_actions",["exports","core_reportbuilder/local/selectors","core_table/local/dynamic/events","core_form/changechecker","core/custom_interaction_events","jquery"],(function(_exports,reportSelectors,tableEvents,FormChangeChecker,CustomEvents,_jquery){var obj;function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj} +/** + * Add bulk actions to the users list report + * + * @module core_admin/bulk_user_actions + * @copyright 2024 Marina Glancy + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,reportSelectors=_interopRequireWildcard(reportSelectors),tableEvents=_interopRequireWildcard(tableEvents),FormChangeChecker=_interopRequireWildcard(FormChangeChecker),CustomEvents=_interopRequireWildcard(CustomEvents),_jquery=(obj=_jquery)&&obj.__esModule?obj:{default:obj};const Selectors_bulkActionsForm="form#user-bulk-action-form",Selectors_userReportWrapper='[data-region="report-user-list-wrapper"]',Selectors_checkbox='input[type="checkbox"][data-togglegroup="report-select-all"][data-toggle="slave"]',Selectors_masterCheckbox='input[type="checkbox"][data-togglegroup="report-select-all"][data-toggle="master"]',Selectors_checkedRows='[data-togglegroup="report-select-all"][data-toggle="slave"]:checked';_exports.init=()=>{var _userBulkForm$closest;const userBulkForm=document.querySelector(Selectors_bulkActionsForm),userReport=null==userBulkForm||null===(_userBulkForm$closest=userBulkForm.closest(Selectors_userReportWrapper))||void 0===_userBulkForm$closest?void 0:_userBulkForm$closest.querySelector(reportSelectors.regions.report);if(!userBulkForm||!userReport)return;const actionSelect=userBulkForm.querySelector("select");CustomEvents.define(actionSelect,[CustomEvents.events.accessibleChange]),(0,_jquery.default)(actionSelect).on(CustomEvents.events.accessibleChange,(event=>{if(event.target.value&&"0"!=="".concat(event.target.value)){const e=new Event("submit",{cancelable:!0});userBulkForm.dispatchEvent(e),e.defaultPrevented||(FormChangeChecker.markFormSubmitted(userBulkForm),userBulkForm.submit())}}));const updateUserIds=()=>{const selectedUsers=[...userReport.querySelectorAll(Selectors_checkedRows)],selectedUserIds=selectedUsers.map((check=>parseInt(check.value)));userBulkForm.querySelector('[name="userids"]').value=selectedUserIds.join(","),actionSelect.disabled=0===selectedUsers.length;const selectedUsersNames=selectedUsers.map((check=>document.querySelector('label[for="'.concat(check.id,'"]')).textContent));userBulkForm.data={userids:selectedUserIds,usernames:selectedUsersNames}};updateUserIds(),document.addEventListener("change",(event=>{(event.target.matches(Selectors_checkbox)||event.target.matches(Selectors_masterCheckbox))&&userReport.contains(event.target)&&updateUserIds()})),document.addEventListener(tableEvents.tableContentRefreshed,(event=>{userReport.contains(event.target)&&updateUserIds()}))}})); + +//# sourceMappingURL=bulk_user_actions.min.js.map \ No newline at end of file diff --git a/admin/amd/build/bulk_user_actions.min.js.map b/admin/amd/build/bulk_user_actions.min.js.map new file mode 100644 index 0000000000000..84606bc2486b2 --- /dev/null +++ b/admin/amd/build/bulk_user_actions.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"bulk_user_actions.min.js","sources":["../src/bulk_user_actions.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Add bulk actions to the users list report\n *\n * @module core_admin/bulk_user_actions\n * @copyright 2024 Marina Glancy\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport * as reportSelectors from 'core_reportbuilder/local/selectors';\nimport * as tableEvents from 'core_table/local/dynamic/events';\nimport * as FormChangeChecker from 'core_form/changechecker';\nimport * as CustomEvents from 'core/custom_interaction_events';\nimport jQuery from 'jquery';\n\nconst Selectors = {\n bulkActionsForm: 'form#user-bulk-action-form',\n userReportWrapper: '[data-region=\"report-user-list-wrapper\"]',\n checkbox: 'input[type=\"checkbox\"][data-togglegroup=\"report-select-all\"][data-toggle=\"slave\"]',\n masterCheckbox: 'input[type=\"checkbox\"][data-togglegroup=\"report-select-all\"][data-toggle=\"master\"]',\n checkedRows: '[data-togglegroup=\"report-select-all\"][data-toggle=\"slave\"]:checked',\n};\n\n/**\n * Initialise module\n */\nexport const init = () => {\n\n const userBulkForm = document.querySelector(Selectors.bulkActionsForm);\n const userReport = userBulkForm?.closest(Selectors.userReportWrapper)?.querySelector(reportSelectors.regions.report);\n if (!userBulkForm || !userReport) {\n return;\n }\n const actionSelect = userBulkForm.querySelector('select');\n CustomEvents.define(actionSelect, [CustomEvents.events.accessibleChange]);\n\n jQuery(actionSelect).on(CustomEvents.events.accessibleChange, event => {\n if (event.target.value && `${event.target.value}` !== \"0\") {\n const e = new Event('submit', {cancelable: true});\n userBulkForm.dispatchEvent(e);\n if (!e.defaultPrevented) {\n FormChangeChecker.markFormSubmitted(userBulkForm);\n userBulkForm.submit();\n }\n }\n });\n\n // Every time the checkboxes in the report are changed, update the list of users in the form values\n // and enable/disable the action select.\n const updateUserIds = () => {\n const selectedUsers = [...userReport.querySelectorAll(Selectors.checkedRows)];\n const selectedUserIds = selectedUsers.map(check => parseInt(check.value));\n userBulkForm.querySelector('[name=\"userids\"]').value = selectedUserIds.join(',');\n actionSelect.disabled = selectedUsers.length === 0;\n const selectedUsersNames = selectedUsers.map(check => document.querySelector(`label[for=\"${check.id}\"]`).textContent);\n // Add the user ids and names to the form data attributes so they can be available from the\n // other JS modules that listen to the form submit event.\n userBulkForm.data = {userids: selectedUserIds, usernames: selectedUsersNames};\n };\n\n updateUserIds();\n\n document.addEventListener('change', event => {\n // When checkboxes are checked next to individual users or the master toggle (Select all/none).\n if ((event.target.matches(Selectors.checkbox) || event.target.matches(Selectors.masterCheckbox))\n && userReport.contains(event.target)) {\n updateUserIds();\n }\n });\n\n document.addEventListener(tableEvents.tableContentRefreshed, event => {\n // When the report contents is updated (i.e. page is changed, filters applied, etc).\n if (userReport.contains(event.target)) {\n updateUserIds();\n }\n });\n};\n"],"names":["Selectors","userBulkForm","document","querySelector","userReport","closest","_userBulkForm$closest","reportSelectors","regions","report","actionSelect","CustomEvents","define","events","accessibleChange","on","event","target","value","e","Event","cancelable","dispatchEvent","defaultPrevented","FormChangeChecker","markFormSubmitted","submit","updateUserIds","selectedUsers","querySelectorAll","selectedUserIds","map","check","parseInt","join","disabled","length","selectedUsersNames","id","textContent","data","userids","usernames","addEventListener","matches","contains","tableEvents","tableContentRefreshed"],"mappings":";;;;;;;0WA6BMA,0BACe,6BADfA,4BAEiB,2CAFjBA,mBAGQ,oFAHRA,yBAIc,qFAJdA,sBAKW,oFAMG,qCAEVC,aAAeC,SAASC,cAAcH,2BACtCI,WAAaH,MAAAA,4CAAAA,aAAcI,QAAQL,qEAAtBM,sBAAoDH,cAAcI,gBAAgBC,QAAQC,YACxGR,eAAiBG,wBAGhBM,aAAeT,aAAaE,cAAc,UAChDQ,aAAaC,OAAOF,aAAc,CAACC,aAAaE,OAAOC,uCAEhDJ,cAAcK,GAAGJ,aAAaE,OAAOC,kBAAkBE,WACtDA,MAAMC,OAAOC,OAAqC,MAA5B,UAAGF,MAAMC,OAAOC,OAAiB,OACjDC,EAAI,IAAIC,MAAM,SAAU,CAACC,YAAY,IAC3CpB,aAAaqB,cAAcH,GACtBA,EAAEI,mBACHC,kBAAkBC,kBAAkBxB,cACpCA,aAAayB,oBAOnBC,cAAgB,WACZC,cAAgB,IAAIxB,WAAWyB,iBAAiB7B,wBAChD8B,gBAAkBF,cAAcG,KAAIC,OAASC,SAASD,MAAMd,SAClEjB,aAAaE,cAAc,oBAAoBe,MAAQY,gBAAgBI,KAAK,KAC5ExB,aAAayB,SAAoC,IAAzBP,cAAcQ,aAChCC,mBAAqBT,cAAcG,KAAIC,OAAS9B,SAASC,mCAA4B6B,MAAMM,UAAQC,cAGzGtC,aAAauC,KAAO,CAACC,QAASX,gBAAiBY,UAAWL,qBAG9DV,gBAEAzB,SAASyC,iBAAiB,UAAU3B,SAE3BA,MAAMC,OAAO2B,QAAQ5C,qBAAuBgB,MAAMC,OAAO2B,QAAQ5C,4BAC3DI,WAAWyC,SAAS7B,MAAMC,SACjCU,mBAIRzB,SAASyC,iBAAiBG,YAAYC,uBAAuB/B,QAErDZ,WAAWyC,SAAS7B,MAAMC,SAC1BU"} \ No newline at end of file diff --git a/admin/amd/src/bulk_user_actions.js b/admin/amd/src/bulk_user_actions.js new file mode 100644 index 0000000000000..6e1a07565c3ca --- /dev/null +++ b/admin/amd/src/bulk_user_actions.js @@ -0,0 +1,91 @@ +// This file is part of Moodle - http://moodle.org/ +// +// Moodle is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Moodle is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Moodle. If not, see . + +/** + * Add bulk actions to the users list report + * + * @module core_admin/bulk_user_actions + * @copyright 2024 Marina Glancy + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +import * as reportSelectors from 'core_reportbuilder/local/selectors'; +import * as tableEvents from 'core_table/local/dynamic/events'; +import * as FormChangeChecker from 'core_form/changechecker'; +import * as CustomEvents from 'core/custom_interaction_events'; +import jQuery from 'jquery'; + +const Selectors = { + bulkActionsForm: 'form#user-bulk-action-form', + userReportWrapper: '[data-region="report-user-list-wrapper"]', + checkbox: 'input[type="checkbox"][data-togglegroup="report-select-all"][data-toggle="slave"]', + masterCheckbox: 'input[type="checkbox"][data-togglegroup="report-select-all"][data-toggle="master"]', + checkedRows: '[data-togglegroup="report-select-all"][data-toggle="slave"]:checked', +}; + +/** + * Initialise module + */ +export const init = () => { + + const userBulkForm = document.querySelector(Selectors.bulkActionsForm); + const userReport = userBulkForm?.closest(Selectors.userReportWrapper)?.querySelector(reportSelectors.regions.report); + if (!userBulkForm || !userReport) { + return; + } + const actionSelect = userBulkForm.querySelector('select'); + CustomEvents.define(actionSelect, [CustomEvents.events.accessibleChange]); + + jQuery(actionSelect).on(CustomEvents.events.accessibleChange, event => { + if (event.target.value && `${event.target.value}` !== "0") { + const e = new Event('submit', {cancelable: true}); + userBulkForm.dispatchEvent(e); + if (!e.defaultPrevented) { + FormChangeChecker.markFormSubmitted(userBulkForm); + userBulkForm.submit(); + } + } + }); + + // Every time the checkboxes in the report are changed, update the list of users in the form values + // and enable/disable the action select. + const updateUserIds = () => { + const selectedUsers = [...userReport.querySelectorAll(Selectors.checkedRows)]; + const selectedUserIds = selectedUsers.map(check => parseInt(check.value)); + userBulkForm.querySelector('[name="userids"]').value = selectedUserIds.join(','); + actionSelect.disabled = selectedUsers.length === 0; + const selectedUsersNames = selectedUsers.map(check => document.querySelector(`label[for="${check.id}"]`).textContent); + // Add the user ids and names to the form data attributes so they can be available from the + // other JS modules that listen to the form submit event. + userBulkForm.data = {userids: selectedUserIds, usernames: selectedUsersNames}; + }; + + updateUserIds(); + + document.addEventListener('change', event => { + // When checkboxes are checked next to individual users or the master toggle (Select all/none). + if ((event.target.matches(Selectors.checkbox) || event.target.matches(Selectors.masterCheckbox)) + && userReport.contains(event.target)) { + updateUserIds(); + } + }); + + document.addEventListener(tableEvents.tableContentRefreshed, event => { + // When the report contents is updated (i.e. page is changed, filters applied, etc). + if (userReport.contains(event.target)) { + updateUserIds(); + } + }); +}; diff --git a/admin/classes/privacy/provider.php b/admin/classes/privacy/provider.php index 118308d59cfb9..a2a277db097c4 100644 --- a/admin/classes/privacy/provider.php +++ b/admin/classes/privacy/provider.php @@ -35,7 +35,7 @@ class provider implements \core_privacy\local\metadata\null_provider { * * @return string */ - public static function get_reason() : string { + public static function get_reason(): string { return 'privacy:metadata'; } } diff --git a/admin/classes/reportbuilder/local/systemreports/users.php b/admin/classes/reportbuilder/local/systemreports/users.php index 680b1c5f047b0..b3ae4c5643468 100644 --- a/admin/classes/reportbuilder/local/systemreports/users.php +++ b/admin/classes/reportbuilder/local/systemreports/users.php @@ -67,6 +67,13 @@ protected function initialise(): void { $this->add_base_fields("{$entityuseralias}.id, {$entityuseralias}.confirmed, {$entityuseralias}.mnethostid, {$entityuseralias}.suspended, {$entityuseralias}.username, " . implode(', ', $fullnamefields)); + if ($this->get_parameter('withcheckboxes', false, PARAM_BOOL)) { + $canviewfullnames = has_capability('moodle/site:viewfullnames', \context_system::instance()); + $this->set_checkbox_toggleall(static function(\stdClass $row) use ($canviewfullnames): array { + return [$row->id, fullname($row, $canviewfullnames)]; + }); + } + $paramguest = database::generate_param_name(); $this->add_base_condition_sql("{$entityuseralias}.deleted <> 1 AND {$entityuseralias}.id <> :{$paramguest}", [$paramguest => $CFG->siteguest]); diff --git a/admin/classes/table/hook_list_table.php b/admin/classes/table/hook_list_table.php index 1f95002703e88..1ffc092708b69 100644 --- a/admin/classes/table/hook_list_table.php +++ b/admin/classes/table/hook_list_table.php @@ -154,11 +154,7 @@ protected function col_deprecates(stdClass $row): string { return ''; } - $rc = new \ReflectionClass($row->classname); - if (!$rc->implementsInterface(\core\hook\deprecated_callback_replacement::class)) { - return ''; - } - $deprecates = call_user_func([$row->classname, 'get_deprecated_plugin_callbacks']); + $deprecates = \core\hook\manager::get_replaced_callbacks($row->classname); if (count($deprecates) === 0) { return ''; } diff --git a/admin/environment.xml b/admin/environment.xml index 45d774ed111ef..4925aafb1c80d 100644 --- a/admin/environment.xml +++ b/admin/environment.xml @@ -3939,6 +3939,7 @@ + @@ -4128,6 +4129,7 @@ + diff --git a/admin/presets/classes/helper.php b/admin/presets/classes/helper.php index 99d41581cd785..b081c49b1e5de 100644 --- a/admin/presets/classes/helper.php +++ b/admin/presets/classes/helper.php @@ -301,14 +301,12 @@ public static function create_default_presets(): void { // Set Activity chooser tabs to the default value ("Starred, Recommended, All, Activities, Resources"). static::add_item($presetid, 'activitychoosertabmode', '3'); - // Modules: Enable chat, database, external tool (lti), IMS content package (imscp), lesson, SCORM, survey, wiki, workshop. - static::add_plugin($presetid, 'mod', 'chat', true); + // Modules: Enable database, external tool (lti), IMS content package (imscp), lesson, SCORM, wiki, workshop. static::add_plugin($presetid, 'mod', 'data', true); static::add_plugin($presetid, 'mod', 'lti', true); static::add_plugin($presetid, 'mod', 'imscp', true); static::add_plugin($presetid, 'mod', 'lesson', true); static::add_plugin($presetid, 'mod', 'scorm', true); - static::add_plugin($presetid, 'mod', 'survey', true); static::add_plugin($presetid, 'mod', 'wiki', true); static::add_plugin($presetid, 'mod', 'workshop', true); diff --git a/admin/presets/classes/privacy/provider.php b/admin/presets/classes/privacy/provider.php index 81b955d972f55..11a719f649e13 100644 --- a/admin/presets/classes/privacy/provider.php +++ b/admin/presets/classes/privacy/provider.php @@ -41,7 +41,7 @@ class provider implements * @param collection $collection A list of information about this component * @return collection The collection object filled out with information about this component. */ - public static function get_metadata(collection $collection) : collection { + public static function get_metadata(collection $collection): collection { // These tables are really data about site configuration and not user data. // The adminpresets includes information about which user performed a configuration change using the admin_presets @@ -74,7 +74,7 @@ public static function get_metadata(collection $collection) : collection { * @param int $userid The user to search. * @return contextlist $contextlist The contextlist containing the list of contexts used in this plugin. */ - public static function get_contexts_for_userid(int $userid) : contextlist { + public static function get_contexts_for_userid(int $userid): contextlist { return new contextlist(); } diff --git a/admin/presets/tests/helper_test.php b/admin/presets/tests/helper_test.php index 0a4c6ed05954c..f0e39c1ed9782 100644 --- a/admin/presets/tests/helper_test.php +++ b/admin/presets/tests/helper_test.php @@ -344,7 +344,7 @@ public function change_default_preset_provider(): array { ], 'plugins' => [ 'assign' => 1, - 'chat' => 1, + 'book' => 1, 'data' => 1, 'lesson' => 1, ], diff --git a/admin/renderer.php b/admin/renderer.php index 7e7ef8d30bc89..184a7ba759a2b 100644 --- a/admin/renderer.php +++ b/admin/renderer.php @@ -617,7 +617,7 @@ public function cron_overdue_warning($cronoverdue) { * @param bool $croninfrequent * @return string HTML to output. */ - public function cron_infrequent_warning(bool $croninfrequent) : string { + public function cron_infrequent_warning(bool $croninfrequent): string { global $CFG; if (!$croninfrequent) { diff --git a/admin/roles/classes/allow_role_page.php b/admin/roles/classes/allow_role_page.php index b32946b456a8a..b1c708912ea4e 100644 --- a/admin/roles/classes/allow_role_page.php +++ b/admin/roles/classes/allow_role_page.php @@ -99,7 +99,7 @@ public function process_submission() { * @param int $fromroleid * @param int $targetroleid */ - protected abstract function set_allow($fromroleid, $targetroleid); + abstract protected function set_allow($fromroleid, $targetroleid); /** * Load the current allows from the database. @@ -183,11 +183,11 @@ public function get_table() { * Snippet of text displayed above the table, telling the admin what to do. * @return string */ - public abstract function get_intro_text(); + abstract public function get_intro_text(); /** * Returns the allow class respective event class name. * @return string */ - protected abstract function get_eventclass(); + abstract protected function get_eventclass(); } diff --git a/admin/roles/classes/capability_table_base.php b/admin/roles/classes/capability_table_base.php index ab45e085861e5..c95cf363c0b9f 100644 --- a/admin/roles/classes/capability_table_base.php +++ b/admin/roles/classes/capability_table_base.php @@ -142,12 +142,12 @@ protected function print_heading_row($capability) { /** * For subclasses to override, output header cells, after the initial capability one. */ - protected abstract function add_header_cells(); + abstract protected function add_header_cells(); /** * For subclasses to override, return the number of cells that add_header_cells/add_row_cells output. */ - protected abstract function num_extra_columns(); + abstract protected function num_extra_columns(); /** * For subclasses to override. Allows certain capabilties @@ -191,5 +191,5 @@ protected function get_row_attributes($capability) { * @param stdClass $capability the capability this row relates to. * @return string html of row cells */ - protected abstract function add_row_cells($capability); + abstract protected function add_row_cells($capability); } diff --git a/admin/roles/classes/capability_table_with_risks.php b/admin/roles/classes/capability_table_with_risks.php index 4e597f472f4da..9ff7da475a508 100644 --- a/admin/roles/classes/capability_table_with_risks.php +++ b/admin/roles/classes/capability_table_with_risks.php @@ -84,7 +84,7 @@ protected function load_current_permissions() { } } - protected abstract function load_parent_permissions(); + abstract protected function load_parent_permissions(); /** * Update $this->permissions based on submitted data, while making a list of @@ -156,7 +156,7 @@ protected function get_row_classes($capability) { return $rowclasses; } - protected abstract function add_permission_cells($capability); + abstract protected function add_permission_cells($capability); protected function add_row_cells($capability) { $cells = $this->add_permission_cells($capability); diff --git a/admin/roles/classes/define_role_table_advanced.php b/admin/roles/classes/define_role_table_advanced.php index f230e0eb905c7..632cb3e39a6a2 100644 --- a/admin/roles/classes/define_role_table_advanced.php +++ b/admin/roles/classes/define_role_table_advanced.php @@ -645,7 +645,7 @@ protected function get_role_risks_info() { protected function print_field($name, $caption, $field, $helpicon = null) { global $OUTPUT; // Attempt to generate HTML like formslib. - echo '
'; + echo '
'; echo '
'; if ($name) { echo '