From 12ab248f84120715651740f3aa7eead9dc392bb7 Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Tue, 29 Aug 2023 12:30:56 +0800 Subject: [PATCH 01/37] [docs] Add notes for MDL-79064 on getString vs get_string --- docs/devupdate.md | 68 +++++++++++++++++-------- docs/guides/javascript/_examples/str.js | 4 +- docs/guides/javascript/index.md | 20 +++++++- 3 files changed, 68 insertions(+), 24 deletions(-) diff --git a/docs/devupdate.md b/docs/devupdate.md index d9c786d344..72358d7989 100644 --- a/docs/devupdate.md +++ b/docs/devupdate.md @@ -118,6 +118,32 @@ As a proof of concept, this feature has been implemented by: A new `core_courseformat/local/content/cm/activitybadge` template has been also created to display this activity badge data. As usual, it can be overridden by any format plugin. +## JavaScript String fetchers + + + +New string fetchers were introduced in MDL-79064: + +- `getString` - fetch a single string, resolving in a Native Promise +- `getStrings` - fetch a set of strings, resolving in an array of Native Promises + +Note: These new fetchers will return a _native_ Promise rather than a jQuery Promise. + +The `get_string` and `get_strings` methods have _not_ been deprecated at this time but work is underway to allow for their future deprecation. + +:::caution Native Promises vs jQuery Promises + +The use of jQuery Promises is discouraged in Moodle as they have a different behaviour in some cases: + +- native Promises do not have the `done` method. Please use `then` instead +- native Promises do not have the `fail` method. Please use `catch` instead + +Please note that the behaviour of `catch` differs from the jQuery `fail`. The `catch` method will return a resolved Promise, whist the `fail` method will not. + +You are **strongly** advised to convert all uses of `.done`, and `.fail` in your code to `.then`, and `.catch` as appropriate. + +::: + ## Modal Dialogues Moodle 4.3 brings a number of improvements to Modal dialogues. These improvements focus on: @@ -431,30 +457,30 @@ The MIME icons located in the `pix/f` directory, have undergone an update. These To streamline the variety of icons associated with different MIME types, several specific MIME icons have been replaced. Instead, their corresponding generic icons have been integrated from the existing collection, leading to a more efficient representation: -- avi -> video -- base -> database -- bmp -> image -- html -> markup -- jpeg -> image -- mov -> video -- mp3 -> audio -- mpeg -> video -- png -> image -- quicktime -> video -- tiff -> image -- wav -> audio -- wmv -> video +- `avi` -> `video` +- `base` -> `database` +- `bmp` -> `image` +- `html` -> `markup` +- `jpeg` -> `image` +- `mov` -> `video` +- `mp3` -> `audio` +- `mpeg` -> `video` +- `png` -> `image` +- `quicktime` -> `video` +- `tiff` -> `image` +- `wav` -> `audio` +- `wmv` -> `video` The subsequent MIME icons have been entirely removed: -- clip-353 -- edit -- env -- explore -- folder-open -- help -- move -- parent +- `clip-353` +- `edit` +- `env` +- `explore` +- `folder-open` +- `help` +- `move` +- `parent` :::warning diff --git a/docs/guides/javascript/_examples/str.js b/docs/guides/javascript/_examples/str.js index ff550819bd..631c9ce980 100644 --- a/docs/guides/javascript/_examples/str.js +++ b/docs/guides/javascript/_examples/str.js @@ -1,7 +1,7 @@ -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; getString('close', 'core') -.then(closeString => { +.then((closeString) => { window.console.log(closeString); return closeString; diff --git a/docs/guides/javascript/index.md b/docs/guides/javascript/index.md index c9374dac29..2bf8a87d3c 100644 --- a/docs/guides/javascript/index.md +++ b/docs/guides/javascript/index.md @@ -433,7 +433,12 @@ import GetModal from '!!raw-loader!./_examples/promises/getModal'; One of the most helpful core modules is `core/str` which allows you to fetch and render language Strings in JavaScript. -The `core/str` module has two main functions, which both return Promises containing the resolved string. +The `core/str` module has several core methods to support fetching strings: + +- `getString` - fetch a single string, returned in a _native_ Promise +- `getStrings` - fetch a set of strings, returned in an array of _native_ Promises +- `get_string` - fetch a single string, returned in a _jQuery_ Promise +- `get_strings` - fetch a set of strings, returned in an array of _jQuery_ Promises Strings are fetched on request from Moodle, and are then cached in LocalStorage. @@ -441,6 +446,19 @@ import WorkingWithStrings from '!!raw-loader!./_examples/str'; {WorkingWithStrings} +:::caution Native vs jQuery promises + + + +The `getString` and `getStrings` methods should be preferred from Moodle 4.3 onwards. These return _native_ Promises, which differ slightly in behaviour from jQuery Promises. + +Care should be taken in the following scenarios: + +- when writing a plugin which supports Moodle 4.2 or earlier, you _must_ use the `get_string` and `get_strings` methods +- you should _never_ use `.done` or `.fail` as they do not exist in the native Promise specification, and their failure behaviour differs + +::: + ## Templates ## Modals From 43033cc9d05b54ab549e470b40316da253c33560 Mon Sep 17 00:00:00 2001 From: sayoder-aprende <132913696+sayoder-aprende@users.noreply.github.com> Date: Tue, 29 Aug 2023 10:28:55 -0400 Subject: [PATCH 02/37] Update Templates docs to clarify availability of globals dict --- docs/guides/templates/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/guides/templates/index.md b/docs/guides/templates/index.md index 2f6e0bec5a..077045a258 100644 --- a/docs/guides/templates/index.md +++ b/docs/guides/templates/index.md @@ -645,6 +645,7 @@ In PHP you have access to the `$CFG` object to allow access to properties. Musta ``` The properties available on the `globals.config` object are the same as normally exposed for JavaScript; these are gathered from `get_config_for_javascript()` function in `lib/outputrequirementslib.php`. +This object is only available when using client-side Mustache rendering in JavaScript; it is not added to templates rendered with the PHP Mustache engine. ## Core templates From b197d08b6f20da0fb17ed9eeb8070a0370d53382 Mon Sep 17 00:00:00 2001 From: Michael Aherne Date: Mon, 4 Sep 2023 09:14:32 +0100 Subject: [PATCH 03/37] Fix typo. --- docs/guides/javascript/reactive/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guides/javascript/reactive/index.md b/docs/guides/javascript/reactive/index.md index 80c75d1c76..4a8636e00e 100644 --- a/docs/guides/javascript/reactive/index.md +++ b/docs/guides/javascript/reactive/index.md @@ -14,7 +14,7 @@ import { Since, TabItem, Tabs, ValidExample, InvalidExample } from '@site/src/co -Third-party plugin developers are free to use any framework they want to implement reactive interfaces like React, Angular or Vue. However, for now Moodle does not provide any of them and all core developments are framework independant. +Third-party plugin developers are free to use any framework they want to implement reactive interfaces like React, Angular or Vue. However, for now Moodle does not provide any of them and all core developments are framework independent. Nevertheless, in terms of reusability, maintainability and user experience the advantages of having a reactive UI are undeniable. For this reason Moodle has an adhoc reactive library that can be used to implement small reactive applications on any Moodle page. From 03be75b3c30e669f249283417d135fc224fb2f49 Mon Sep 17 00:00:00 2001 From: "Eloy Lafuente (stronk7)" Date: Mon, 4 Sep 2023 15:47:52 +0200 Subject: [PATCH 04/37] Better explain tracker resolution causes and fix versions This is part of https://tracker.moodle.org/browse/MDLSITE-7354 In that issue, it has been decided to start applying some simple rules to better control resolution causes and fix versions in the MDL project. These changes just insert that information within the Tracker guide (note that some of the points already were present) --- general/development/tracker/guide.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/general/development/tracker/guide.md b/general/development/tracker/guide.md index d9b036e0d3..5db966ecdd 100644 --- a/general/development/tracker/guide.md +++ b/general/development/tracker/guide.md @@ -52,7 +52,7 @@ Once an issue has been created, the following additional fields are able to be c | Field | Values | Notes | |---|---|---| -| **Fixed Version/s** |
  • Prior to integration, this will be set to a backlog (a queue of development work), for example `Must fix for X`.
  • After integration, this will be set to the Moodle version the bug was fixed in, for example `4.0.1`.
|
  • This is usually set by an integrator.
  • Not to be confused with `Affected version`, which is used to define the Moodle version where the issue can be reproduced.
  • If you resolve the bug as anything but `Fixed` (`Cannot Reproduce`, `Won't Fix`, etc.) leave **Fix Version/s** blank.
  • **Fix version/s** are used to automatically build release notes (see the tabs on )
| +| **Fixed Version/s** |
  • Prior to integration, this will be blank or set to a backlog (a queue of development work), for example `Must fix for X`.
  • After integration, this will be set to the Moodle version(s) the issue was fixed in, for example `4.0.1`.
  • For more detailed information, look to the **Resolution** field below.
|
  • This is usually set by an integrator.
  • Not to be confused with `Affected version`, which is used to define the Moodle version where the issue can be reproduced.
  • If you resolve the bug as anything but `Fixed` and, sometimes, `Done` (like `Cannot Reproduce`, `Won't Fix`, etc.) leave **Fix Version/s** blank.
  • **Fix version/s** are used to automatically build release notes (see the tabs on )
| | **Priority** |
  • **Blocker**
    Blocks development or testing, prevents Moodle from running. Applicable to bugs only.
  • **Critical**
    Crashes server, loss of data, severe memory leak
  • **Major**
    Major loss of function, incorrect output
  • **Minor**
    Minor loss of function where workaround is possible
  • **Trivial**
    Cosmetic problem like misspelt words or misaligned text
|
  • When it is reported, the priority level represents the severity of a bug.
  • After being reported, the priority may be promoted by HQ developers and component leads as an issue escalates.
  • Other users wishing to influence the priority of issues should do so by voting for the issue.
  • The priority of new features and improvements should generally remain at the default (Minor) level.
| | **Reporter** | The person who logs the bug.
This field is automatically filled by Tracker. | | | **Assignee** | The person who will fix the issue. The assignee should be set when there is a definite intention to complete the issue. |
  • Developers or QA Testers can reassign issues.
  • Please note that even though a person may be assigned to an issue, this does not mean they are currently working on the issue, although they are likely to in future.
| @@ -76,7 +76,7 @@ Once an issue has been created, the following additional fields are able to be c | Field | Values | Notes | |---|---|---| -| **Resolution** |
  • **Fixed**
    Bug has been fixed; a code change has been integrated into Moodle code.
  • **Won't Fix**
    The problem described is an issue which will never be fixed. Specific reasons should be given.
  • **Not a bug**
    This issue is not a bug. The issue may have been logged in error. Use this code if the bug was fixed by another bug report or in some earlier Moodle version.
  • **Duplicate**
    The problem is a duplicate of an existing issue
  • **Incomplete**
    More information was needed to understand this bug, but it was not provided.
  • **Can't Reproduce**
    Attempts at reproduce the issue failed. If more information appears later, please open a new issue.
  • **Deferred**
    The resolution to this bug will be deferred to a later release or to a fix in a third-party plugin used in Moodle.
| This field is only displayed when resolving or closing a bug. | +| **Resolution** | Issues that may/must have the **Fixed versions** field filled:
  • **Fixed**
    Issue has been fixed; a code change has been integrated into Moodle code. It's **mandatory** to set the *Fixed versions* field for these issues.
  • **Done**
    Normally used for tasks, epics... issues that don't "own" code changes in Moodle, but still have required actions (planning, review, adjust some related system...). When relevant or clearly related with any release it's **recommended** to set the *Fixed versions* to them.
Issues that must not have the **Fixed versions** field filled:
  • **Won't Fix**
    The problem described is an issue which will never be fixed. Specific reasons should be given.
  • **Not a bug**
    This issue is not a bug. The issue may have been logged in error. Use this code if the bug was fixed by another bug report or in some earlier Moodle version.
  • **Duplicate**
    The problem is a duplicate of an existing issue
  • **Incomplete**
    More information was needed to understand this bug, but it was not provided.
  • **Can't Reproduce**
    Attempts at reproduce the issue failed. If more information appears later, please open a new issue.
  • **Deferred**
    The resolution to this bug will be deferred to a later release or to a fix in a third-party plugin used in Moodle.
| This field is only displayed when resolving or closing a bug. | ## See also From c6cb33aee562309c27936d55c277a547a8745ee7 Mon Sep 17 00:00:00 2001 From: sarjona Date: Sun, 10 Sep 2023 05:37:00 +0000 Subject: [PATCH 05/37] [repo] Update Moodle Component metadata --- data/libraries.json | 44 +++++++++++++++++++-- data/moodle-contributors.txt | 11 ++++++ general/community/credits/thirdpartylibs.md | 36 +++++++++++++++-- 3 files changed, 83 insertions(+), 8 deletions(-) diff --git a/data/libraries.json b/data/libraries.json index 0406a8770c..119859a582 100644 --- a/data/libraries.json +++ b/data/libraries.json @@ -9,6 +9,33 @@ "repository": "https://github.com/olivernn/lunr.js", "customised": false }, + { + "copyrightHolders": [], + "location": "admin/tool/mfa/factor/totp/extlib/Assert", + "name": "Assert", + "version": "2.1", + "license": "MIT", + "repository": "https://github.com/beberlei/assert", + "customised": false + }, + { + "copyrightHolders": [], + "location": "admin/tool/mfa/factor/totp/extlib/OTPHP", + "name": "OTPHP", + "version": "9.1.1", + "license": "MIT", + "repository": "https://github.com/Spomky-Labs/otphp", + "customised": false + }, + { + "copyrightHolders": [], + "location": "admin/tool/mfa/factor/totp/extlib/ParagonIE/ConstantTime", + "name": "Constant-Time Encoding", + "version": "2.1.1", + "license": "MIT", + "repository": "https://github.com/paragonie/constant_time_encoding", + "customised": false + }, { "copyrightHolders": [ "2015 Richard Dancsi" @@ -138,7 +165,7 @@ "location": "lib/amd/src/chartjs-lazy.js", "name": "Chart.js", "description": "Simple yet flexible JavaScript charting for designers & developers.", - "version": "4.2.1", + "version": "4.4.0", "license": "MIT", "repository": "https://github.com/chartjs/Chart.js", "customised": true @@ -234,7 +261,7 @@ "location": "lib/editor/atto/plugins/html/yui/src/codemirror", "name": "codemirror", "description": "CodeMirror is a versatile text editor implemented in JavaScript for the browser.", - "version": "5.65.12", + "version": "5.65.15", "license": "MIT", "repository": "https://github.com/codemirror/codemirror5", "customised": false @@ -435,7 +462,7 @@ "location": "lib/jquery", "name": "jQuery", "description": "jQuery is a fast, small, and feature-rich JavaScript library widely used on moodle.", - "version": "3.6.4", + "version": "3.7.1", "license": "MIT", "repository": "https://github.com/jquery/jquery", "customised": false @@ -459,7 +486,7 @@ "location": "lib/lti1p3", "name": "LTI 1.3 Tool Library", "description": "A library used for building IMS-certified LTI 1.3 tool providers in PHP.", - "version": "5.2.6", + "version": "5.4.1", "license": "Apache", "licenseversion": "2.0", "repository": "https://github.com/packbackbooks/lti-1-3-php-library", @@ -879,6 +906,15 @@ "licenseversion": "2.0+", "customised": true }, + { + "copyrightHolders": [], + "location": "lib/webauthn/src", + "name": "WebAuthn", + "version": "2.0.1", + "license": "MIT", + "repository": "https://github.com/lbuchs/WebAuthn", + "customised": false + }, { "copyrightHolders": [ "2009 Phacility" diff --git a/data/moodle-contributors.txt b/data/moodle-contributors.txt index 6d18960377..59aa9fce60 100644 --- a/data/moodle-contributors.txt +++ b/data/moodle-contributors.txt @@ -167,6 +167,7 @@ Candilio Carlos Carrick Carrier +Carrillo Carson Carsten Carter @@ -221,6 +222,7 @@ Cunningham Curry D'souza Dageförde +Dale Dallimore Dalton Damian @@ -240,6 +242,7 @@ Dave Davenport David Davidson +Davies Davis Davitasvili Davo @@ -293,6 +296,7 @@ Eberhardt Eddin Eduardo Ehringer +Eichelberger Eijdenberg EizEddin Elbert @@ -389,6 +393,7 @@ Gray Green Greeve Greg +Gregor Gregory Greidanus Grigory @@ -443,6 +448,7 @@ Hoobin Howard Hugh Hughes +Hummel HungTran Hunkler Hunt @@ -468,6 +474,7 @@ Iurii Iyer Iñaki Iñigo +Jablon Jackson Jacob Jade @@ -492,11 +499,13 @@ Jean-Roch Jeff Jenkins Jenny +Jeremy Jerome Jess Jesse Jetha Jinu +Joachim Joby Jody Joey @@ -935,6 +944,7 @@ Schach Schmadel Schrenk Schroeder +Schulz Scotson ScottVerbeek Scribner @@ -1142,6 +1152,7 @@ Wirianto Wojciech Woldeselassie Wolf +Wolfgang Woloszyn Wolters Woods diff --git a/general/community/credits/thirdpartylibs.md b/general/community/credits/thirdpartylibs.md index 907ef23711..104b17a504 100644 --- a/general/community/credits/thirdpartylibs.md +++ b/general/community/credits/thirdpartylibs.md @@ -21,6 +21,27 @@ Provide a great search experience without the need for external, server-side, se - **License**: MIT - **URL**: [https://github.com/olivernn/lunr.js](https://github.com/olivernn/lunr.js) +### Assert + +- **Location**: admin/tool/mfa/factor/totp/extlib/Assert +- **Version**: 2.1 +- **License**: MIT +- **URL**: [https://github.com/beberlei/assert](https://github.com/beberlei/assert) + +### OTPHP + +- **Location**: admin/tool/mfa/factor/totp/extlib/OTPHP +- **Version**: 9.1.1 +- **License**: MIT +- **URL**: [https://github.com/Spomky-Labs/otphp](https://github.com/Spomky-Labs/otphp) + +### Constant-Time Encoding + +- **Location**: admin/tool/mfa/factor/totp/extlib/ParagonIE/ConstantTime +- **Version**: 2.1.1 +- **License**: MIT +- **URL**: [https://github.com/paragonie/constant_time_encoding](https://github.com/paragonie/constant_time_encoding) + ### jQuery EU Cookie Law popups An easy-to-install jQuery plugin to create EU Cookie Law popups and for GDPR compliance. @@ -130,7 +151,7 @@ JavaScript library to insulate apps from spec changes and prefix differences in Simple yet flexible JavaScript charting for designers & developers. - **Location**: lib/amd/src/chartjs-lazy.js -- **Version**: 4.2.1 (with Moodle customisations) +- **Version**: 4.4.0 (with Moodle customisations) - **License**: MIT - **URL**: [https://github.com/chartjs/Chart.js](https://github.com/chartjs/Chart.js) - **Copyright holders**: @@ -218,7 +239,7 @@ Beautify HTML code in Atto. CodeMirror is a versatile text editor implemented in JavaScript for the browser. - **Location**: lib/editor/atto/plugins/html/yui/src/codemirror -- **Version**: 5.65.12 +- **Version**: 5.65.15 - **License**: MIT - **URL**: [https://github.com/codemirror/codemirror5](https://github.com/codemirror/codemirror5) @@ -394,7 +415,7 @@ Filters HTML. jQuery is a fast, small, and feature-rich JavaScript library widely used on moodle. - **Location**: lib/jquery -- **Version**: 3.6.4 +- **Version**: 3.7.1 - **License**: MIT - **URL**: [https://github.com/jquery/jquery](https://github.com/jquery/jquery) - **Copyright holders**: @@ -416,7 +437,7 @@ jQuery UI is a set of user interface interactions, effects, widgets, and themes A library used for building IMS-certified LTI 1.3 tool providers in PHP. - **Location**: lib/lti1p3 -- **Version**: 5.2.6 (with Moodle customisations) +- **Version**: 5.4.1 (with Moodle customisations) - **License**: Apache 2.0 - **URL**: [https://github.com/packbackbooks/lti-1-3-php-library](https://github.com/packbackbooks/lti-1-3-php-library) - **Copyright holders**: @@ -791,6 +812,13 @@ URL syntax validation using PHP and regular expressions. - **Copyright holders**: - 2000 Rod Apeldoorn - rod(at)canowhoopass(dot)com +### WebAuthn + +- **Location**: lib/webauthn/src +- **Version**: 2.0.1 +- **License**: MIT +- **URL**: [https://github.com/lbuchs/WebAuthn](https://github.com/lbuchs/WebAuthn) + ### XHProf A Hierarchical Profiler for PHP. From dbe96a837f9ffb4dadacd55d90e444765f27accb Mon Sep 17 00:00:00 2001 From: "Eloy Lafuente (stronk7)" Date: Sun, 10 Sep 2023 10:52:10 +0200 Subject: [PATCH 06/37] Add a note about the goutte=>browserkit move (MDL-78934) (also, a brief comment about the removal of --skip-passed (MDL-78570) --- docs/devupdate.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/devupdate.md b/docs/devupdate.md index 72358d7989..f2c6b3f206 100644 --- a/docs/devupdate.md +++ b/docs/devupdate.md @@ -487,3 +487,18 @@ The subsequent MIME icons have been entirely removed: Files utilizing any of these removed icons will now be represented by the "unknown" icon. ::: + + +## Behat + +### Removal of Goutte and Goutte Mink Driver + +The [goutte behat mink driver](https://packagist.org/packages/behat/mink-goutte-driver) has been replaced by the [browserkit](https://packagist.org/packages/behat/mink-browserkit-driver) one because the former has been abandoned. + +The change should be completely transparent for (near) everybody. Only **if you are using some custom-generated `behat.yml`** file or other configuration alternatives different from the Moodle default one, then, **any `goutte` browser occurrence needs to be changed to `browserkit_http`** when configuring the behat mink extension. + +See MDL-78934 for more details and changes applied. + +### Removal of the `--skip-passed` option + +The legacy (and custom) Behat `--skip-passed` option has been removed completely. Please, use the standard `--rerun` option that provides exactly the same (execution of failed scenarios only). From 1e9f7dfb186ba0ad36c0e75c45d853ea7b12a5ef Mon Sep 17 00:00:00 2001 From: Dan Marsden Date: Thu, 24 Aug 2023 17:35:39 +1200 Subject: [PATCH 07/37] [docs] - plugin checklist approval blockers --- general/community/plugincontribution/checklist.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/general/community/plugincontribution/checklist.md b/general/community/plugincontribution/checklist.md index 34400d8f84..46ac34361a 100644 --- a/general/community/plugincontribution/checklist.md +++ b/general/community/plugincontribution/checklist.md @@ -199,6 +199,8 @@ Examples of issues that will prevent your plugin from being approved: 1. There is no public and transparent issue tracker where the community members can leave feedback, report bugs and suggest improvements. 1. Your SQL fails to work on PostgreSQL even when working on MySQL. +1. [Namespace collisions](../../development/policies/codingstyle/frankenstyle.md). +1. Compliance with [security guidelines](../../development/policies/security/index.md). 1. It integrates with an external system and does not have the privacy API correctly implemented. 1. It is an activity module and does not have the backup and restore API implemented. From 18707fbb59675197e087887751c94ba11ef342bb Mon Sep 17 00:00:00 2001 From: Ilya Tregubov Date: Wed, 30 Aug 2023 10:33:21 +0800 Subject: [PATCH 08/37] [docs] Enrol plugin API update related to csv course upload --- data/component-spelling.txt | 1 + docs/apis/subsystems/enrol.md | 18 ++++++++++++++++++ docs/devupdate.md | 22 ++++++++++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/data/component-spelling.txt b/data/component-spelling.txt index 27e0f5c114..1671091138 100644 --- a/data/component-spelling.txt +++ b/data/component-spelling.txt @@ -89,6 +89,7 @@ qbehaviour qformat qtype question +quicktime rating report reportbuilder diff --git a/docs/apis/subsystems/enrol.md b/docs/apis/subsystems/enrol.md index 661039e071..c1044c66a8 100644 --- a/docs/apis/subsystems/enrol.md +++ b/docs/apis/subsystems/enrol.md @@ -223,6 +223,19 @@ The method takes the following parameters: $enrolplugin->add_default_instance($course->id); ``` +### $enrol_plugin->add_custom_instance() + +Add a new enrolment instance to a specific course with custom settings an returns the instance id. This method will create a new instance record in the `enrol` table with the specified settings. + +The method takes the following parameters: + +- Course object +- Array of instance settings + +```php +$enrolplugin->add_custom_instance($course, $settings); +``` + ### $enrol_plugin->delete_instance() Remove an enrolment instance form a course and invalidate all related user enrolments. @@ -235,6 +248,11 @@ The method takes the following parameters: $enrolplugin->delete_instance($instance); ``` +### $enrol_plugin->is_csv_upload_supported() + +Checks whether enrolment plugin is supported in CSV course upload. Defaults to false. Override this function in your enrolment plugin if you want it to +be supported in CSV course upload. + ## See also - [Enrolment plugins](../plugintypes/enrol/index.md) diff --git a/docs/devupdate.md b/docs/devupdate.md index f2c6b3f206..da1a8e3115 100644 --- a/docs/devupdate.md +++ b/docs/devupdate.md @@ -502,3 +502,25 @@ See MDL-78934 for more details and changes applied. ### Removal of the `--skip-passed` option The legacy (and custom) Behat `--skip-passed` option has been removed completely. Please, use the standard `--rerun` option that provides exactly the same (execution of failed scenarios only). + +## Enrolment methods support in CSV course upload + +As part of [MDL-78855](https://tracker.moodle.org/browse/MDL-78855) new methods have been created for `enrol_plugin` to explicitly mark those enrolment methods that are supported in CSV course upload + +Example below for method to be supported: + +```php title="CSV supported" +public function is_csv_upload_supported(): bool { + return true; +} +``` + +Also a new method has been created for `enrol_plugin` to create enrolment instance with custom settings + +```php title="Add custom instance" +public function add_custom_instance(stdClass $course, ?array $fields = null): ?int { + return $this->add_instance($course, $fields); +} +``` + +In [MDL-73839](https://tracker.moodle.org/browse/MDL-73839) cohort enrolment method has been updated to support CSV course upload. From eb5fbd6353f8aa61d1784433eaa687b47f7598ae Mon Sep 17 00:00:00 2001 From: "Eloy Lafuente (stronk7)" Date: Sun, 10 Sep 2023 16:42:19 +0200 Subject: [PATCH 09/37] Expanded DB identifier sizes (tables and columns - MDL-76459) Update all the references to the old 28 and 30 char long limits to the new 53 and 63 ones for Moodle 4.3 and up. Also, add a note in the 4.3 release page, database requirements, about the new max CFG->prefix length (10 characters). Finally, add a note with the same details also to the devupdate doc. Update docs/apis/plugintypes/assign/feedback.md Co-authored-by: Andrew Lyons Update docs/apis/plugintypes/assign/submission.md Co-authored-by: Andrew Lyons Update general/development/tools/xmldb.md Co-authored-by: Andrew Lyons --- docs/apis/plugintypes/assign/feedback.md | 5 +++-- docs/apis/plugintypes/assign/submission.md | 6 +++--- docs/devupdate.md | 12 ++++++++++++ general/development/tools/xmldb.md | 6 +++++- general/releases/4.3.md | 2 ++ 5 files changed, 25 insertions(+), 6 deletions(-) diff --git a/docs/apis/plugintypes/assign/feedback.md b/docs/apis/plugintypes/assign/feedback.md index aa7a5c58a9..9c2f2f4429 100644 --- a/docs/apis/plugintypes/assign/feedback.md +++ b/docs/apis/plugintypes/assign/feedback.md @@ -30,10 +30,11 @@ Assignment Feedback plugins are located in the `/mod/assign/feedback` directory. :::important Plugin naming -The plugin name should be no longer than 13 characters - this is because the database tables for a submission plugin must be prefixed with `assignfeedback_[pluginname]` (15 chars + X) and the table names can be no longer than 28 chars due to a limitation with Oracle. +The plugin name should be no longer than 38 (13 before Moodle 4.3) characters - this is because the database tables for a submission plugin must be prefixed with `assignfeedback_[pluginname]` (15 chars + X) and the table names can be no longer than 53 (28 before Moodle 4.3) chars due to a limitation with PostgreSQL. -If a plugin requires multiple database tables, the plugin name will need to be shorter to allow different table names to fit under the 28 character limit. +If a plugin requires multiple database tables, the plugin name will need to be shorter to allow different table names to fit under the 53 character limit (28 before Moodle 4.3). +Note: If your plugin is intended to work with versions of Moodle older than 4.3, then the plugin name must be 13 characters or shorter, and table names must be 28 characters or shorter. ::: Each plugin is in a separate subdirectory and consists of a number of _mandatory files_ and any other files the developer is going to use. diff --git a/docs/apis/plugintypes/assign/submission.md b/docs/apis/plugintypes/assign/submission.md index dd5083acd8..87e35ab206 100644 --- a/docs/apis/plugintypes/assign/submission.md +++ b/docs/apis/plugintypes/assign/submission.md @@ -30,11 +30,11 @@ Assignment Feedback plugins are located in the `/mod/assign/submission` director :::important Plugin naming -The plugin name should be no longer than 11 characters - this is because the database tables for a submission plugin must be prefixed with `assignsubmission_[pluginname]` (17 chars + X) and the table names can be no longer than 28 chars due to a limitation with Oracle. +The plugin name should be no longer than 36 (11 before Moodle 4.3) characters - this is because the database tables for a submission plugin must be prefixed with `assignsubmission_[pluginname]` (17 chars + X) and the table names can be no longer than 53 (28 before Moodle 4.3) chars due to a limitation with PostgreSQL. -If a plugin requires multiple database tables, the plugin name will need to be shorter to allow different table names to fit under the 28 character limit. +If a plugin requires multiple database tables, the plugin name will need to be shorter to allow different table names to fit under the 53 character limit (28 before Moodle 4.3). -::: +Note: If your plugin is intended to work with versions of Moodle older than 4.3, then the plugin name must be 11 characters or shorter, and table names must be 28 characters or shorter. Each plugin is in a separate subdirectory and consists of a number of _mandatory files_ and any other files the developer is going to use. diff --git a/docs/devupdate.md b/docs/devupdate.md index da1a8e3115..ba93608ebe 100644 --- a/docs/devupdate.md +++ b/docs/devupdate.md @@ -12,6 +12,18 @@ import TabItem from '@theme/TabItem'; This page highlights the important changes that are coming in Moodle 4.3 for developers. +## Database table and column names + +Starting with Moodle 4.3 the limits for both table and column names have been raised. Now table names can be up to 53 characters long (from previous 28 characters limit) and column names can be up to 63 characters long (from previous 30 characters limit). + +:::caution +In order to achieve the above, the maximum length for the database prefix (`$CFG->prefix`) is now 10 characters. Installation or upgrade won't be possible with longer prefixes. +::: + +:::caution +If you are writing a plugin intended for older versions of Moodle then you must continue to use the lower limits of 28, and 30. +::: + ## Activity badge The new activity card design proposed for Moodle 4.3 differentiates badge information from other HTML content (displayed using the pre-existing `afterlink` feature). diff --git a/general/development/tools/xmldb.md b/general/development/tools/xmldb.md index 22ba8212c7..6abbac492d 100644 --- a/general/development/tools/xmldb.md +++ b/general/development/tools/xmldb.md @@ -76,7 +76,11 @@ Apart from the [Database Structures guidelines](https://docs.moodle.org/dev/Data 1. About names: 1. All lowercase names (tables, indexes, keys and fields). - 1. Table names and field names must use only a-z, 0-9 and _ chars. Max 28 characters. + 1. Table names and field names must use only a-z, 0-9 and _ chars. Max 53 characters for tables and 63 characters for fields (28 and 30 before Moodle 4.3). + + :::caution + If you are writing a plugin intended for older versions of Moodle then you must continue to use the lower limits of 28, and 30. + ::: 1. Key and index names under the XMLDB Files must be formed by concatenating the name of the fields present in the key/index with the '"-" (minus) character. 1. Primary key always must be named "primary" (this is one exception to the previous convention). 1. It's highly recommended to avoid [reserved words](https://docs.moodle.org/dev/XMLDB_reserved_words) completely. We know we have some of them now but they should be completely out for next releases. diff --git a/general/releases/4.3.md b/general/releases/4.3.md index 042114dc1b..9238da7a09 100644 --- a/general/releases/4.3.md +++ b/general/releases/4.3.md @@ -36,6 +36,8 @@ Moodle supports the following database servers. Again, version numbers are just | [Microsoft SQL Server](http://www.microsoft.com/en-us/server-cloud/products/sql-server/) | 2017 | Latest | | [Oracle Database](http://www.oracle.com/us/products/database/overview/index.html) | 19 | Latest | +Note that, since Moodle 4.3, the maximum length for the database prefix (`$CFG->prefix`) is 10 characters. Installation or upgrade won't be possible with longer prefixes. + ## Client requirements ### Browser support From b0d82d49d12e1cb43cc634ac0ef872b873c97a44 Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Fri, 18 Aug 2023 15:49:13 +0800 Subject: [PATCH 10/37] [docs] Update information about modal instantiation (MDL-78324) --- docs/devupdate.md | 98 ++++++++++++++++++ docs/guides/javascript/modal/index.md | 143 +++++++++++++++++++++++--- 2 files changed, 229 insertions(+), 12 deletions(-) diff --git a/docs/devupdate.md b/docs/devupdate.md index ba93608ebe..00f2345120 100644 --- a/docs/devupdate.md +++ b/docs/devupdate.md @@ -255,6 +255,104 @@ export default class MyModal extends Modal { +### Instantiation and deprecation of `core/modal_factory` + + + +Moodle 4.3 introduces a new way to instantiate modals which is significantly simpler than earlier versions. Rather than calling the `create` method on the `modal_factory`, it can now be called on the class that you are instantiating. + +In addition, a new `configure` method is introduced which allows you to override configuration options, provide your own, and more. + +Modals which are instantiated using the new method *do not* need to be registered if they are not consumed using the modal factory. + +This change will increase encapsulation and allow modals to handle common actions such as showing on creation, and removing on close much more easily. + +:::note Compatibility with Moodle 4.2 and older + +If your code is intended to work with Moodle 4.2 and older, then you must continue to use the existing `core/modal_factory`, and you must continue to register your modal. This legacy method will be maintained until Moodle 4.6. + +::: + + + + + +The legacy registration will continue to work and should be used if your plugin will be used in Moodle 4.2, or earlier. + +```js title="mod/example/amd/src/mymodal.js" +export default class MyModal extends Modal { + static TYPE = 'mod_example/myModal'; + static TEMPLATE = 'mod_example/my_modal'; + + myCustomSetter(value) { + this.value = value; + } +} +let registered = false; +if (!registered) { + ModalRegistry.register(MyModal.TYPE, MyModal, 'mod_example/my_modal'); + registered = true; +} +``` + +```js title="mod/example/amd/src/consumer.js" +import MyModal from './mymodal'; +import ModalFactory from 'core/modal_factory'; + +// ... +ModalFactory.create({ + TYPE: MyModal.TYPE, +}).then((modal) => { + modal.show(); + modal.myCustomSetter('someValue'); + + return modal; +}) +``` + + + + + + + +```js title="mod/example/amd/src/mymodal.js" +export default class MyModal extends Modal { + static TYPE = 'mod_example/myModal'; + static TEMPLATE = 'mod_example/my_modal'; + + configure(modalConfig) { + // Specify any defaults you like here. + modalConfig.show = true; + super.configure(modalConfig); + + this.myCustomSetter(modalConfig.myCustomValue); + } + + myCustomSetter(value) { + this.value = value; + } +} +``` + +```js title="mod/example/amd/src/consumer.js" +import MyModal from './mymodal'; + +// ... +MyModal.create({ + myCustomValue: 'someValue', + show: true, +}); +``` + + + + + ### Registration helper diff --git a/docs/guides/javascript/modal/index.md b/docs/guides/javascript/modal/index.md index d53dda6d01..3f2daef807 100644 --- a/docs/guides/javascript/modal/index.md +++ b/docs/guides/javascript/modal/index.md @@ -8,7 +8,7 @@ tags: -import { CodeBlock, CodeExample, InvalidExample, ValidExample, Since } from '@site/src/components'; +import { CodeBlock, CodeExample, InvalidExample, ValidExample, Since, DeprecatedSince } from '@site/src/components'; The use of modal modules provides a simplified developer experience for creating modal dialogues within Moodle. @@ -20,6 +20,36 @@ Moodle ships with several standard modal types for you to re-use including a sim ## Creating a basic modal + + +Modals can be created by calling the static `create` method on the modal type you wish to create, for example: + +```javascript title="Creating a stadard modal" +import Modal from 'core/modal'; + +export const init = async () => { + const modal = await Modal.create({ + title: 'Test title', + body: '

Example body content

', + footer: 'An example footer content', + show: true, + removeOnClose: true, + }); +} +``` + +Other standard options are described in the JS Documentation for [the MoodleConfig type](https://jsdoc.moodledev.io/master/module-core_modal.html#~MoodleConfig). + +:::note Support for earlier versions + +If you are supporting an earlier version of Moodle, then you must use the Modal Factory and register your modal. + +::: + +### Modal Factory + + + The Modal Factory can be used to instantiate a new Modal. The factory provides a `create` function, accepting some configuration which is used to create the modal instance, and an optional _trigger element_. The `create` function returns a Promise that is resolved with the created modal. The configuration is provided as an object with key/value pairs. The options are: @@ -65,7 +95,9 @@ export const init = async () => { }; ``` -### Using the 'trigger' +#### Using the 'trigger' + + Moodle Modals created using the Modal Factory support an optional _trigger_ element. Whilst this is available, it is no longer recommended and support for it will likely be removed in Moodle 4.3. @@ -93,6 +125,59 @@ A number of commonly used modals are available as standard, these include: - a Save / Cancel modal - a Cancel modal +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + + + + + +:::note + +If you are developing code for use in Moodle 4.2, or earlier, then you must continue to follow the 4.2 guidelines. + +::: + +To use these modals you can call the `create` method on the relevant Modal Class. + +```javascript title="Creating a save/cancel modal" +import ModalSaveCancel from 'core/modal_save_cancel'; +import {get_string as getString} from 'core/str'; + +export const init = async () => { + const modal = await ModalSaveCancel.create({ + title: 'test title', + body: getString('confirmchange', 'mod_example'), + }); + + // ... +}; +``` + +Each type of modal may fire additional events to allow your code to handle the new functionality being offered -- for example, if you wanted to have a save/cancel modal that you did some form validation on before saving you could do something like the example below. + +```javascript title="Listening to a Save event" +import ModalSaveCancel from 'core/modal_save_cancel'; +import ModalEvents from 'core/modal_events'; +import {get_string as getString} from 'core/str'; + +export const init = async () => { + const modal = await ModalSaveCancel.create({ + title: 'test title', + body: getString('confirmchange', 'mod_example'), + }); + + modal.getRoot().on(ModalEvents.save, (e) => { + // ... + }) + + // ... +}; +``` + + + + To use these modals you can provide the `type` argument to the `ModalFactory.create` method. This argument takes a string value and values can be found for these modals in `ModalFactory.TYPES`. ```javascript title="Creating a save/cancel modal" @@ -132,6 +217,9 @@ export const init = async () => { }; ``` + + + ## Creating a custom modal type In some situations it is desirable to write a brand new modal. @@ -141,19 +229,21 @@ There are two parts to this: - a new Modal class which extends the `core/modal` class; and - a template -Creating the Modal class is as simple as extending the `core/modal` class, providing a `TYPE` property, a `TEMPLATE` property, and registering the modal with the modal factory. +:::important Custom modals in Moodle 4.2 and earlier + +Since Moodle 4.3, creating the Modal class is as simple as extending the `core/modal` class, and providing a `TYPE` property, and `TEMPLATE` property. + +For older versions of Moodle, refer to the [Moodle 4.2 documentation](/versioned_docs/version-4.2/guides/javascript/modal/index.md#creating-a-custom-modal-type). + +::: ```javascript title="mod/example/amd/src/my_modal.js" import Modal from 'core/modal'; -import ModalFactory from 'core/modal_factory'; export default MyModal extends Modal { static TYPE = "mod_example/my_modal"; static TEMPLATE = "mod_example/my_modal"; } - -// Modal Registration process for Moodle 4.3 onwards. -MyModal.registerModalType(); ``` The template should extend the `core/modal` core template and can override any of the title, body, or footer regions, for example: @@ -186,18 +276,47 @@ The template should extend the `core/modal` core template and can override any o {{/ core/modal }} ``` -Once defined, the new modal can be instantiated using the Modal Factory, for example: +Once defined, the new modal can be instantiated using the standard `create` method, for example: ```javascript title="Instantiating a custom modal" -import ModalFactory from 'core/modal_factory'; import MyModal from 'mod_example/my_modal'; export default const init = async() => { // ... - const modal = await ModalFactory.create({ - type: MyModal.TYPE, - }); + const modal = await MyModal.create({}); modal.show(); } ``` + +### Overriding default configuration + +When creating your own modal type, you may wish to override the standard configuration. This can be achieved by overriding the `configure` class and providing your own options, for example: + +```javascript title="Overriding standard options" +import Modal from 'core/modal'; + +export default MyModal extends Modal { + static TYPE = "mod_example/my_modal"; + static TEMPLATE = "mod_example/my_modal"; + + configure(modalConfig) { + // Show this modal on instantiation. + modalConfig.show = true; + + // Remove from the DOM on close. + modalConfig.removeOnClose = true; + + super.configure(modalConfig); + + // Accept our own custom arguments too. + if (modalConfig.someValue) { + this.setSomeValue(someValue); + } + } + + setSomeValue(value) { + this.someValue = value; + } +} +``` From c56f7361ef949d7a00e159231f51fd7b427d0d52 Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Tue, 12 Sep 2023 13:20:55 +0800 Subject: [PATCH 11/37] [docs] Remove registration changes We were able to deprecate the modal_factory entirely in 4.3, so for new code aimed at 4.3 onwards, there is no need to register. The new registration process is only available for 4.3 onwards, so it no longer makes sense to document this. Code aimed at versions including 4.2 and older will still need to register using the previous mechanism. --- docs/devupdate.md | 62 ----------------------------------------------- 1 file changed, 62 deletions(-) diff --git a/docs/devupdate.md b/docs/devupdate.md index 00f2345120..3efca17ad9 100644 --- a/docs/devupdate.md +++ b/docs/devupdate.md @@ -353,68 +353,6 @@ MyModal.create({ -### Registration helper - - - -Moodle 4.3 introduces a new `registerModalType` method on the Modal class to aid in registering a modal. - -:::note Compatibility with Moodle 4.2 and older - -If your code is intended to work with Moodle 4.2 and older, then you must continue to use the old method of registration. This legacy method will be maintained until Moodle 4.6. - -::: - - - - - - -The legacy registration will continue to work and should be used if your plugin will be used in Moodle 4.2, or earlier. - -```js -var MyModal = function(root) { - Modal.call(this, root); -}; - -MyModal.TYPE = 'mod_example/myModal'; -MyModal.prototype = Object.create(Modal.prototype); -MyModal.prototype.constructor = MyModal; - -let registered = false; -if (!registered) { - ModalRegistry.register(MyModal.TYPE, MyModal, 'mod_example/my_modal'); - registered = true; -} - -return MyModal; -``` - - - - - - - -The shortcut helper for Modal registration is suitable for Moodle 4.3 onwards. - -```js -export default class MyModal extends Modal { - static TYPE = 'mod_example/myModal'; - static TEMPLATE = 'mod_example/my_modal'; -} - -MyModal.registerModalType(); -``` - - - - - ## Forms API ### add_sticky_action_buttons From f3c7f12995465972ba671dbf1437ed855b4ce5e5 Mon Sep 17 00:00:00 2001 From: juliavs85 Date: Tue, 12 Sep 2023 16:59:04 +0200 Subject: [PATCH 12/37] [docs] Product writing guidelines --- .../03-productwriting/confirm-msg.md | 183 ++++++++++++++++++ .../03-productwriting/index.md | 11 ++ .../03-productwriting/success-msg.md | 117 +++++++++++ 3 files changed, 311 insertions(+) create mode 100644 general/contentstyleguide/03-productwriting/confirm-msg.md create mode 100644 general/contentstyleguide/03-productwriting/index.md create mode 100644 general/contentstyleguide/03-productwriting/success-msg.md diff --git a/general/contentstyleguide/03-productwriting/confirm-msg.md b/general/contentstyleguide/03-productwriting/confirm-msg.md new file mode 100644 index 0000000000..b704207c47 --- /dev/null +++ b/general/contentstyleguide/03-productwriting/confirm-msg.md @@ -0,0 +1,183 @@ +--- +title: Confirmation messages +sidebar_position: 2 +tags: +- Content style guide +- UX Writing +--- +import {ValidExample, InvalidExample } from '@site/src/components'; + +Confirmation messages prompt people to confirm actions that have significant consequences or are difficult to undo. They can also help prevent errors or unexpected results by verifying user intent before proceeding with an action. + +Clear and concise confirmation messages help people feel more confident and in control of their actions within the Moodle products. + +## Related components + +[HTML modal](https://componentlibrary.moodle.com/admin/tool/componentlibrary/docspage.php/moodle/components/dom-modal/) + +## Basic guidelines + +- Address a single task. +- Ask a clear, specific question. +- Provide clear options to either confirm or cancel the action. +- Explain the consequences of the action so people can make an informed decision. + +## Content + +### Title + +The title or heading of a confirmation message should focus only on one task, and mention the specific action that the user wants to perform. + + + +**Remove grade?** + + + + + +**Confirm action?** + + + +Ask a clear, unambiguous question. + + + +**Delete question?** + + + + + +**Confirmation** + + + + + +**Send notification?** + + + + + +**Warning** + + + +Don't include articles like 'a', 'an' or 'the', so that the question is short and easy to scan. + +### Description + +The description should explain the consequences of the action and share additional details that enable people to make a confident decision. + + + +**Delete entry?** +This will delete the entry 'My first blog post'. + + + + + +**Delete entry?** +Are you sure you want to delete the blog post? + + + +Don't repeat information from the title. + + + +**Remove account 'Barbara Gardner'?** +This account and its data on the site [site name] will be removed from the app on this device. + + + + + +**Remove account 'Barbara Gardner'?** +Are you sure you want to remove this account? + + + +Save "Are you sure?" for actions that have very serious consequences. For example, actions that could prevent a course or activity from working properly, or deleting something that can't be retrieved from the recycle bin. + + + +**Delete tool?** +This tool is currently being used in at least one activity in your course. If you delete this tool, the activities that use it will no longer work. Are you sure you want to delete the tool? + + + + + +**Delete activity?** +Are you sure you want to delete the activity? + + + +### Calls to action + +Calls to action (CTAs) should be clear and simple, and offer a straightforward way out. + + + +**Delete downloaded data?** +Cancel | Delete + + + + + +**Delete downloaded data?** +Cancel | Continue + + + + + +**Delete downloaded data?** +No | Yes + + + +Use the same verb in both the title and the confirmation button to make the content more scannable and summarise outcomes. + + + +**Move** selected activities? +Cancel | **Move** + + + + + +**Move** selected activities? +Cancel | **Continue** + + + + + +**Delete** tool? +Cancel | **Remove** + + + +Avoid 'cancelling cancellations' + + + +**Cancel booking?** +Keep my booking | Cancel booking + + + + + +**Cancel subscription?** +Cancel | Confirm cancellation + + diff --git a/general/contentstyleguide/03-productwriting/index.md b/general/contentstyleguide/03-productwriting/index.md new file mode 100644 index 0000000000..6366ec8cbb --- /dev/null +++ b/general/contentstyleguide/03-productwriting/index.md @@ -0,0 +1,11 @@ +--- +title: Product writing guidelines +tags: +- Content style guide +- UX Writing +--- +import {ValidExample, InvalidExample } from '@site/src/components'; + +These guidelines provide recommendations on how to write effective, consistent, and reliable content for Moodle's products. + +By following these guidelines, you can help ensure that we're effective at communicating key messages to our users, and that we maintain consistency across all parts of the Moodle experience. diff --git a/general/contentstyleguide/03-productwriting/success-msg.md b/general/contentstyleguide/03-productwriting/success-msg.md new file mode 100644 index 0000000000..ce512663dc --- /dev/null +++ b/general/contentstyleguide/03-productwriting/success-msg.md @@ -0,0 +1,117 @@ +--- +title: Success messages +sidebar_position: 3 +tags: +- Content style guide +- UX Writing +--- +import {ValidExample, InvalidExample } from '@site/src/components'; + +Success messages tell users that they've completed a task or achieved a goal, or that an action within the system has been successfully completed. + +## Related components + +[Notification](https://componentlibrary.moodle.com/admin/tool/componentlibrary/docspage.php/moodle/components/notifications/) + +## Basic guidelines + +- Keep success messages short and scannable. +- Focus on what the user has achieved or the system has completed. +- Avoid technical jargon. +- Add a full stop at the end of success messages. + +## Content + +### Message + +Use the short form of passive instead of the full form. + + + +Changes saved. + + + + + +Your changes have been saved. + + + + + +Changes were saved. + + + +Use a verb that describes the action that has been completed successfully, especially if the success message is triggered by an action that the user took. + + + +Course created. + + + + + +The course has been added to your site. + + + + + +Tool deleted. + + + + + +The tool was removed. + + + +In general, avoid expressions like 'successfully' or 'successful'. + + + +Preset applied. + + + + + +The preset has been successfully applied. + + + +Don't repeat information from the title. + + + +Message sent. + + + + + +Message delivery successful. + + + +### Calls to action + +Use a call to action only if there are any next steps that users can take. For example, seeing something new they have created. + +Keep calls to action as short and actionable as you can. + + + +Message posted. You have 30 minutes to edit it. **See post**. + + + + + +Your message has been posted successfully. You have 30 mins to make any changes to it. + + From 86ebd18714cd6347480b92a4a97859104ffefc23 Mon Sep 17 00:00:00 2001 From: Sara Arjona Date: Tue, 12 Sep 2023 17:59:19 +0200 Subject: [PATCH 13/37] [repo] Add missing migrated pages While working on MDL-77708 I identified a couple of migrated pages that were missed in the data/migratedPages.yml. --- data/migratedPages.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/data/migratedPages.yml b/data/migratedPages.yml index 2dedb85d0a..4884f30704 100644 --- a/data/migratedPages.yml +++ b/data/migratedPages.yml @@ -4,6 +4,9 @@ AJAX: AMD_Modal: - filePath: "/docs/guides/javascript/modal/index.md" slug: "/docs/guides/javascript/modal/" +Acceptance_testing: +- filePath: "/general/development/tools/behat/index.md" + slug: "/general/development/tools/behat/" Acceptance_testing/Browsers: - filePath: "/general/development/tools/behat/browsers/index.md" slug: "/general/development/tools/behat/browsers/" @@ -112,6 +115,9 @@ Composer: Conditional_activities_API: - filePath: "/docs/apis/core/conditionalactivities/index.md" slug: "/docs/apis/core/conditionalactivities/" +Contributing_to_Moodle: +- filePath: "/general/community/contribute.md" + slug: "/general/community/contribute" Core_APIs: - filePath: "/docs/apis.md" slug: "/docs/apis" From 34045ef7d7ed622694d377a716be9b1f91e60bb6 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Wed, 13 Sep 2023 11:17:51 +0200 Subject: [PATCH 14/37] MOBILE-4362: Add MacOS instructions in browser guide --- general/app/development/setup/app-in-browser.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/general/app/development/setup/app-in-browser.md b/general/app/development/setup/app-in-browser.md index 2a8d875fc0..cde756cf71 100644 --- a/general/app/development/setup/app-in-browser.md +++ b/general/app/development/setup/app-in-browser.md @@ -46,6 +46,10 @@ chromium-browser --allow-file-access-from-files --disable-web-security --disable start chrome.exe --allow-file-access-from-files --disable-web-security --disable-site-isolation-trials --allow-running-insecure-content --no-referrers --unlimited-storage --auto-open-devtools-for-tabs --user-data-dir=~/.chromium-dev-data ``` +```bash title="MacOS" +open -a /Applications/Chromium.app --args --allow-file-access-from-files --disable-web-security --disable-site-isolation-trials --allow-running-insecure-content --no-referrers --unlimited-storage --auto-open-devtools-for-tabs --user-data-dir=~/.chromium-dev-data +``` + If you are using other operative system, check out [how to run chromium with flags](https://www.chromium.org/developers/how-tos/run-chromium-with-flags) in other environments. Depending on the version of your browser, you may get a warning message saying "You are using an unsupported command-line flag". This is expected and can safely be ignored (for development). From 240fa6a6b7e60d740fe2b41e1abfe7225173505d Mon Sep 17 00:00:00 2001 From: Noel De Martin Date: Wed, 13 Sep 2023 13:52:14 +0200 Subject: [PATCH 15/37] [docs] Add warning about newer chromium versions --- general/app/development/setup/app-in-browser.md | 8 +++++--- general/app/development/setup/troubleshooting.md | 4 ++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/general/app/development/setup/app-in-browser.md b/general/app/development/setup/app-in-browser.md index cde756cf71..b48ea23cb5 100644 --- a/general/app/development/setup/app-in-browser.md +++ b/general/app/development/setup/app-in-browser.md @@ -5,10 +5,12 @@ sidebar_position: 1 tags: - Moodle App --- -Browsers are not officially supported by the application, but you can use a **Chromium-based** browser for development if you don't need any native functionality. +Browsers are not officially supported by the application, but you can use a **Chromium-based** browser older than version 119 for development if you don't need any native functionality. :::note Notice -Please notice that it has to be a Chromium-based browser, because the application relies on the [Web SQL Database API](https://caniuse.com/?search=websql) which isn't supported in most browsers. This is a non-standard API, but that's not a problem because this is only used in the browser. Running on a native environment, the application relies on native APIs that are well supported. +Please notice that it has to be a Chromium-based browser older than version 119 because the application relies on the [Web SQL Database API](https://caniuse.com/?search=websql) which isn't supported in most browsers. This is a non-standard API, but that's not a problem because this is only used in the browser. Running on a native environment, the application relies on native APIs that are well supported. + +This requirement may be dropped in future versions of the app: [MOBILE-4304](https://tracker.moodle.org/browse/MOBILE-4304) ::: ## Differences between Chromium and Google Chrome @@ -34,7 +36,7 @@ Disadvantages: ## Installation and configuration -You can install the Chromium browser by downloading it from [the official download page](https://www.chromium.org/getting-involved/download-chromium). +You can install the Chromium browser by downloading it from [the official download page](https://www.chromium.org/getting-involved/download-chromium) (make sure to install a version older than 119). In order to run the Moodle App, we recommend that you launch the browser with a couple of arguments. These are necessary to disable some of the limitations that don't exist in the native application, and also prepare the development environment. diff --git a/general/app/development/setup/troubleshooting.md b/general/app/development/setup/troubleshooting.md index c0750c52b7..b10650b076 100644 --- a/general/app/development/setup/troubleshooting.md +++ b/general/app/development/setup/troubleshooting.md @@ -224,3 +224,7 @@ There will be a pause (a few minutes) while building everything. It should finis ` Then you can access it by running Chrome and connecting to localhost:8100. + +## `window.openDatabase` is not a function + +This error appears in browsers that don't support the [WebSQL](https://caniuse.com/?search=websql) API. We suggest using a [Chromium-based browser older than version 119](./app-in-browser.md). From c2d15abdf02ade530de34fdd3a2fb52e5357797d Mon Sep 17 00:00:00 2001 From: "Eloy Lafuente (stronk7)" Date: Wed, 13 Sep 2023 14:24:23 +0200 Subject: [PATCH 16/37] Declare PHP 8.2 supported - Do it in the PHP policy page: By MDL-76405, since 4.2.3 - Update 4.2 release notes. - Update 4.3 release notes. --- general/development/policies/php.md | 10 ++++++---- general/releases/4.2.md | 2 +- general/releases/4.3.md | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/general/development/policies/php.md b/general/development/policies/php.md index 14a36cb5f6..7b9214be55 100644 --- a/general/development/policies/php.md +++ b/general/development/policies/php.md @@ -49,6 +49,12 @@ You must be logged in to tracker to see issues in Epics. ## PHP supported versions +### PHP 8.2 + + + +PHP 8.2 **can be used with** Moodle 4.2.3, Moodle 4.3 and later releases. See MDL-76405 for details. + ### PHP 8.1 @@ -93,10 +99,6 @@ PHP 7.0 **can be used with** Moodle 3.0.1, Moodle 3.1 and later releases. It is ## PHP versions under development -### PHP 8.2 - -PHP 8.2 support **is currently being implemented** for Moodle 4.2.x, 4.3.0 and later releases. Hence it's still **incomplete and only for development purposes**. See MDL-76405 for details. - ### PHP 8.3 PHP 8.3 support **is currently being implemented**. We hope to make this available for Moodle 4.4 but this has not yet been confirmed and is subject to change. See MDL-76426 for details. diff --git a/general/releases/4.2.md b/general/releases/4.2.md index 96de72e0a8..acd8baf7a6 100644 --- a/general/releases/4.2.md +++ b/general/releases/4.2.md @@ -19,7 +19,7 @@ If you are upgrading from a previous version, please see [Upgrading](https://doc These are just the minimum supported versions. We recommend keeping all of your software and operating systems up-to-date. - Moodle upgrade: Moodle 3.11.8 or later. -- PHP version: minimum PHP 8.0.0 *Note: minimum PHP version has increased since Moodle 4.1*. PHP 8.1.x is supported too. See [PHP](../development/policies/php.md) for details. +- PHP version: minimum PHP 8.0.0 *Note: minimum PHP version has increased since Moodle 4.1*. PHP 8.2.x is supported too (since Moodle 4.2.3). See [PHP](../development/policies/php.md) for details. - PHP extension **sodium** is required. See [Environment - PHP extension sodium](https://docs.moodle.org/en/Environment_-_PHP_extension_sodium). - PHP extension **exif** is recommended. - PHP setting **max_input_vars** must be >= 5000. For further details, see [Environment - max input vars](https://docs.moodle.org/en/Environment_-_max_input_vars). diff --git a/general/releases/4.3.md b/general/releases/4.3.md index 9238da7a09..a2344735eb 100644 --- a/general/releases/4.3.md +++ b/general/releases/4.3.md @@ -19,7 +19,7 @@ If you are upgrading from a previous version, please see [Upgrading](https://doc These are just the minimum supported versions. We recommend keeping all of your software and operating systems up-to-date. - Moodle upgrade: Moodle 3.11.8 or later. -- PHP version: minimum PHP 8.0.0 *Note: minimum PHP version has increased since Moodle 4.1*. PHP 8.1.x is supported too. See [PHP](../development/policies/php.md) for details. +- PHP version: minimum PHP 8.0.0 *Note: minimum PHP version has increased since Moodle 4.1*. PHP 8.2.x is supported too. See [PHP](../development/policies/php.md) for details. - PHP extension **sodium** is required. See [Environment - PHP extension sodium](https://docs.moodle.org/en/Environment_-_PHP_extension_sodium). - PHP setting **max_input_vars** must be >= 5000. For further details, see [Environment - max input vars](https://docs.moodle.org/en/Environment_-_max_input_vars). - PHP variants: Only 64-bit versions of PHP are supported. *Note: Changed since 4.1*. From 869b7d3427f76dab148e3b177f6c90a3aad3c3f2 Mon Sep 17 00:00:00 2001 From: Sara Arjona Date: Wed, 13 Sep 2023 13:40:30 +0200 Subject: [PATCH 17/37] [docs] Add missing SVG files to devupdate --- docs/devupdate.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/devupdate.md b/docs/devupdate.md index ba93608ebe..8727dd9236 100644 --- a/docs/devupdate.md +++ b/docs/devupdate.md @@ -500,6 +500,18 @@ Files utilizing any of these removed icons will now be represented by the "unkno ::: +### SVG icons + +A new PHPUnit test has been introduced to verify the presence of SVG files for all system icons in Moodle LMS. Any missing SVG files have been rectified within Moodle LMS. + +:::tip + +Third-party plugins are strongly encouraged to follow suit, adding missing SVG files too, to avoid PHPUnit test failures. + +The SVG icons in Moodle LMS were sourced from https://fontawesome.com/search?m=free&o=r, which offers free icons under the Creative Commons Attribution 4.0 International license, consistent with the Moodle icon set. + +::: + ## Behat From 50aaab23bc5b2324f6b237791a3c2bbfd6ea2129 Mon Sep 17 00:00:00 2001 From: Jun Pataleta Date: Tue, 12 Sep 2023 12:43:33 +0800 Subject: [PATCH 18/37] MDL-78806 better page title documentation --- general/development/policies/accessibility.md | 45 ++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/general/development/policies/accessibility.md b/general/development/policies/accessibility.md index def00ab7ba..624875c7de 100644 --- a/general/development/policies/accessibility.md +++ b/general/development/policies/accessibility.md @@ -6,6 +6,8 @@ tags: - Certification --- +import { ValidExample } from '@site/src/components'; + Moodle is designed to provide equal functionality and information to all people. This means that there should be no barriers for people regardless of disabilities, assistive technologies that are used, different screen sizes and different input devices (for example mouse, keyboard and touchscreen). ## Accessibility conformance @@ -121,7 +123,48 @@ See [the ARIA best practice advice on landmarks](https://www.w3.org/TR/wai-aria- All pages should have a unique title that describes the current page. -See [the WCAG 2.1 success criteria for web page titles](https://www.w3.org/TR/WCAG21/#page-titled) for further information. +Some tips for providing a meaningful page title: + +- The page title must be accurate and informative. +- If the page causes a change of context (e.g. a search functionality), it should describe the result or change of context to the user. +- It should be concise. +- If possible, it should uniquely identify the page. +- The most identifying information should come first. + +#### Example + +Consider that a student in the submission page of an assignment called `Kinetics problem set 1` in the `Physics 101` in the `Moodle University`. + +Then a suitable page title for the page would be something like: + + + +The most unique identifying information first represented by the activity name and its sub-page, then followed by broader identifiers such as the course name and the site name. + +`Kinetics problem set 1: Submit assignment | Physics 101 | Moodle University` + + + + + +The most unique identifying information first represented by sub-page's name followed by the activity name that the page belongs to, then followed by broader identifiers such as the course name and the site name. + +`Submit assignment | Kinetics problem set 1 | Physics 101 | Moodle University` + + + +#### Separating components of a page title + +When separating the components of the page tile, please use the `moodle_page:TITLE_SEPARATOR` constant. + +#### Site name on the page title + +There's no need to add the site's name when setting the page title using `$PAGE->set_title()`. The site name is automatically appended at the end of the page title by default by `$PAGE->set_title()`. The displayed site name on the page title will depend on the admin setting `$CFG->sitenameintitle` whether it's set to show the site's full name or the site's short name. + +#### Useful resources + +- [Understanding Success Criterion 2.4.2: Page Titled (Level A)](https://www.w3.org/WAI/WCAG21/Understanding/page-titled) +- [Technique G88: Providing descriptive titles for Web pages](https://www.w3.org/WAI/WCAG21/Techniques/general/G88) ### Advanced UX Widgets From 0f91708ad870ef09a4845e4ba04e5b71afd74274 Mon Sep 17 00:00:00 2001 From: Jun Pataleta <13312075+junpataleta@users.noreply.github.com> Date: Wed, 13 Sep 2023 22:24:16 +0800 Subject: [PATCH 19/37] Apply Andrew's review suggestions Co-authored-by: Andrew Lyons --- general/development/policies/accessibility.md | 46 ++++++++++++++++--- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/general/development/policies/accessibility.md b/general/development/policies/accessibility.md index 624875c7de..c5a2f52930 100644 --- a/general/development/policies/accessibility.md +++ b/general/development/policies/accessibility.md @@ -126,14 +126,22 @@ All pages should have a unique title that describes the current page. Some tips for providing a meaningful page title: - The page title must be accurate and informative. -- If the page causes a change of context (e.g. a search functionality), it should describe the result or change of context to the user. +- If the page causes a _change of context_ (for example, a search functionality), it should describe the result or change of context to the user. - It should be concise. - If possible, it should uniquely identify the page. - The most identifying information should come first. +:::note change of context + +(not to be confused with Moodle's `\core\context` class and its implementations) + +According to the [WCAG 2.1 Understanding Docs](https://www.w3.org/WAI/WCAG21/Understanding/on-focus.html#dfn-changes-of-context), a change in context is a major change that, if made without user awareness, can disorient users who are not able to view the entire page simultaneously. It can include changes of user agent, viewport, focus, or content that changes the meaning of the web page. + +::: + #### Example -Consider that a student in the submission page of an assignment called `Kinetics problem set 1` in the `Physics 101` in the `Moodle University`. +Consider that a student is on the submission page of an assignment activity called `Kinetics problem set 1` in the `Physics 101` course on the `Mount Orange School` Moodle site. Then a suitable page title for the page would be something like: @@ -141,25 +149,49 @@ Then a suitable page title for the page would be something like: The most unique identifying information first represented by the activity name and its sub-page, then followed by broader identifiers such as the course name and the site name. -`Kinetics problem set 1: Submit assignment | Physics 101 | Moodle University` +`Kinetics problem set 1: Submit assignment | Physics 101 | Mount Orange School` -The most unique identifying information first represented by sub-page's name followed by the activity name that the page belongs to, then followed by broader identifiers such as the course name and the site name. +The most unique identifying information first represented by the name of the sub-page, followed by the activity name that the page belongs to, then followed by broader identifiers such as the course name and the site name. -`Submit assignment | Kinetics problem set 1 | Physics 101 | Moodle University` +`Submit assignment | Kinetics problem set 1 | Physics 101 | Mount Orange School` #### Separating components of a page title -When separating the components of the page tile, please use the `moodle_page:TITLE_SEPARATOR` constant. +When separating the components of the page tile, please use the `moodle_page::TITLE_SEPARATOR` constant. + + + +```php +[$course, $cm] = get_course_and_cm_from_cmid($id); +// Activity name and its sub-page as the unique identifying information. +$pagename = format_string($cm->name) . ': ' . get_string('view'); +// Course name. +$coursename = format_string($course->fullname); +// Set the page title, combining the activity page's name and course name using the title separator constant. +$PAGE->set_title($pagename . moodle_page::TITLE_SEPARATOR . $coursename); +``` + + #### Site name on the page title -There's no need to add the site's name when setting the page title using `$PAGE->set_title()`. The site name is automatically appended at the end of the page title by default by `$PAGE->set_title()`. The displayed site name on the page title will depend on the admin setting `$CFG->sitenameintitle` whether it's set to show the site's full name or the site's short name. +You should not add the name of the site when setting the page title using `$PAGE->set_title()`. The site name is automatically appended to the end of the page title in the correct format when using `$PAGE->set_title()`. + +:::info + +Administrators can use the `sitenameinititle` configuration setting to configure how this is shown in the title with possible options including: + +- the _full name_ of the site, for example, "Mount Orange School" +- the _short name_ of the site, for example: "MOS" + +This is automatically handled by `$PAGE->set_title()`. +::: #### Useful resources From 1d9ea38dac776ca224e313a0a1eb26bc5ab5bc9a Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Wed, 13 Sep 2023 21:45:15 +0800 Subject: [PATCH 20/37] [docs] Update deprecation policy for named argument support This is to solve issues identified and raised in MDLSITE-7358 --- general/development/policies/deprecation.md | 136 ++++++++++++++++++-- 1 file changed, 123 insertions(+), 13 deletions(-) diff --git a/general/development/policies/deprecation.md b/general/development/policies/deprecation.md index ccd09fba98..e642ea2e40 100644 --- a/general/development/policies/deprecation.md +++ b/general/development/policies/deprecation.md @@ -14,6 +14,8 @@ tags: ## Why is deprecation needed? +import { ValidExample, InvalidExample, TabItem, Tabs } from '@site/src/components'; + In an open source project, the end use of the codebase varies. People may have customisations and plugins that depend on a function that has been targeted for deprecation. Rather than simply removing a function, we must gracefully deprecate the function over a period covered by a number of released versions. ## What is Moodle's deprecation policy? @@ -93,29 +95,137 @@ Longer deprecation periods can be considered for functions that are widely used. ## Parameters deprecation -- The deprecated parameter should be renamed to `$unused` and it's default value changed to `null`. -- The respective parameter phpDoc should be updated stating the parameter has been deprecated since version X.X and should not be used any more. +Whilst it is possible to deprecate individual method parameters, care must be taken in doing so. -``` - @param null $unused This parameter has been deprecated since 4.0 and should not be used anymore. -``` +If a method is overridden then it is often not possible to change parameters. This includes changing any type hint, or adding a new default value. Additionally, adding a default value to any argument is only possible if all remaining arguments are optional too. -:::note +:::tip + +It is strongly advised to deprecate an entire method, rather than deprecating a single parameter. + +::: + +- Deprecated parameters **MUST** be retained, and **MUST NOT** be renamed +- The respective parameter phpDoc should be updated stating the parameter has been deprecated since version X.X and should not be used any more +- Update all calls to the affected function and either: + - converting to use named parameters, removing the deprecated parameter; or + - removing if at the end of a list of optional parameters. +- Add a mention to the corresponding `upgrade.txt` file, documenting that the deprecated parameter should not be used any more +- Add a mention to the [Developer Update notes](https://github.com/moodle/devdocs/blob/main/docs/devupdate.md), documenting that the deprecated parameter should not be used any more +- _Where possible_: + - If a type was previously specified it should be altered to be made nullable + - If the default value is not already `null`, then it should be updated to `null` + - If the default value was not already `null`, and a non-null value is provided, a debugging notice should be emitted +- Where it is not possible to make the the type nullable, consider deprecating the method and creating a new one with the updated parameters + +:::caution Changes to default values and types -Remember the phpDoc parameter type should also be updated to `null`. +The [Covariance and Contravariance rules for PHP](https://www.php.net/manual/en/language.oop5.variance.php) prevent changes to argument types and defaults when a class is extended and that method overridden. + +When deprecating a method which is _likely_ to be extended, you should strongly consider deprecating the entire method and creating a replacement method with the updated arguments instead. This includes all renderer methods. ::: -- Show a debugging message if that parameter is being provided in the function call: + + + + +```php +/** + * Greet a user and their pets. Remind them how old they are. + * + * @param string $name The name of the individual + * @param int $age The age of the individaul + * @param string[] $pets A list of pets that the individual has + * @return The greeting + */ +public function greet_person( + string $name, + int $age, + array $pets = [], +}: string { + return sprintf( + "A big, warm, welcome to %s who, at the grand old age of %d, has %d pets!", + $name, + $age, + count($pets), + ); +} +``` + + + +```php +/** + * Greet a user and their pets. + * + * @param string $name The name of the individual + * @param null|int $age This parameter has been deprecated since 4.0 and should not be used anymore. + * @param string[] $pets A list of pets that the individual has + */ +public function greet_person( + string $name, + null|int $age = null, + array $pets = [], +}: void { + if ($age !== null) { + debugging( + 'The age argument has been deprecated. Please remove it from your method calls.', + DEBUG_DEVELOPER, + ); + } + + return sprintf( + "A big, warm, welcome to %s who has %d pets!", + $name, + count($pets), + ); +} ``` - if ($unused !== null) { - debugging('Deprecated argument passed to ' . __FUNCTION__, DEBUG_DEVELOPER); - } + + + + +```php +/** + * Greet a user and their pets. + * + * @param string $name The name of the individual + * @param null|int $age This parameter has been deprecated since 4.0 and should not be used anymore. + * @param string[] $pets A list of pets that the individual has + */ +public function greet_person( + string $name, + int $age, + array $pets = [], +}: void { + debugging( + 'The `greet_person` method has been deprecated and replaced with `greet_person_with_pets`. ' . + 'Please update your method calls accordingly.', + DEBUG_DEVELOPER, + ); + + return $this->greet_person_with_pets( + name: $name, + pets: $pets, + ); +} ``` -- Update all calls to the affected function removing the deprecated parameter. -- Add a mention to corresponding `upgrade.txt` documenting the deprecated parameter should not be used any more. + + + + +:::note Deprecations for core methods in Moodle 4.1 and earlier + +Prior to support for PHP 8.0 in Moodle 4.2, the policy for parameter argument deprecation stated that deprecated parameters must be renamed to `$unused` or a similar name. + +This has been changed to no longer allow renaming of arguments because it can lead to fatal errors if the calling code makes use of named parameter arguments. + +Named parameter arguments are available from PHP 8.0 onwards. + +::: ## See also... From 647f3561d6996273829e147b924f7751d5f245bc Mon Sep 17 00:00:00 2001 From: juliavs85 Date: Thu, 14 Sep 2023 15:50:55 +0200 Subject: [PATCH 21/37] [docs] Reorganisation of content for writing guidelines --- .../index.md => contentguidelines.md} | 11 +++++++---- .../01-productwriting/_category_.yml | 3 +++ .../01-productwriting}/confirm-msg.md | 0 .../01-productwriting}/success-msg.md | 0 .../02-styleguide}/01-voice/_category_.yml | 0 .../02-styleguide}/01-voice/contentprinciples.md | 0 .../02-styleguide}/01-voice/values.md | 0 .../02-styleguide}/01-voice/voiceandtone.md | 0 .../02-styleguide}/01-voice/whoweare.md | 0 .../02-styleguide}/02-style/_category_.yml | 0 .../02-styleguide}/grammar.md | 0 general/contentguidelines/02-styleguide/index.md | 9 +++++++++ .../02-styleguide}/translation.md | 0 .../02-styleguide}/wordlist.md | 0 general/contentstyleguide.md | 12 ------------ sidebars/docs.js | 6 ++++++ sidebars/general.js | 8 ++++---- 17 files changed, 29 insertions(+), 20 deletions(-) rename general/{contentstyleguide/03-productwriting/index.md => contentguidelines.md} (55%) create mode 100644 general/contentguidelines/01-productwriting/_category_.yml rename general/{contentstyleguide/03-productwriting => contentguidelines/01-productwriting}/confirm-msg.md (100%) rename general/{contentstyleguide/03-productwriting => contentguidelines/01-productwriting}/success-msg.md (100%) rename general/{contentstyleguide => contentguidelines/02-styleguide}/01-voice/_category_.yml (100%) rename general/{contentstyleguide => contentguidelines/02-styleguide}/01-voice/contentprinciples.md (100%) rename general/{contentstyleguide => contentguidelines/02-styleguide}/01-voice/values.md (100%) rename general/{contentstyleguide => contentguidelines/02-styleguide}/01-voice/voiceandtone.md (100%) rename general/{contentstyleguide => contentguidelines/02-styleguide}/01-voice/whoweare.md (100%) rename general/{contentstyleguide => contentguidelines/02-styleguide}/02-style/_category_.yml (100%) rename general/{contentstyleguide/02-style => contentguidelines/02-styleguide}/grammar.md (100%) create mode 100644 general/contentguidelines/02-styleguide/index.md rename general/{contentstyleguide/02-style => contentguidelines/02-styleguide}/translation.md (100%) rename general/{contentstyleguide/02-style => contentguidelines/02-styleguide}/wordlist.md (100%) delete mode 100644 general/contentstyleguide.md diff --git a/general/contentstyleguide/03-productwriting/index.md b/general/contentguidelines.md similarity index 55% rename from general/contentstyleguide/03-productwriting/index.md rename to general/contentguidelines.md index 6366ec8cbb..0d487ac979 100644 --- a/general/contentstyleguide/03-productwriting/index.md +++ b/general/contentguidelines.md @@ -1,10 +1,13 @@ --- -title: Product writing guidelines +title: The Moodle content guidelines +sidebar_label: Content guidelines +sidebar_position: 1 tags: -- Content style guide -- UX Writing + - Content style guide + - UX Writing --- -import {ValidExample, InvalidExample } from '@site/src/components'; + +The Moodle content guidelines are for UX writers, copywriters, marketers, developers, translators, and anyone else writing for Moodle. These guidelines provide recommendations on how to write effective, consistent, and reliable content for Moodle's products. diff --git a/general/contentguidelines/01-productwriting/_category_.yml b/general/contentguidelines/01-productwriting/_category_.yml new file mode 100644 index 0000000000..b7013b6e77 --- /dev/null +++ b/general/contentguidelines/01-productwriting/_category_.yml @@ -0,0 +1,3 @@ +label: Product writing guidelines +link: + type: generated-index diff --git a/general/contentstyleguide/03-productwriting/confirm-msg.md b/general/contentguidelines/01-productwriting/confirm-msg.md similarity index 100% rename from general/contentstyleguide/03-productwriting/confirm-msg.md rename to general/contentguidelines/01-productwriting/confirm-msg.md diff --git a/general/contentstyleguide/03-productwriting/success-msg.md b/general/contentguidelines/01-productwriting/success-msg.md similarity index 100% rename from general/contentstyleguide/03-productwriting/success-msg.md rename to general/contentguidelines/01-productwriting/success-msg.md diff --git a/general/contentstyleguide/01-voice/_category_.yml b/general/contentguidelines/02-styleguide/01-voice/_category_.yml similarity index 100% rename from general/contentstyleguide/01-voice/_category_.yml rename to general/contentguidelines/02-styleguide/01-voice/_category_.yml diff --git a/general/contentstyleguide/01-voice/contentprinciples.md b/general/contentguidelines/02-styleguide/01-voice/contentprinciples.md similarity index 100% rename from general/contentstyleguide/01-voice/contentprinciples.md rename to general/contentguidelines/02-styleguide/01-voice/contentprinciples.md diff --git a/general/contentstyleguide/01-voice/values.md b/general/contentguidelines/02-styleguide/01-voice/values.md similarity index 100% rename from general/contentstyleguide/01-voice/values.md rename to general/contentguidelines/02-styleguide/01-voice/values.md diff --git a/general/contentstyleguide/01-voice/voiceandtone.md b/general/contentguidelines/02-styleguide/01-voice/voiceandtone.md similarity index 100% rename from general/contentstyleguide/01-voice/voiceandtone.md rename to general/contentguidelines/02-styleguide/01-voice/voiceandtone.md diff --git a/general/contentstyleguide/01-voice/whoweare.md b/general/contentguidelines/02-styleguide/01-voice/whoweare.md similarity index 100% rename from general/contentstyleguide/01-voice/whoweare.md rename to general/contentguidelines/02-styleguide/01-voice/whoweare.md diff --git a/general/contentstyleguide/02-style/_category_.yml b/general/contentguidelines/02-styleguide/02-style/_category_.yml similarity index 100% rename from general/contentstyleguide/02-style/_category_.yml rename to general/contentguidelines/02-styleguide/02-style/_category_.yml diff --git a/general/contentstyleguide/02-style/grammar.md b/general/contentguidelines/02-styleguide/grammar.md similarity index 100% rename from general/contentstyleguide/02-style/grammar.md rename to general/contentguidelines/02-styleguide/grammar.md diff --git a/general/contentguidelines/02-styleguide/index.md b/general/contentguidelines/02-styleguide/index.md new file mode 100644 index 0000000000..e66cd0cd91 --- /dev/null +++ b/general/contentguidelines/02-styleguide/index.md @@ -0,0 +1,9 @@ +--- +title: Style guide +tags: +- Content style guide +- UX Writing +--- +import {ValidExample, InvalidExample } from '@site/src/components'; + +We use this guide to help us create a clear and consistent approach to the content we produce: from our website, marketing, and communications to the actual text in Moodle products. diff --git a/general/contentstyleguide/02-style/translation.md b/general/contentguidelines/02-styleguide/translation.md similarity index 100% rename from general/contentstyleguide/02-style/translation.md rename to general/contentguidelines/02-styleguide/translation.md diff --git a/general/contentstyleguide/02-style/wordlist.md b/general/contentguidelines/02-styleguide/wordlist.md similarity index 100% rename from general/contentstyleguide/02-style/wordlist.md rename to general/contentguidelines/02-styleguide/wordlist.md diff --git a/general/contentstyleguide.md b/general/contentstyleguide.md deleted file mode 100644 index 2c114259e4..0000000000 --- a/general/contentstyleguide.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -title: The Moodle content style guide -sidebar_label: Content style guide -sidebar_position: 1 -tags: - - Content style guide - - UX Writing ---- - -The Moodle content style guide is for UX writers, copywriters, marketers, developers, translators, and anyone else writing for Moodle. - -We use this guide to help us create a clear and consistent approach to the content we produce: from our website, marketing, and communications to the actual text in Moodle products. diff --git a/sidebars/docs.js b/sidebars/docs.js index 3105a0101d..d795d16256 100644 --- a/sidebars/docs.js +++ b/sidebars/docs.js @@ -50,6 +50,12 @@ const sidebars = { }, }, + { + label: 'Content guidelines', + type: 'link', + href: '/general/contentguidelines', + }, + { label: 'Developer update', type: 'doc', diff --git a/sidebars/general.js b/sidebars/general.js index a420d6f322..7be549bd21 100644 --- a/sidebars/general.js +++ b/sidebars/general.js @@ -152,19 +152,19 @@ const sidebars = { }, }, ], - contentstyleguide: [ + contentguidelines: [ { - label: 'Content style guide', + label: 'Content guidelines', type: 'category', items: [ { type: 'autogenerated', - dirName: 'contentstyleguide', + dirName: 'contentguidelines', }, ], link: { type: 'doc', - id: 'contentstyleguide', + id: 'contentguidelines', }, }, ], From e146c797b6e17b3b3b0b0f993eeec9b921ed04ef Mon Sep 17 00:00:00 2001 From: Artur Neumann Date: Fri, 15 Sep 2023 12:14:45 +0545 Subject: [PATCH 22/37] [docs] repository plugins: typo fix --- docs/apis/plugintypes/repository/_examples/lib.php | 2 +- .../version-4.1/apis/plugintypes/repository/_examples/lib.php | 2 +- versioned_docs/version-4.1/apis/plugintypes/repository/index.md | 2 +- .../version-4.2/apis/plugintypes/repository/_examples/lib.php | 2 +- versioned_docs/version-4.2/apis/plugintypes/repository/index.md | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/apis/plugintypes/repository/_examples/lib.php b/docs/apis/plugintypes/repository/_examples/lib.php index 30346d3054..366cb2711d 100644 --- a/docs/apis/plugintypes/repository/_examples/lib.php +++ b/docs/apis/plugintypes/repository/_examples/lib.php @@ -12,7 +12,7 @@ class repository_pluginname extends repository { * * @param string $encodedpath * @param string $page - * @return array the list of files, including meta infomation + * @return array the list of files, including meta information */ public function get_listing($encodedpath = '', $page = '') { // This methods diff --git a/versioned_docs/version-4.1/apis/plugintypes/repository/_examples/lib.php b/versioned_docs/version-4.1/apis/plugintypes/repository/_examples/lib.php index 30346d3054..366cb2711d 100644 --- a/versioned_docs/version-4.1/apis/plugintypes/repository/_examples/lib.php +++ b/versioned_docs/version-4.1/apis/plugintypes/repository/_examples/lib.php @@ -12,7 +12,7 @@ class repository_pluginname extends repository { * * @param string $encodedpath * @param string $page - * @return array the list of files, including meta infomation + * @return array the list of files, including meta information */ public function get_listing($encodedpath = '', $page = '') { // This methods diff --git a/versioned_docs/version-4.1/apis/plugintypes/repository/index.md b/versioned_docs/version-4.1/apis/plugintypes/repository/index.md index a71418fb7e..d2d1dbf193 100644 --- a/versioned_docs/version-4.1/apis/plugintypes/repository/index.md +++ b/versioned_docs/version-4.1/apis/plugintypes/repository/index.md @@ -457,7 +457,7 @@ This function will return a list of files to be displayed to the user, the list * * @param string $encodedpath * @param string $page - * @return array the list of files, including meta infomation + * @return array the list of files, including meta information */ public function get_listing($encodedpath = '', $page = '') { // This methods diff --git a/versioned_docs/version-4.2/apis/plugintypes/repository/_examples/lib.php b/versioned_docs/version-4.2/apis/plugintypes/repository/_examples/lib.php index 30346d3054..366cb2711d 100644 --- a/versioned_docs/version-4.2/apis/plugintypes/repository/_examples/lib.php +++ b/versioned_docs/version-4.2/apis/plugintypes/repository/_examples/lib.php @@ -12,7 +12,7 @@ class repository_pluginname extends repository { * * @param string $encodedpath * @param string $page - * @return array the list of files, including meta infomation + * @return array the list of files, including meta information */ public function get_listing($encodedpath = '', $page = '') { // This methods diff --git a/versioned_docs/version-4.2/apis/plugintypes/repository/index.md b/versioned_docs/version-4.2/apis/plugintypes/repository/index.md index a71418fb7e..d2d1dbf193 100644 --- a/versioned_docs/version-4.2/apis/plugintypes/repository/index.md +++ b/versioned_docs/version-4.2/apis/plugintypes/repository/index.md @@ -457,7 +457,7 @@ This function will return a list of files to be displayed to the user, the list * * @param string $encodedpath * @param string $page - * @return array the list of files, including meta infomation + * @return array the list of files, including meta information */ public function get_listing($encodedpath = '', $page = '') { // This methods From d7e25d00bedaf36577601c5ce0868fc7f1dc861d Mon Sep 17 00:00:00 2001 From: Artur Neumann Date: Fri, 15 Sep 2023 16:25:43 +0545 Subject: [PATCH 23/37] [docs] repository plugins: typo fixes --- docs/apis/plugintypes/repository/index.md | 2 +- versioned_docs/version-4.1/apis/plugintypes/repository/index.md | 2 +- versioned_docs/version-4.2/apis/plugintypes/repository/index.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/apis/plugintypes/repository/index.md b/docs/apis/plugintypes/repository/index.md index a71418fb7e..d1e541cc3d 100644 --- a/docs/apis/plugintypes/repository/index.md +++ b/docs/apis/plugintypes/repository/index.md @@ -192,7 +192,7 @@ function supported_filetypes() { A system-wide instance of a repository is created when it is enabled. It is also possible to support both course, and user specific repositories.. This can be achieved by setting the `enablecourseinstances` and `enableuserinstances` options. There are three ways that this can be done: 1. Define **$string\['enablecourseinstances'\]** and **$string\['enableuserinstances'\]** in your plugin's language file. You can check an example in the [filesystem repository](https://github.com/moodle/moodle/blob/v4.0.0/repository/filesystem/lang/en/repository_filesystem.php). -2. The plugin must provide a **get_instance_option_names** method which returns at least one instance option name. This method defined the specific instances options, if none instance atrtribute is needed, the system will not allow the plguin to define course and user instances. Note, you must not define the form fields for these options in the **type_config_form()** function. For example, [filesystem repository](https://github.com/moodle/moodle/blob/v4.0.0/repository/filesystem/lib.php#L439). +2. The plugin must provide a **get_instance_option_names** method which returns at least one instance option name. This method defined the specific instances options, if none instance attribute is needed, the system will not allow the plugin to define course and user instances. Note, you must not define the form fields for these options in the **type_config_form()** function. For example, [filesystem repository](https://github.com/moodle/moodle/blob/v4.0.0/repository/filesystem/lib.php#L439). 3. Several 'core' repositories use the **db/install.php** to create the original repository instance by constructing an instance of the **repository_type** class. The options can be defined in the array passed as the second parameter to the constructor. For example [Wikipedia repository](https://github.com/moodle/moodle/blob/v4.0.0/repository/wikimedia/db/install.php). ### Developer-defined API diff --git a/versioned_docs/version-4.1/apis/plugintypes/repository/index.md b/versioned_docs/version-4.1/apis/plugintypes/repository/index.md index d2d1dbf193..c902c44865 100644 --- a/versioned_docs/version-4.1/apis/plugintypes/repository/index.md +++ b/versioned_docs/version-4.1/apis/plugintypes/repository/index.md @@ -192,7 +192,7 @@ function supported_filetypes() { A system-wide instance of a repository is created when it is enabled. It is also possible to support both course, and user specific repositories.. This can be achieved by setting the `enablecourseinstances` and `enableuserinstances` options. There are three ways that this can be done: 1. Define **$string\['enablecourseinstances'\]** and **$string\['enableuserinstances'\]** in your plugin's language file. You can check an example in the [filesystem repository](https://github.com/moodle/moodle/blob/v4.0.0/repository/filesystem/lang/en/repository_filesystem.php). -2. The plugin must provide a **get_instance_option_names** method which returns at least one instance option name. This method defined the specific instances options, if none instance atrtribute is needed, the system will not allow the plguin to define course and user instances. Note, you must not define the form fields for these options in the **type_config_form()** function. For example, [filesystem repository](https://github.com/moodle/moodle/blob/v4.0.0/repository/filesystem/lib.php#L439). +2. The plugin must provide a **get_instance_option_names** method which returns at least one instance option name. This method defined the specific instances options, if none instance attribute is needed, the system will not allow the plugin to define course and user instances. Note, you must not define the form fields for these options in the **type_config_form()** function. For example, [filesystem repository](https://github.com/moodle/moodle/blob/v4.0.0/repository/filesystem/lib.php#L439). 3. Several 'core' repositories use the **db/install.php** to create the original repository instance by constructing an instance of the **repository_type** class. The options can be defined in the array passed as the second parameter to the constructor. For example [Wikipedia repository](https://github.com/moodle/moodle/blob/v4.0.0/repository/wikimedia/db/install.php). ### Developer-defined API diff --git a/versioned_docs/version-4.2/apis/plugintypes/repository/index.md b/versioned_docs/version-4.2/apis/plugintypes/repository/index.md index d2d1dbf193..c902c44865 100644 --- a/versioned_docs/version-4.2/apis/plugintypes/repository/index.md +++ b/versioned_docs/version-4.2/apis/plugintypes/repository/index.md @@ -192,7 +192,7 @@ function supported_filetypes() { A system-wide instance of a repository is created when it is enabled. It is also possible to support both course, and user specific repositories.. This can be achieved by setting the `enablecourseinstances` and `enableuserinstances` options. There are three ways that this can be done: 1. Define **$string\['enablecourseinstances'\]** and **$string\['enableuserinstances'\]** in your plugin's language file. You can check an example in the [filesystem repository](https://github.com/moodle/moodle/blob/v4.0.0/repository/filesystem/lang/en/repository_filesystem.php). -2. The plugin must provide a **get_instance_option_names** method which returns at least one instance option name. This method defined the specific instances options, if none instance atrtribute is needed, the system will not allow the plguin to define course and user instances. Note, you must not define the form fields for these options in the **type_config_form()** function. For example, [filesystem repository](https://github.com/moodle/moodle/blob/v4.0.0/repository/filesystem/lib.php#L439). +2. The plugin must provide a **get_instance_option_names** method which returns at least one instance option name. This method defined the specific instances options, if none instance attribute is needed, the system will not allow the plugin to define course and user instances. Note, you must not define the form fields for these options in the **type_config_form()** function. For example, [filesystem repository](https://github.com/moodle/moodle/blob/v4.0.0/repository/filesystem/lib.php#L439). 3. Several 'core' repositories use the **db/install.php** to create the original repository instance by constructing an instance of the **repository_type** class. The options can be defined in the array passed as the second parameter to the constructor. For example [Wikipedia repository](https://github.com/moodle/moodle/blob/v4.0.0/repository/wikimedia/db/install.php). ### Developer-defined API From 838451506efd882d562894fa65fcb5c01ccee7ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikel=20Mart=C3=ADn?= Date: Tue, 23 May 2023 14:17:39 +0200 Subject: [PATCH 24/37] [docs] Migrate Custom Fields API page --- data/migratedPages.yml | 3 + docs/apis.md | 2 +- docs/apis/core/customfields/index.md | 172 +++++++++++++++++++++++++++ 3 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 docs/apis/core/customfields/index.md diff --git a/data/migratedPages.yml b/data/migratedPages.yml index 4884f30704..d750bc2fe6 100644 --- a/data/migratedPages.yml +++ b/data/migratedPages.yml @@ -136,6 +136,9 @@ Credits: Custom_Moodle_Apps: - filePath: "/general/app/customisation/custom-apps.md" slug: "/general/app/customisation/custom-apps" +Custom_fields_API: +- filePath: "/docs/apis/core/customfields/index.md" + slug: "/docs/apis/core/customfields/" DDL_functions: - filePath: "/docs/apis/core/dml/ddl.md" slug: "/docs/apis/core/dml/ddl" diff --git a/docs/apis.md b/docs/apis.md index 39d7699dfe..03f11fc61b 100644 --- a/docs/apis.md +++ b/docs/apis.md @@ -217,7 +217,7 @@ The [https://docs.moodle.org/dev/OpenBadges_User_Documentation Badges] user docu ### Custom fields API (customfield) -The [Custom fields API](https://docs.moodle.org/dev/Custom_fields_API) allows you to configure and add custom fields for different entities +The [Custom fields API](./apis/core/customfields/index.md) allows you to configure and add custom fields for different entities ## Activity module APIs diff --git a/docs/apis/core/customfields/index.md b/docs/apis/core/customfields/index.md new file mode 100644 index 0000000000..e90ee676a8 --- /dev/null +++ b/docs/apis/core/customfields/index.md @@ -0,0 +1,172 @@ +--- +title: Custom fields API +tags: [] +--- + +import { Since } from '@site/src/components'; + + + +## Custom fields API overview + +Custom fields API allows to configure custom fields that can be added to various contexts. Each **component** (or plugin) that wants to use custom fields can define several **areas**. + +:::info Example +The `core_course` component defines an area `course` that allows to add custom fields to the courses. The same component can define another area `coursecat` that will allow to add custom fields to the course categories. +::: + +Inside each area, the component/plugin can decide whether to use or not to use **itemid**. + +:::info Example +Course custom fields are the same throughout the system and they don't use `itemid` (it is always 0). But there could be an activity module that would want to configure different custom fields for each individual instance of module. Then this module would use the module id as the `itemid`, as in ([example](https://github.com/marinaglancy/moodle-mod_surveybuilder)). This would allow to create modules similar to `mod_data` and `mod_feedback` where each instance has its own set of fields. +::: + +New plugin type `customfield` was also added as part of the Custom fields API. Additional types of custom fields can be installed into `/customfield/field/`. + +## How to use custom fields + +Component/plugin that uses custom fields must define a **handler class** for each area and a **configuration page**. Handler class must be called `/customfield/_handler` and be placed in autoloaded location `/classes/customfield/_handler.php`. This class must extend **\core_customfield\handler** . Configuration page may be located anywhere. For course custom fields configuration the admin settings page is used [/course/customfield.php](https://github.com/moodle/moodle/blob/master/course/customfield.php). If the area uses `itemid` this page should take `itemid` as a parameter. + +Handler has protected constructor, to get a handler call `create()` method. Some areas may choose to return a singleton here: + +```php +$handler = HANDLERCLASS::create($itemid); +``` + +Configuration page contents will be: + +```php +$output = $PAGE->get_renderer('core_customfield'); +$outputpage = new \core_customfield\output\management($handler); +echo $output->render($outputpage); +``` + +Handler must implement all abstract methods (calculate configuration or instance context, check permissions to configure, view or edit) and also may choose to overwrite: + +```php +handler::uses_categories() +handler::generate_category_name() +handler::config_form_definition() // For example, the course_handler adds "locked" and "visibility" settings that control who can edit or view the particular field. +handler::setup_edit_page() // Sets page context/url/breadcrumb for the customfield/edit.php page, in some cases it must be overridden. +``` + +### Add custom fields to the instance edit form + +Custom fields are added to the **instances**. For example, course custom fields are added to the courses, so `courseid` is the `instanceid`. In the example of [mod_surveybuilder](https://github.com/marinaglancy/moodle-mod_surveybuilder) we use `$USER->id` as the `instanceid` (which means that in this example one user can fill the survey in one module only once). In each case of using custom fields there should be a clear concept of an **instance**. `Instanceid` is required to save the data but it may be empty when we render the instance edit form (for example, the course is not yet created). + +Developer must add custom field callbacks to the instance edit form. If the instance is "made up" (like in `mod_surveybuilder`), a new form has to be created with `id` field in it that will refer to the `instanceid`. + +The following callbacks should be used in `form definition`, `definition_after_data`, `validation` and `after form submission`: + +```php +$handler->instance_form_definition($mform) +$handler->instance_form_before_set_data() +$handler->instance_form_definition_after_data() +$handler->instance_form_validation() +$handler->instance_form_save($data) +``` + +The `instance_form_save()` method must be called after the form was saved as the `$data` parameter must have the `id` attribute. + +On deletion of an instance or on deletion of the whole item call: + +```php +$handler->delete_instance() +$handler->delete_all() +``` + +### Retrieving instances custom fields + +How custom fields are used depends entirely on the situation. + +```php title="Handler methods to retrieve custom fields values for the given instance(s)" +$handler->export_instance_data() +$handler->export_instance_data_object() +$handler->display_custom_fields_data() +``` + +Additional methods for advanced usage: + +```php +$handler->get_instance_data() +$handler->get_instances_data() +$handler->get_instance_data_for_backup() +``` + +Method `restore_instance_data_from_backup()` exists in the handler class but is not implemented. + +```php title="To retrieve the list of custom fields used in the given component/area/itemid" +$handler->get_categories_with_fields() +$handler->get_fields() +``` + +:::note +The list of fields is cached in the handler and these two functions can be called multiple times. +::: + +```php title="Example code for course custom fields. This function will return all the custom fields for a given courseid" +function get_course_metadata($courseid) { + $handler = \core_customfield\handler::get_handler('core_course', 'course'); + // This is equivalent to the line above. + //$handler = \core_course\customfield\course_handler::create(); + $datas = $handler->get_instance_data($courseid); + $metadata = []; + foreach ($datas as $data) { + if (empty($data->get_value())) { + continue; + } + $cat = $data->get_field()->get_category()->get('name'); + $metadata[$data->get_field()->get('shortname')] = $cat . ': ' . $data->get_value(); + } + return $metadata; +} +``` + +### Privacy API + +Custom fields API does not export or delete any data because it does not know how custom fields are used, what data is considered user data and if it is considered private or shared data. + +```php title="Plugins that store user information in custom fields should link subsystem in their get_metadata" +$collection->link_subsystem('core_customfield', 'privacy:metadata:customfieldpurpose'); +``` + +```php title="They can use the following methods in the export/delete functions" +use core_customfield\privacy\provider as customfield_provider; + +customfield_provider::get_customfields_data_contexts() +customfield_provider::export_customfields_data() +customfield_provider::delete_customfields_data() +customfield_provider::delete_customfields_data_for_context() +``` + +In case when custom fields configuration is considered to be user data (configuration means the definition of the fields, not the instance data), there are also couple of methods to help with privacy API implementations: + +```php +customfield_provider::get_customfields_configuration_contexts() +customfield_provider::delete_customfields_configuration() +customfield_provider::delete_customfields_configuration_for_context() +``` + +:::info +Export of configuration was not yet implemented at the time of writing this because of difficult implementation and very unclear use case. If it is needed please feel free to contribute to Moodle. +::: + +## Custom fields plugins + +Custom fields plugin type was added to allow implement different types of custom fields (somehow similar to user profile fields plugin type). Plugins are located in `/customfield/field/` and the full frankenstyle name of the plugins start with `customfield_`. + +Except for common [Plugin files](../../commonfiles/index.mdx) and tests the following classes must be present in `customfield` plugins (in respective autoloaded locations): + +```php +namespace customfield_; +class field_controller extends \core_customfield\field_controller; +class data_controller extends \core_customfield\data_controller; + +namespace customfield_\privacy; +class provider implements \core_privacy\local\metadata\null_provider, \core_customfield\privacy\customfield_provider; +``` + +## See also + +- [MDL-64626](https://tracker.moodle.org/browse/MDL-64626) - Custom fields API (Moodle 3.7+) implementations and improvements +- [MDL-57898](https://tracker.moodle.org/browse/MDL-57898) - Add custom field types plugin and course custom fields functionality From 0627aaa6781103ec5a03980d67f609dfda04ae48 Mon Sep 17 00:00:00 2001 From: Daniel Ziegenberg Date: Fri, 15 Sep 2023 15:10:55 +0200 Subject: [PATCH 25/37] [docs] update links to PHPUnit Manual Signed-off-by: Daniel Ziegenberg --- docs/apis/plugintypes/antivirus/index.mdx | 2 +- general/development/policies/codingstyle/index.md | 4 ++-- general/development/tools/gha.md | 2 +- general/development/tools/phpunit.md | 8 ++++---- .../version-4.1/apis/plugintypes/antivirus/index.mdx | 2 +- .../version-4.2/apis/plugintypes/antivirus/index.mdx | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/apis/plugintypes/antivirus/index.mdx b/docs/apis/plugintypes/antivirus/index.mdx index dc9d920541..c28e2d0276 100644 --- a/docs/apis/plugintypes/antivirus/index.mdx +++ b/docs/apis/plugintypes/antivirus/index.mdx @@ -109,6 +109,6 @@ Writing unit tests is strongly encouraged as it can help to identify bugs, or ch Since antivirus plugins typically rely on an external dependency, it is usually a good idea to replace the real component with a test "double". You can see an example of this in Moodle in the [antivirus_clamav unit tests](https://github.com/moodle/moodle/blob/81407f18ecff1fded66a9d8bdc25bbf9d8ccd5ca/lib/antivirus/clamav/tests/scanner_test.php#L45-L56). -The PHPUnit Manual contains a dedicated [section on Test Doubles](https://phpunit.de/manual/current/en/test-doubles.html). +The PHPUnit Manual contains a dedicated [section on Test Doubles](https://docs.phpunit.de/en/9.6/test-doubles.html). You may also wish to include some tests of the real system to ensure that upgrades to the Antivirus software do not break your plugin. diff --git a/general/development/policies/codingstyle/index.md b/general/development/policies/codingstyle/index.md index f84f9de2d3..ba07bd1913 100644 --- a/general/development/policies/codingstyle/index.md +++ b/general/development/policies/codingstyle/index.md @@ -1769,9 +1769,9 @@ There are some tags that are only allowed within some contexts and not globally. - `@Given`, `@When`, `@Then`, within the [behat steps definitions](../../tools/behat/writing.md#writing-new-acceptance-test-step-definitions). - `@covers`, `@coversDefaultClass`, `@coversNothing`, `@uses` to better control coverage within [unit tests](https://docs.moodle.org/dev/Writing_PHPUnit_tests#Generators). - `@dataProvider` and `@testWith`, to provide example data and expectations, within [unit tests](https://docs.moodle.org/dev/Writing_PHPUnit_tests#Generators). -- `@depends`, to express dependencies between tests, where each producer returned data in passed to consumers. See [`@depends` examples](https://phpunit.readthedocs.io/en/9.5/writing-tests-for-phpunit.html#writing-tests-for-phpunit-examples-stacktest2-php) for more information. +- `@depends`, to express dependencies between tests, where each producer returned data in passed to consumers. See [`@depends` examples](https://docs.phpunit.de/en/9.6/writing-tests-for-phpunit.html#writing-tests-for-phpunit-examples-stacktest2-php) for more information. - `@group`, for easier collecting unit tests together, following the guidelines in the [PHPUnit MoodleDocs](../../tools/phpunit.md#using-the-group-annotation). -- `@requires`, to specify unit test requirements and skip if not fulfilled. See [`@requires` usages](https://phpunit.readthedocs.io/en/9.5/incomplete-and-skipped-tests.html#incomplete-and-skipped-tests-requires-tables-api) for more information. +- `@requires`, to specify unit test requirements and skip if not fulfilled. See [`@requires` usages](https://docs.phpunit.de/en/9.6/incomplete-and-skipped-tests.html#incomplete-and-skipped-tests-requires-tables-api) for more information. - `@runTestsInSeparateProcesses` and `@runInSeparateProcess`, to execute an individual test or a testcase in isolation. To be used only when strictly needed. ### Files diff --git a/general/development/tools/gha.md b/general/development/tools/gha.md index f539a4dab9..dec677cb31 100644 --- a/general/development/tools/gha.md +++ b/general/development/tools/gha.md @@ -42,7 +42,7 @@ And that's all, pretty nice, simple and effective. ### How do I configure the PHPUnit executions -Since Moodle 3.11.8 and 4.0.2, it's possible to configure the PHPUnit executions by creating a [GitHub repository secret](https://docs.github.com/en/actions/security-guides/encrypted-secrets#creating-encrypted-secrets-for-a-repository) named `phpunit_options`. Just setup its value to the options that you want to pass to PHPUnit and they will come into effect for all runs. For a list of [PHPUnit available options](https://phpunit.readthedocs.io/en/9.5/textui.html#command-line-options) use the `vendor/bin/phpunit --help` command. +Since Moodle 3.11.8 and 4.0.2, it's possible to configure the PHPUnit executions by creating a [GitHub repository secret](https://docs.github.com/en/actions/security-guides/encrypted-secrets#creating-encrypted-secrets-for-a-repository) named `phpunit_options`. Just setup its value to the options that you want to pass to PHPUnit and they will come into effect for all runs. For a list of [PHPUnit available options](https://docs.phpunit.de/en/9.6/textui.html#command-line-options) use the `vendor/bin/phpunit --help` command. ## Moodle plugins diff --git a/general/development/tools/phpunit.md b/general/development/tools/phpunit.md index 7a0b89d5e8..8bfcb6c32f 100644 --- a/general/development/tools/phpunit.md +++ b/general/development/tools/phpunit.md @@ -56,9 +56,9 @@ The following table shows what PHPUnit version is installed in which Moodle vers | Moodle version | PHPUnit version | Links | | --- | --- |---------------------------------------------------------| -| Moodle 3.11 - 4.0 | PHPUnit 9.5 | [Documentation](https://phpunit.readthedocs.io/en/9.5/) | -| Moodle 3.10 | PHPUnit 8.5 | [Documentation](https://phpunit.readthedocs.io/en/8.5/) | -| Moodle 3.7 - 3.9 | PHPUnit 7.5 | [Documentation](https://phpunit.readthedocs.io/en/7.5/) | +| Moodle 3.11 - 4.0 | PHPUnit 9.5 | [Documentation](https://docs.phpunit.de/en/9.6/) | +| Moodle 3.10 | PHPUnit 8.5 | [Documentation](https://docs.phpunit.de/en/8.5/) | +| Moodle 3.7 - 3.9 | PHPUnit 7.5 | [Documentation](https://docs.phpunit.de/en/7.5/) | | Moodle 3.4 - 3.6 | PHPUnit 6.5 | [Documentation](https://phpunit.de/manual/6.5/en/) | | Moodle 3.2 - 3.3 | PHPUnit 5.5 | [Documentation](https://phpunit.de/manual/5.5/en/) | | Moodle 3.1 | PHPUnit 4.8.27 | [Documentation 4.8](https://phpunit.de/manual/4.8/en/) | @@ -277,7 +277,7 @@ define('TEST_EXTERNAL_FILES_HTTP_URL', 'http://localhost/moodle-exttests'); ## Writing new tests -- read [official PHPUnit online documentation](http://www.phpunit.de/manual/current/en/) +- read [official PHPUnit online documentation](https://docs.phpunit.de/en/9.6/) - see [Writing PHPUnit tests](https://docs.moodle.org/dev/Writing_PHPUnit_tests) ## PHPUnit support in IDEs diff --git a/versioned_docs/version-4.1/apis/plugintypes/antivirus/index.mdx b/versioned_docs/version-4.1/apis/plugintypes/antivirus/index.mdx index dc9d920541..c28e2d0276 100644 --- a/versioned_docs/version-4.1/apis/plugintypes/antivirus/index.mdx +++ b/versioned_docs/version-4.1/apis/plugintypes/antivirus/index.mdx @@ -109,6 +109,6 @@ Writing unit tests is strongly encouraged as it can help to identify bugs, or ch Since antivirus plugins typically rely on an external dependency, it is usually a good idea to replace the real component with a test "double". You can see an example of this in Moodle in the [antivirus_clamav unit tests](https://github.com/moodle/moodle/blob/81407f18ecff1fded66a9d8bdc25bbf9d8ccd5ca/lib/antivirus/clamav/tests/scanner_test.php#L45-L56). -The PHPUnit Manual contains a dedicated [section on Test Doubles](https://phpunit.de/manual/current/en/test-doubles.html). +The PHPUnit Manual contains a dedicated [section on Test Doubles](https://docs.phpunit.de/en/9.6/test-doubles.html). You may also wish to include some tests of the real system to ensure that upgrades to the Antivirus software do not break your plugin. diff --git a/versioned_docs/version-4.2/apis/plugintypes/antivirus/index.mdx b/versioned_docs/version-4.2/apis/plugintypes/antivirus/index.mdx index dc9d920541..c28e2d0276 100644 --- a/versioned_docs/version-4.2/apis/plugintypes/antivirus/index.mdx +++ b/versioned_docs/version-4.2/apis/plugintypes/antivirus/index.mdx @@ -109,6 +109,6 @@ Writing unit tests is strongly encouraged as it can help to identify bugs, or ch Since antivirus plugins typically rely on an external dependency, it is usually a good idea to replace the real component with a test "double". You can see an example of this in Moodle in the [antivirus_clamav unit tests](https://github.com/moodle/moodle/blob/81407f18ecff1fded66a9d8bdc25bbf9d8ccd5ca/lib/antivirus/clamav/tests/scanner_test.php#L45-L56). -The PHPUnit Manual contains a dedicated [section on Test Doubles](https://phpunit.de/manual/current/en/test-doubles.html). +The PHPUnit Manual contains a dedicated [section on Test Doubles](https://docs.phpunit.de/en/9.6/test-doubles.html). You may also wish to include some tests of the real system to ensure that upgrades to the Antivirus software do not break your plugin. From 685cfc460562fc039ecad45b942a21dcf135d2d8 Mon Sep 17 00:00:00 2001 From: Marina Glancy Date: Fri, 15 Sep 2023 17:38:55 +0100 Subject: [PATCH 26/37] [docs] corrected custom fields types --- docs/apis/core/customfields/index.md | 4 +++- docs/apis/plugintypes/customfield/index.md | 15 ++++++++------- docs/apis/plugintypes/index.md | 2 +- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/docs/apis/core/customfields/index.md b/docs/apis/core/customfields/index.md index e90ee676a8..cddf2bfeff 100644 --- a/docs/apis/core/customfields/index.md +++ b/docs/apis/core/customfields/index.md @@ -1,6 +1,8 @@ --- title: Custom fields API -tags: [] +tags: + - customfield + - Custom field --- import { Since } from '@site/src/components'; diff --git a/docs/apis/plugintypes/customfield/index.md b/docs/apis/plugintypes/customfield/index.md index 78a507c5f6..920373ead2 100644 --- a/docs/apis/plugintypes/customfield/index.md +++ b/docs/apis/plugintypes/customfield/index.md @@ -1,12 +1,12 @@ --- -title: Course Custom fields +title: Custom fields tags: - - core_course + - customfield - Course - Custom field --- -Course custom fields allow you to create field types to be used for course custom fields. Instances of these field types can be added to a course. for example, if you want to display radio buttons on the course edit page, then you can create a radio custom course field plugin. +Custom fields allow you to create field types to be used for custom fields. Instances of these field types can be added to the respective areas that implement [Custom fields API](/docs/apis/core/customfields). Currently in Moodle core only courses implement this API, however custom fields are also used in addon plugins for other areas. For example, if you want to display radio buttons on the course edit page, then you can add an instance of a radio custom field plugin to the Course custom fields configuration. import { Lang, @@ -16,7 +16,7 @@ import DataController from './_files/data_controller'; ## File structure -Course custom field plugins are located in the `/customfield/field` directory. A plugin should not include any custom files outside of it's own plugin folder. +Custom field plugins are located in the `/customfield/field` directory. A plugin should not include any custom files outside of it's own plugin folder. Each plugin is in a separate subdirectory and consists of a number of _mandatory files_ and any other files the developer is going to use. @@ -44,10 +44,10 @@ customfield/field/checkbox -A course custom field plugin requires two _controller_ classes: +A custom field plugin requires two _controller_ classes: - a _field_ controller, which describes the field itself; and -- a _data_ controller, which describes with interface within the context of the course page. +- a _data_ controller, which describes with interface within the context of the instance (i.e. course). ### Field Controller @@ -121,8 +121,9 @@ The `datafield()` function returns an enumerated string and describes which data #### instance_form_definition() -The `instance_form_definition()` function adds any required field elements that are displayed on the course editing page. +The `instance_form_definition()` function adds any required field elements that are displayed on the instance editing page (i.e. on the course settings page). ## See Also +- [Custom files API](/docs/apis/core/customfields) - [User Profile Fields](https://docs.moodle.org/dev/User_profile_fields) diff --git a/docs/apis/plugintypes/index.md b/docs/apis/plugintypes/index.md index 614ec4bb83..9ac047503a 100644 --- a/docs/apis/plugintypes/index.md +++ b/docs/apis/plugintypes/index.md @@ -55,7 +55,7 @@ The underscore character is not supported in activity modules for legacy reasons | [Assignment submission plugins](./assign/submission.md) | assignsubmission | /mod/assign/submission | Different forms of assignment submissions | 2.3+ | | [Assignment feedback plugins](./assign/feedback.md) | assignfeedback | /mod/assign/feedback | Different forms of assignment feedbacks | 2.3+ | | [Book tools](./mod_book/index.md) | booktool | /mod/book/tool | Small information-displays or tools that can be moved around pages | 2.1+ | -| [Course Custom fields](./customfield/index.md) | customfield | /customfield/field | Custom field types, used in Custom course fields | 3.7+ | +| [Custom fields](./customfield/index.md) | customfield | /customfield/field | Custom field types, used in Custom course fields | 3.7+ | | [Database fields](./mod_data/fields.md) | datafield | /mod/data/field | Different types of data that may be added to the Database activity module | 1.6+ | | [Database presets](./mod_data/presets.md) | datapreset | /mod/data/preset | Pre-defined templates for the Database activity module | 1.6+ | | [LTI sources](https://docs.moodle.org/dev/External_tool_source) | ltisource | /mod/lti/source | LTI providers can be added to external tools easily through the external tools interface see [Documentation on External Tools](https://docs.moodle.org/en/External_tool). This type of plugin is specific to LTI providers that need a plugin that can register custom handlers to process LTI messages | 2.7+ | From 5e241a7f9be062655c7f414dbc5e74e71993d460 Mon Sep 17 00:00:00 2001 From: sarjona Date: Sun, 17 Sep 2023 05:36:41 +0000 Subject: [PATCH 27/37] [repo] Update Moodle Component metadata --- data/component-spelling.txt | 1 - data/libraries.json | 4 ++-- data/moodle-contributors.txt | 1 + general/community/credits/thirdpartylibs.md | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/data/component-spelling.txt b/data/component-spelling.txt index 1671091138..27e0f5c114 100644 --- a/data/component-spelling.txt +++ b/data/component-spelling.txt @@ -89,7 +89,6 @@ qbehaviour qformat qtype question -quicktime rating report reportbuilder diff --git a/data/libraries.json b/data/libraries.json index 119859a582..5c14b917a9 100644 --- a/data/libraries.json +++ b/data/libraries.json @@ -314,7 +314,7 @@ "location": "lib/emoji-data", "name": "Emoji data", "description": "Library to parse easily data and sprite sheets for emoji.", - "version": "14.0.1", + "version": "15.0.1", "license": "MIT", "repository": "https://github.com/iamcal/emoji-data/", "customised": true @@ -707,7 +707,7 @@ "location": "lib/phpspreadsheet", "name": "PhpSpreadsheet", "description": "Library to read, write and create spreadsheet documents in PHP.", - "version": "1.28.0", + "version": "1.29.0", "license": "MIT", "repository": "https://github.com/PHPOffice/PhpSpreadsheet", "customised": true diff --git a/data/moodle-contributors.txt b/data/moodle-contributors.txt index 59aa9fce60..5e44ebea1c 100644 --- a/data/moodle-contributors.txt +++ b/data/moodle-contributors.txt @@ -1414,6 +1414,7 @@ skaldrom skodak spvickers stabijoh +stemiwe stevando stigbjarne stronk7 diff --git a/general/community/credits/thirdpartylibs.md b/general/community/credits/thirdpartylibs.md index 104b17a504..0082f8f41c 100644 --- a/general/community/credits/thirdpartylibs.md +++ b/general/community/credits/thirdpartylibs.md @@ -284,7 +284,7 @@ CodeMirror is a versatile text editor implemented in JavaScript for the browser. Library to parse easily data and sprite sheets for emoji. - **Location**: lib/emoji-data -- **Version**: 14.0.1 (with Moodle customisations) +- **Version**: 15.0.1 (with Moodle customisations) - **License**: MIT - **URL**: [https://github.com/iamcal/emoji-data/](https://github.com/iamcal/emoji-data/) - **Copyright holders**: @@ -633,7 +633,7 @@ Class for sending email using either sendmail, PHP mail(), or SMTP. Methods are Library to read, write and create spreadsheet documents in PHP. - **Location**: lib/phpspreadsheet -- **Version**: 1.28.0 (with Moodle customisations) +- **Version**: 1.29.0 (with Moodle customisations) - **License**: MIT - **URL**: [https://github.com/PHPOffice/PhpSpreadsheet](https://github.com/PHPOffice/PhpSpreadsheet) From ae8ac5a807c6fc42c434663b63b311e6c7125dd3 Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Mon, 18 Sep 2023 09:05:26 +0800 Subject: [PATCH 28/37] [docs] Remove moodle-local_codechecker install path for phpcs This was recently removed. See https://github.com/moodlehq/moodle-local_codechecker/commit/a51fb0b6427f9360d9c9768d00a53272e96b3ba7 for further information. --- general/development/tools/phpcs.md | 42 ------------------------------ 1 file changed, 42 deletions(-) diff --git a/general/development/tools/phpcs.md b/general/development/tools/phpcs.md index 17c188be53..8edb386637 100644 --- a/general/development/tools/phpcs.md +++ b/general/development/tools/phpcs.md @@ -72,48 +72,6 @@ This approach is **not recommended** and is only preserved for reference. ::: -## Moodle plugin - -Moodle includes a copy of the PHPCodeSniffer package, and the Moodle ruleset, as part of the [`moodle-local_codechecker`](https://github.com/moodlehq/moodle-local_codechecker) Moodle plugin. This makes the code checker available via a web-based interface for checking the syntax of a given file or folder. - -One way to install this plugin is using `git clone`: - -```console -git clone https://github.com/moodlehq/moodle-local_codechecker.git local/codechecker -``` - -It is recommended that you add the plugin to your _local_ git ignore: - -```console -echo local/codechecker >> .git/info/exclude -``` - -:::info - -The `.git/info/exclude` file is a per-repository version of the `.gitignore` file. Whilst `.gitignore` is tracked within the Moodle codebase and a version is shipped with Moodle, the `.git/info/exclude` file is local to your git clone. - -See the [gitignore](https://git-scm.com/docs/gitignore) documentation for more information on the gitignore feature. - -::: - -:::note - -If you are not installing the moodle ruleset globally, and are instead using the [`local_codechecker`](https://github.com/moodlehq/moodle-local_codechecker) plugin, then you _must_ also use the version of phpcs distributed in the plugin. - -This is located at `local/codechecker/phpcs/bin/phpcs`. - -::: - -Once installed a new codechecker option will appear in the Site administration -> Development page. - -This page allows for the code in a specified directory to be checked, for example if you wanted to check the code for the `shortanswer` question type you would enter - -``` -/question/type/shortanswer -``` - -You would then be presented with a list of the count of files processed and any warnings or errors. - ## Editor integrations Many modern editors and IDEs will natively integrate with PHPCodeSniffer, and since Moodle versions 3.11.7, 4.0.1, and 4.1.0, no additional configuration is required. From 15ca0096ec01786be8a2ce1f36d670906657b28b Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Mon, 18 Sep 2023 09:36:58 +0800 Subject: [PATCH 29/37] [docs] Mention new moodle-extra style --- general/development/tools/phpcs.md | 59 +++++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 5 deletions(-) diff --git a/general/development/tools/phpcs.md b/general/development/tools/phpcs.md index 8edb386637..b9642d4f85 100644 --- a/general/development/tools/phpcs.md +++ b/general/development/tools/phpcs.md @@ -9,22 +9,25 @@ sidebar_position: 1 import { Since } from '@site/src/components'; -## Overview +[PHPCodeSniffer](https://github.com/squizlabs/PHP_CodeSniffer) is a tool used to analyse PHP code using a set of rules. In many cases these rules can be used to automatically fix the errors they identify. -This document describes the various code sniffing tools that Moodle recommends, their purpose, and their usage. +Moodle has two sets of published rule-sets intended to meet the [Moodle Coding Style](../policies/codingstyle/index.md), and identify any parts of the code do not conform to this style. These are: -[PHPCodeSniffer](https://github.com/squizlabs/PHP_CodeSniffer) is a tool used to analyse PHP code using a set of rules. In many cases these rules can be used to automatically fix the errors they identify. +- `moodle` - The standard ruleset which meets all variants of the coding style +- `moodle-extra` - The extended standard which includes recommended best practices -Moodle has published a ruleset intended to meet the [Moodle Coding Style](../policies/codingstyle/index.md), and identify any parts of the code do not conform to this style. +We recommend use of the `moodle-extra` standard, particularly when writing new code. ## Installation -It is recommend that both the phpcs scripts, and the Moodle ruleset, are installed globally using Composer: +The recommended method of installation is via the global composer command: ```console composer global require moodlehq/moodle-cs ``` +This ensures that you have a single copy of the standard used for all code. + ### Configuration @@ -33,6 +36,52 @@ A PHPCS configuration is included in the Moodle codebase and ensures that the co This can be further extended by generating an additional configuration to ignore all third-party libraries using the `grunt ignorefiles` command. See [grunt](./nodejs.md#grunt) for further information on using Grunt. +If you would like to make use of the `moodle-extra` standard then you should create a `.phpcs.xml` file with the following content: + +```xml + + + + + +``` + +This will extend the standard configuration, and apply the extra standard on top of it. + +#### Moodle 3.10 and earlier + +The easiest way to have PHP CodeSniffer pick up your preferred style is via a local configuration file. + +You can create a file named `.phpcs.xml` with the following contents: + +```xml + + + + +``` + +If you wish to use the `moodle-extra` coding style, then you can use the following content: + +```xml + + + + +``` + +:::info + +Third-party library code will not be ignored with these versions of Moodle. + +::: + +:::tip Ignoring the file with Git + +We recommend configuring your code checkout to ignore the `.phpcs.xml` file by adding a local ignore record to `.git/info/exclude` + +::: + #### Community plugins, and older Moodle versions If you are developing your own plugin outside of the main Moodle directory, or you are working with an older version of Moodle, the easiest way to configure phpcs to use the Moodle ruleset is by creating a local `phpcs.xml.dist` configuration at the root directory of your repository with the following content: From 3ac7186a2bd8642fb26e4c12342f53be82ace074 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CFilippo=E2=80=9D?= <“crnfpp@unife.it”> Date: Mon, 18 Sep 2023 12:13:13 +0200 Subject: [PATCH 30/37] [docs] Message API --- data/migratedPages.yml | 3 + docs/apis.md | 2 +- docs/apis/_files/db-messages-php.mdx | 2 +- docs/apis/core/message/index.md | 210 ++++++++++++++++++ .../development/custom-push-notifications.md | 4 +- general/releases/3.5.md | 4 +- 6 files changed, 219 insertions(+), 6 deletions(-) create mode 100644 docs/apis/core/message/index.md diff --git a/data/migratedPages.yml b/data/migratedPages.yml index d750bc2fe6..cb9b13d63e 100644 --- a/data/migratedPages.yml +++ b/data/migratedPages.yml @@ -267,6 +267,9 @@ Lock_API: Machine_learning_backends: - filePath: "/docs/apis/plugintypes/mlbackend/index.md" slug: "/docs/apis/plugintypes/mlbackend/" +Message_API: +- filePath: "/docs/apis/core/message/index.md" + slug: "/docs/apis/core/message/" Mission: - filePath: "/general/community/mission.md" slug: "/general/community/mission" diff --git a/docs/apis.md b/docs/apis.md index 03f11fc61b..c1ce928e2c 100644 --- a/docs/apis.md +++ b/docs/apis.md @@ -139,7 +139,7 @@ The [Lock API](./apis/core/lock/index.md) lets you synchronise processing betwee ### Message API (message) -The [Message API](https://docs.moodle.org/dev/Message_API) lets you post messages to users. They decide how they want to receive them. +The [Message API](./apis/core/message/index.md) lets you post messages to users. They decide how they want to receive them. ### Media API (media) diff --git a/docs/apis/_files/db-messages-php.mdx b/docs/apis/_files/db-messages-php.mdx index 000225f1dc..2b52b7cf0a 100644 --- a/docs/apis/_files/db-messages-php.mdx +++ b/docs/apis/_files/db-messages-php.mdx @@ -1,4 +1,4 @@ The `db/messages.php` file allows you to declare the messages that your plugin sends. -See the [Message API](https://docs.moodle.org/dev/Message_API) documentation for further information. +See the [Message API](../core/message/index.md) documentation for further information. diff --git a/docs/apis/core/message/index.md b/docs/apis/core/message/index.md new file mode 100644 index 0000000000..44d674fb92 --- /dev/null +++ b/docs/apis/core/message/index.md @@ -0,0 +1,210 @@ +--- +title: Message API +tags: + - API + - Tutorial + - Plugins + - Messaging +--- + +import { Since } from '@site/src/components'; + +## What is this document? + +This document describes how to make use of the Moodle Messaging API to send messages to Moodle users. + +If you are after a general introduction on using the Moodle Messaging system go to [messaging user documentation](https://docs.moodle.org/en/Messaging). + +If you are looking for details of how the Messaging system's internal structure was implemented, go to [Messaging 2.0](https://docs.moodle.org/dev/Messaging_2.0). + +If you are looking for instructions on the implementation of a custom message processor (a component that receives messages sent to a user), go to [Messaging custom components](https://docs.moodle.org/dev/Messaging_custom_components). + +If you are looking for instructions on sending messages programatically within Moodle then read on... + +## Overview + +Moodle components have the ability to send messages to users via the Moodle messaging system. Any type of component, for example a plugin or block, can register as a message producer then send messages to users. + +## File locations + +The Message API code is contained within `lib/messagelib.php` and is automatically included for you during page setup. + +## Functions + +`message_send()` is the primary point of contact for the message API. Call it to send a message to a user. You can find a full description of the arguments that must be supplied at (link to phpdocs). There is also an example below. + +## Message popup + + + +A JavaScript popup can be displayed through a link to invite a user to message another. In order to use this feature, you need to require the JavaScript libraries using `message_messenger_requirejs()` and create a link with the attributes returned by `message_messenger_sendmessage_link_params()`. More in the examples. + +## Examples + +### How to register as a message producer + +The messages produced by a message provider is defined in the `/db/messages.php` file of a component. Below is code from the quiz module's `messages.php` file, shown as an example. + +```php +defined('MOODLE_INTERNAL') || die(); +$messageproviders = array ( + // Notify teacher that a student has submitted a quiz attempt + 'submission' => array ( + 'capability' => 'mod/quiz:emailnotifysubmission' + ), + // Confirm a student's quiz attempt + 'confirmation' => array ( + 'capability' => 'mod/quiz:emailconfirmsubmission' + ) +); +``` + +The quiz can send two kinds of messages, quiz "submission" and "confirmation" notifications. Each message type is only available to users with the appropriate capability. Please note that the capability is checked at the system level context. Users who have this capability will have this message listed in their messaging preferences. You can omit the capability section if your message should be visible for all users. For example forum post notifications are available to all users. + +```php +$messageproviders = array ( + // Ordinary single forum posts + 'posts' => array ( + ) +); +``` + +When displaying your message types in a user's messaging preferences it will use a string from your component's language file called `messageprovider:messagename`. For example here are the relevant strings from the quiz's language file. + +```php +$string['messageprovider:confirmation'] = 'Confirmation of your own quiz submissions'; +$string['messageprovider:submission'] = 'Notification of quiz submissions'; +``` + +Once your `messages.php` is complete you need to increase the version number of your component in its `version.php`. That will cause Moodle to check `messages.php` looking for new or changed message definitions. Log in as an admin and go to `/admin/index.php` (the Notifications page) to start the upgrade process. + +### Setting defaults + +```php title="The default processor can be set using an element of the array" + + 'mynotification' => [ + 'defaults' => [ + 'popup' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_LOGGEDIN + MESSAGE_DEFAULT_LOGGEDOFF, + 'email' => MESSAGE_PERMITTED + ], + ], +``` + +With that setting email will be permitted but disabled for each user by default. It can be turned on by each user through the `preferences/notification` preferences options (`/message/notificationpreferences.php?userid=X`) +The possible values are recorded in the lib.php file of messaging + +```php +/** + * Define contants for messaging default settings population. For unambiguity of + * plugin developer intentions we use 4-bit value (LSB numbering): + * bit 0 - whether to send message when user is loggedin (MESSAGE_DEFAULT_LOGGEDIN) + * bit 1 - whether to send message when user is loggedoff (MESSAGE_DEFAULT_LOGGEDOFF) + * bit 2..3 - messaging permission (MESSAGE_DISALLOWED|MESSAGE_PERMITTED|MESSAGE_FORCED) + * + * MESSAGE_PERMITTED_MASK contains the mask we use to distinguish permission setting + */ +``` + +Note that if you change the values in message.php and then upgrade the plugin the values will not automatically be changed in the `config_plugins` table where they are stored. + +### How to send a message + + + +Here is example code showing you how to actually send a notification message. The example shows the construction of a object with specific properties, which is then passed to the `message_send()` function that uses the information to send a message. + +```php +$message = new \core\message\message(); +$message->component = 'mod_yourmodule'; // Your plugin's name +$message->name = 'mynotification'; // Your notification name from message.php +$message->userfrom = core_user::get_noreply_user(); // If the message is 'from' a specific user you can set them here +$message->userto = $user; +$message->subject = 'message subject 1'; +$message->fullmessage = 'message body'; +$message->fullmessageformat = FORMAT_MARKDOWN; +$message->fullmessagehtml = '

message body

'; +$message->smallmessage = 'small message'; +$message->notification = 1; // Because this is a notification generated from Moodle, not a user-to-user message +$message->contexturl = (new \moodle_url('/course/'))->out(false); // A relevant URL for the notification +$message->contexturlname = 'Course list'; // Link title explaining where users get to for the contexturl +$content = array('*' => array('header' => ' test ', 'footer' => ' test ')); // Extra content for specific processor +$message->set_additional_content('email', $content); + +// You probably don't need attachments but if you do, here is how to add one +$usercontext = context_user::instance($user->id); +$file = new stdClass; +$file->contextid = $usercontext->id; +$file->component = 'user'; +$file->filearea = 'private'; +$file->itemid = 0; +$file->filepath = '/'; +$file->filename = '1.txt'; +$file->source = 'test'; + +$fs = get_file_storage(); +$file = $fs->create_file_from_string($file, 'file1 content'); +$message->attachment = $file; + +// Actually send the message +$messageid = message_send($message); +``` + +```php title="Before 2.9 message data used to be a stdClass object as shown below (This formation of a message will no longer work as of Moodle 3.6. Only a message object will be accepted):" + +$message = new stdClass(); +$message->component = 'mod_quiz'; //your component name +$message->name = 'submission'; //this is the message name from messages.php +$message->userfrom = $USER; +$message->userto = $touser; +$message->subject = $subject; +$message->fullmessage = $message; +$message->fullmessageformat = FORMAT_PLAIN; +$message->fullmessagehtml = ''; +$message->smallmessage = ''; +$message->notification = 1; //this is only set to 0 for personal messages between users +message_send($message); +``` + +### How to set-up the message popup + +Here is example code showing you how to set-up the JavaScript popup link. + +```php +require_once('message/lib.php'); +$userid = 2; +$userto = $DB->get_record('user', array('id' => $userid)); + +message_messenger_requirejs(); +$url = new moodle_url('message/index.php', array('id' => $userto->id)); +$attributes = message_messenger_sendmessage_link_params($userto); +echo html_writer::link($url, 'Send a message', $attributes); +``` + +## Changes in Moodle 3.5 + + + +In Moodle 3.5, there were some moderately big changes. The only docs I have been able to find about them are in [upgrade.txt](https://github.com/moodle/moodle/blob/33a388eff737c049786ee42d7430db549568471c/message/upgrade.txt#L56) file. However, that is the details, here is an overview: + +The main `message_send()` API to send a message has not changed, so if your code is just sending messages, you don't need to do anything. + +Similarly, message_output plugins don't need to change, so no worries there. + +If you are doing things with messages, then you need to understand how the internals have changed. + +The database tables have changed. Messages from Moodle components to a user (e.g. mod_quiz), telling them that something has happened (e.g. an attempt was submitted) have always been 'Notifications'. In the past, this was just a column in the `mdl_message` table. Now, messages and notifications are stored in completely separate tables. Notifications are in `mdl_notifications`. The strucutre of this table is very similar to the old `mdl_message` table which is now not used at all. Messages are in `mdl_messages`, and related tables, that now exist to support group messaging. Those tables join together like this: + +```sql +SELECT * + +FROM mdl_messages m +JOIN mdl_message_conversations con ON con.id = m.conversationid +JOIN mdl_message_conversation_members mem ON mem.conversationid = con.id +LEFT JOIN mdl_message_user_actions act ON act.userid = mem.userid AND act.messageid = m.id + +ORDER BY m.timecreated, m.id, mem.userid, act.id +``` + +## See also + +- [Core APIs](../../../apis.md) diff --git a/general/app/development/custom-push-notifications.md b/general/app/development/custom-push-notifications.md index a555d737aa..5ca5ccec3d 100644 --- a/general/app/development/custom-push-notifications.md +++ b/general/app/development/custom-push-notifications.md @@ -24,9 +24,9 @@ In both cases, please remember that the number of user devices that can receive ## Moodle's Message API -Please read the official Moodle documentation, [Message API](https://docs.moodle.org/dev/Message_API), where it is explained how to create a new message provider within a plugin. +Please read the official Moodle documentation, [Message API](/docs/apis/core/message/), where it is explained how to create a new message provider within a plugin. -Apart from what is explained in the [Message API](https://docs.moodle.org/dev/Message_API) please notice that it is possible to add a "customdata" field (json-encoded) with additional parameters. Those parameters are the ones described in the Airnotifier API section below. +Apart from what is explained in the [Message API](/docs/apis/core/message/) please notice that it is possible to add a "customdata" field (json-encoded) with additional parameters. Those parameters are the ones described in the Airnotifier API section below. ## Airnotifier API diff --git a/general/releases/3.5.md b/general/releases/3.5.md index 8a90b1f315..e02616ae58 100644 --- a/general/releases/3.5.md +++ b/general/releases/3.5.md @@ -119,7 +119,7 @@ Note: Legacy browsers with known compatibility issues with Moodle 3.5: - [MDL-36941](https://tracker.moodle.org/browse/MDL-36941) - Create new tables for messaging - [MDL-61255](https://tracker.moodle.org/browse/MDL-61255) - Ad-hoc task to upgrade messages to merged table -See also [Message API#Changes in Moodle 3.5](https://docs.moodle.org/dev/Message_API#Changes_in_Moodle_3.5) +See also [Message API#Changes in Moodle 3.5](/docs/apis/core/message/#changes-in-moodle-3.5) ## Other Highlights @@ -176,7 +176,7 @@ Read lib/upgrade.txt to check for the deprecations and core API changes, make su - [lib/upgrade.txt](https://github.com/moodle/moodle/blob/v3.5.0/lib/upgrade.txt) changes to various core APIs, deprecations, functions removal - [calendar/upgrade.txt](https://github.com/moodle/moodle/blob/v3.5.0/calendar/upgrade.txt) changes to Calendar API - [search/upgrade.txt](https://github.com/moodle/moodle/blob/v3.5.0/search/upgrade.txt) changes to Global search API -- [message/upgrade.txt](https://github.com/moodle/moodle/blob/v3.5.0/message/upgrade.txt) changes to Messages API - See also [Message API#Changes in Moodle 3.5](https://docs.moodle.org/dev/Message_API#Changes_in_Moodle_3.5) +- [message/upgrade.txt](https://github.com/moodle/moodle/blob/v3.5.0/message/upgrade.txt) changes to Messages API - See also [Message API#Changes in Moodle 3.5](/docs/apis/core/message/#changes-in-moodle-3.5) - [course/upgrade.txt](https://github.com/moodle/moodle/blob/v3.5.0/course/upgrade.txt) changes to Course API **2. Check for changes in the API of your plugin type** From 8bf954ac43066a1b492893686e7b0330dcf67633 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CFilippo=E2=80=9D?= <“crnfpp@unife.it”> Date: Mon, 18 Sep 2023 12:58:22 +0200 Subject: [PATCH 31/37] [docs] inline edit --- data/migratedPages.yml | 7 +- docs/apis.md | 2 +- .../_inplace/inplace_editable_example.png | Bin 0 -> 22857 bytes .../subsystems/{output.md => output/index.md} | 0 docs/apis/subsystems/output/inplace.md | 123 ++++++++++++++++++ general/releases/3.1.md | 2 +- versioned_docs/version-4.1/apis.md | 2 +- versioned_docs/version-4.2/apis.md | 2 +- 8 files changed, 132 insertions(+), 6 deletions(-) create mode 100644 docs/apis/subsystems/output/_inplace/inplace_editable_example.png rename docs/apis/subsystems/{output.md => output/index.md} (100%) create mode 100644 docs/apis/subsystems/output/inplace.md diff --git a/data/migratedPages.yml b/data/migratedPages.yml index d750bc2fe6..e070625534 100644 --- a/data/migratedPages.yml +++ b/data/migratedPages.yml @@ -249,6 +249,9 @@ Groups_API: Hardening_new_Roles_system: - filePath: "/docs/apis/subsystems/roles.md" slug: "/docs/apis/subsystems/roles" +Inplace_editable: +- filePath: "/docs/apis/subsystems/output/inplace.md" + slug: "/docs/apis/subsystems/output/inplace" Integration_Review: - filePath: "/general/development/process/integration/index.md" slug: "/general/development/process/integration" @@ -1477,10 +1480,10 @@ New_docs_version_process: - filePath: "/general/development/process/release/newuserdocs.md" slug: "/general/development/process/release/newuserdocs" Output_API: -- filePath: "/docs/apis/subsystems/output.md" +- filePath: "/docs/apis/subsystems/output/index.md" slug: "/docs/apis/subsystems/output" Output_functions: -- filePath: "/docs/apis/subsystems/output.md" +- filePath: "/docs/apis/subsystems/output/index.md" slug: "/docs/apis/subsystems/output#output-functions" Overview: - filePath: "/general/community/intro.md" diff --git a/docs/apis.md b/docs/apis.md index 03f11fc61b..3ffaa19d85 100644 --- a/docs/apis.md +++ b/docs/apis.md @@ -40,7 +40,7 @@ The [Page API](https://docs.moodle.org/dev/Page_API) is used to set up the curre ### Output API (output) -The [Output API](./apis/subsystems/output.md) is used to render the HTML for all parts of the page. +The [Output API](./apis/subsystems/output/index.md) is used to render the HTML for all parts of the page. ### String API (string) diff --git a/docs/apis/subsystems/output/_inplace/inplace_editable_example.png b/docs/apis/subsystems/output/_inplace/inplace_editable_example.png new file mode 100644 index 0000000000000000000000000000000000000000..e276ade2fa20575b0d5ddfab460893eff6612e85 GIT binary patch literal 22857 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%0D_NeY@N~_uTB{eUv#JxKJ8%^*;6%mN5@MHQj%0 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 Ny;D?IfXaOc{vWzd<(U8g literal 0 HcmV?d00001 diff --git a/docs/apis/subsystems/output.md b/docs/apis/subsystems/output/index.md similarity index 100% rename from docs/apis/subsystems/output.md rename to docs/apis/subsystems/output/index.md diff --git a/docs/apis/subsystems/output/inplace.md b/docs/apis/subsystems/output/inplace.md new file mode 100644 index 0000000000..fdab889d92 --- /dev/null +++ b/docs/apis/subsystems/output/inplace.md @@ -0,0 +1,123 @@ +--- +title: Inplace editable +tags: + - AJAX + - Javascript +--- +import { Since } from '@site/src/components'; + + + +inplace_editable is a mini-API introduced under [MDL-51802](https://tracker.moodle.org/browse/MDL-51802) for Moodle 3.1. It allows developers easily add in-place editing of a value on any page. The interface is the same as sections and activity name editing. It is implemented as AMD module using JQuery and is re-usable. + +![inplace editable example.png](./_inplace/inplace_editable_example.png) + +## Implementing inplace_editable in a plugin + +The best way is to explain the usage on a simple example. Imagine we have plugin `tool_mytest` that needs to implement in-place editing of a field 'name' from db table `tool_mytest_mytable`. We are going to call this itemtype "mytestname". Each plugin (or core component) may use as many itemtypes as it needs. + +Define a callback in `/admin/tool/mytest/lib.php` that starts with the plugin name and ends with `_inplace_editable`: + +```php +function tool_mytest_inplace_editable($itemtype, $itemid, $newvalue) { + if ($itemtype === 'mytestname') { + global $DB; + $record = $DB->get_record('tool_mytest_mytable', array('id' => $itemid), '*', MUST_EXIST); + // Must call validate_context for either system, or course or course module context. + // This will both check access and set current context. + \external_api::validate_context(context_system::instance()); + // Check permission of the user to update this item. + require_capability('tool/mytest:update', context_system::instance()); + // Clean input and update the record. + $newvalue = clean_param($newvalue, PARAM_NOTAGS); + $DB->update_record('tool_mytest_mytable', array('id' => $itemid, 'name' => $newvalue)); + // Prepare the element for the output: + $record->name = $newvalue; + return new \core\output\inplace_editable('tool_mytest', 'mytestname', $record->id, true, + format_string($record->name), $record->name, 'Edit mytest name', 'New value for ' . format_string($record->name)); + } +} +``` + +In your renderer or wherever you actually display the name, use the same `inplace_editable` template: + +```php +$tmpl = new \core\output\inplace_editable('tool_mytest', 'mytestname', $record->id, + has_capability('tool/mytest:update', context_system::instance()), + format_string($record->name), $record->name, 'Edit mytest name', 'New value for ' . format_string($record->name)); +echo $OUTPUT->render($tmpl); +``` + +This was a very simplified example, in the real life you will probably want to: + +- Create a function (or class extending `core\output\inplace_editable`) to form the instance of templateable object so you don't need to duplicate code; +- Use language strings for `edithint` and `editlabel`, best practice is to use `new lang_string` because these strings will not be needed if `editable=false` +- Use an existing function to update a record (which hopefully also validates input value and triggers events) +- Add unit tests and behat tests. There is a useful behat step **I press key "13" in the field "New value for myname"** + +## Toggles and dropdowns + +You may choose to set the UI for your inplace editable element to be a string value (default), toggle or dropdown. + +Examples of dropdown setup (see also [example by overriding class](https://github.com/moodle/moodle/blob/master/tag/classes/output/tagareacollection.php)): + +```php +$tagcollections = \core_tag_collection::get_collections_menu(true); +$tmpl = new \core\output\inplace_editable('core_tag', 'tagareacollection', $tagarea->id, $editable, + null, $value, $edithint, $editlabel); +$tmpl->set_type_select($tagcollections); +// Note that $displayvalue is not needed (null was passed in the example above) - it will be automatically taken from options. +// $value in the example above must be an existing index from the $tagcollections array, otherwise exception will be thrown. +``` + +Example of toggle setup (see also [example by overriding class](https://github.com/moodle/moodle/blob/master/tag/classes/output/tagareaenabled.php)): + +```php +$tmpl = new \core\output\inplace_editable('core_tag', 'tagflag', $tag->id, $editable, $displayvalue, $value, $hint); +$tmpl->set_type_toggle(array(0, 1)); +// Note that $editlabel is not needed. +// $value must be an existing element of the array passed to set_type_toggle(), otherwise exception will be thrown. +// $displayvalue in toggles is usually an image, for example closed/open eye. It is easier to implement by +// overriding the class. In this case $displayvalue can be generated from $value during exporting. +``` + +## How does it work + +`inplace_editable` consists of + +- Templateable/renderable **class core\output\inplace_editable** +- Template **core/inplace_editable** +- JavaScript module **core/inplace_editable** +- Web service **core_update_inplace_editable** available from AJAX + +All four call each other so it's hard to decide where we start explaining this circle of friends but let's start with web service. + +1. **Web service** receives arguments (`$component`, `$itemtype`, `$itemid`, `$newvalue`) - it searches for the inplace_editable callback in the component. Then web service calls this callback as `{component}_inplace_editable($itemtype, $itemid, $newvalue)`, this must return templateable element which is sent back to the web service caller. Web service requires user to be logged in. **Any other `capability/access` checks must be performed inside the callback.** + +2. **Templateable element** contains such properties as component, `itemtype`, `itemid`, `displayvalue`, `value`, `editlabel` and `edithint`. When used in a **template** It only renders the displayvalue and the edit link (with `title=edithint`). All other properties are rendered as `data-xxx` attributes. Template also ensures that JavaScript module is loaded. + +3. **JavaScript module** registers a listener to when the edit link is clicked and then it replaces the displayvalue with the text input box that allows to edit value. When user presses "Enter" the AJAX request is called to the web service and code from the component is executed. If web service throws an exception it is displayed for user as a popup. + +## Events + +Plugin page can listen to JQuery events that are triggered on successful update or when update failed. Example of the listeners (as inline JS code): + +```php +$PAGE->requires->js_amd_inline(" +require(['jquery'], function(\$) { + $('body').on('updatefailed', '[data-inplaceeditable]', function(e) { + var exception = e.exception; // The exception object returned by the callback. + var newvalue = e.newvalue; // The value that user tried to udpated the element to. + e.preventDefault(); // This will prevent default error dialogue. + // Do your own error processing here. + }); + $('body').on('updated', '[data-inplaceeditable]', function(e) { + var ajaxreturn = e.ajaxreturn; // Everything that web service returned. + var oldvalue = e.oldvalue; // Element value before editing (note, this is raw value and not display value). + // Do your own stuff, for example update all other occurences of this element on the page. + }); +}); +"); +``` + +## See also diff --git a/general/releases/3.1.md b/general/releases/3.1.md index 1edcabe9fb..fae570b1ac 100644 --- a/general/releases/3.1.md +++ b/general/releases/3.1.md @@ -200,7 +200,7 @@ There are no security issues included in this release, please refer to [Moodle 3 ### Smaller new things -- [MDL-51802](https://tracker.moodle.org/browse/MDL-51802) - Reusable element for inplace editing ([documentation](https://docs.moodle.org/dev/Inplace_editable)) +- [MDL-51802](https://tracker.moodle.org/browse/MDL-51802) - Reusable element for inplace editing ([documentation](/docs/apis/subsystems/output/inplace)) - [MDL-30811](https://tracker.moodle.org/browse/MDL-30811) - Introduce notification stack to moodle sessions ([documentation](https://docs.moodle.org/dev/Notifications)) - [MDL-52237](https://tracker.moodle.org/browse/MDL-52237) - Add a callback to inject nodes in the user profile navigation ([documentation](/docs/apis/core/navigation/#user-profile)) - [MDL-51324](https://tracker.moodle.org/browse/MDL-51324) - New course chooser element for moodleforms ([documentation](https://docs.moodle.org/dev/lib/formslib.php_Form_Definition#autocomplete)) diff --git a/versioned_docs/version-4.1/apis.md b/versioned_docs/version-4.1/apis.md index bbcd7fca7b..db3e0c43c3 100644 --- a/versioned_docs/version-4.1/apis.md +++ b/versioned_docs/version-4.1/apis.md @@ -40,7 +40,7 @@ The [Page API](https://docs.moodle.org/dev/Page_API) is used to set up the curre ### Output API (output) -The [Output API](./apis/subsystems/output.md) is used to render the HTML for all parts of the page. +The [Output API](./apis/subsystems/output/index.md) is used to render the HTML for all parts of the page. ### String API (string) diff --git a/versioned_docs/version-4.2/apis.md b/versioned_docs/version-4.2/apis.md index bbcd7fca7b..db3e0c43c3 100644 --- a/versioned_docs/version-4.2/apis.md +++ b/versioned_docs/version-4.2/apis.md @@ -40,7 +40,7 @@ The [Page API](https://docs.moodle.org/dev/Page_API) is used to set up the curre ### Output API (output) -The [Output API](./apis/subsystems/output.md) is used to render the HTML for all parts of the page. +The [Output API](./apis/subsystems/output/index.md) is used to render the HTML for all parts of the page. ### String API (string) From a56b00c32ffbecc1752ac28857d5fad6b93341b8 Mon Sep 17 00:00:00 2001 From: Sara Arjona Date: Mon, 18 Sep 2023 13:10:01 +0200 Subject: [PATCH 32/37] [docs] Migrate admin tools project --- data/migratedPages.yml | 3 ++ data/projects.json | 16 +++++++ docs/apis/plugintypes/index.md | 2 +- general/projects/api/admin-tools.md | 65 +++++++++++++++++++++++++++++ general/releases/2.2.md | 2 +- 5 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 general/projects/api/admin-tools.md diff --git a/data/migratedPages.yml b/data/migratedPages.yml index d750bc2fe6..58007ad4b6 100644 --- a/data/migratedPages.yml +++ b/data/migratedPages.yml @@ -40,6 +40,9 @@ Adding_a_web_service_to_a_plugin: Admin_settings: - filePath: "/docs/apis/subsystems/admin/index.md" slug: "/docs/apis/subsystems/admin/" +Admin_tools: +- filePath: "/general/projects/api/admin-tools.md" + slug: "/general/projects/api/admin-tools" Advanced_grading_API: - filePath: "/docs/apis/core/grading/index.md" slug: "/docs/apis/core/grading/" diff --git a/data/projects.json b/data/projects.json index 5c9047b665..065894f1c5 100644 --- a/data/projects.json +++ b/data/projects.json @@ -37,6 +37,22 @@ } ] }, + "api/admin-tools": { + "title": "Admin tools", + "status": "Complete", + "owners": [ + { + "name": "Petr Škoda", + "githubUsername": "škoďák" + } + ], + "issueLinks": [ + { + "link": "https://tracker.moodle.org/browse/MDL-29029", + "title": "MDL-29029" + } + ] + }, "api/amos": { "title": "Automated Manipulation of Strings (AMOS)", "status": "Complete", diff --git a/docs/apis/plugintypes/index.md b/docs/apis/plugintypes/index.md index 614ec4bb83..485851b3c4 100644 --- a/docs/apis/plugintypes/index.md +++ b/docs/apis/plugintypes/index.md @@ -79,7 +79,7 @@ The underscore character is not supported in activity modules for legacy reasons | [TinyMCE editor plugins](./tiny/legacy.md) | tinymce | /lib/editor/tinymce/plugins | Extra functionality for the TinyMCE text editor. | 2.4+ | | [Enrolment plugins](./enrol/index.md) | enrol | /enrol | Ways to control who is enrolled in courses | 2.0+ | | [Authentication plugins](https://docs.moodle.org/dev/Authentication_plugins) | auth | /auth | Allows connection to external sources of authentication | 2.0+ | -| [Admin tools](https://docs.moodle.org/dev/Admin_tools) | tool | /admin/tool | Provides utility scripts useful for various site administration and maintenance tasks | 2.2+ | +| [Admin tools](/general/projects/api/admin-tools) | tool | /admin/tool | Provides utility scripts useful for various site administration and maintenance tasks | 2.2+ | | [Log stores](./logstore/index.md) | logstore | /admin/tool/log/store | Event logs storage back-ends | 2.7+ | | [Availability conditions](./availability/index.md) | availability | /availability/condition | Conditions to restrict user access to activities and sections. | 2.7+ | | [Calendar types](https://docs.moodle.org/dev/Calendar_types) | calendartype | /calendar/type | Defines how dates are displayed throughout Moodle | 2.6+ | diff --git a/general/projects/api/admin-tools.md b/general/projects/api/admin-tools.md new file mode 100644 index 0000000000..bf29c890bd --- /dev/null +++ b/general/projects/api/admin-tools.md @@ -0,0 +1,65 @@ +--- +title: Admin tools +tags: + - Plugins + - Admin +--- + +import { ProjectSummary } from '@site/src'; +import { Since } from '@site/src/components'; + + + +
+ + + +Admin tools are advanced plugins that are intended especially for site administrators, they are accessible via the admin site administration tree menu. Ideally most of the functionality in `/admin/` directory should be moved to separate plugins in the future. + +## Previous problems + +Before 2.2, tools for administrators were created as admin reports (because we did not have better pluggable place), placed directly into `/admin/` or `/local/ directory`. + +- Confusing `/admin/report/` or `/local/ plugins` +- No way to disable or remove or add custom admin tools +- The official distribution should not include local plugins by definition (`/local/qeupgradehelper`). + +## Benefits + +- Major cleanup in `/admin/` +- No need to abuse admin reports +- Contrib admin tools can be distributed easier +- It is possible to remove or replace core admin tools (no hardcoded links) + +## Upgrades + +How to migrate existing admin reports: + +1. Move all files to new `/admin/tool/yourplugin/` location +1. Update all links to admin tools `/admin/report/` to `/admin/tool/` +1. Rename/add language pack file with at least `pluginname` string +1. Update all language strings (use `tool_yourplugin` instead of `report_yourplugin`. Use [AMOS](https://docs.moodle.org/dev/Languages/AMOS) hints in commit message +1. Update all capability names +1. Create `db/install.php` migration script. Delete old settings and capabilities (see converted plugins for examples) +1. Grep the plugin codebase and look for any remaining `coursereport` occurrences +1. Update CSS selectors + +## FAQs + +- **Is it necessary to migrate existing admin reports?**
+Yes. Old admin reports directory is completely ignored. + +- **Is it difficult to migrate admin reports?**
+No, it usually takes less than an hour to migrate and test one admin tool. + +- **What is the difference between report and admin tool?**
+Report is a view of live or historical data, it may also contain export feature, reports usually do not modify data. Admin tools are intended mostly for administrators, they usually work only in system context. + +- **What is the difference between admin tool and local plugin?**
+Local plugin is everything else, it may be intended for non-admin users. Examples of local plugins: event handlers, web service/function definitions, shared library hacks, new lang strings used in core hacks, etc. + +## See also + +- [General report plugins](https://docs.moodle.org/dev/General_report_plugins) diff --git a/general/releases/2.2.md b/general/releases/2.2.md index 2d2c3b0233..19d6053830 100644 --- a/general/releases/2.2.md +++ b/general/releases/2.2.md @@ -52,7 +52,7 @@ All security issues that were fixed in 2.1.x and 2.0.x were also fixed in 2.2. #### New plugin types - [report - report](https://docs.moodle.org/dev/General_report_plugins) -- [admin/tool - tool](https://docs.moodle.org/dev/Admin_tools) +- [admin/tool - tool](../projects/api/admin-tools.md) - [mod/quiz/accessrule - quizaccess](https://docs.moodle.org/dev/Quiz_access_rules) #### Plugin API changes From 0844c45538295d5a090b5d2543d6418d6ae378f5 Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Mon, 18 Sep 2023 19:38:27 +0800 Subject: [PATCH 33/37] [docs] Amend message API documentation --- docs/apis/core/message/index.md | 110 ++++++++++++++++---------------- 1 file changed, 54 insertions(+), 56 deletions(-) diff --git a/docs/apis/core/message/index.md b/docs/apis/core/message/index.md index 44d674fb92..00a8d914a7 100644 --- a/docs/apis/core/message/index.md +++ b/docs/apis/core/message/index.md @@ -19,7 +19,7 @@ If you are looking for details of how the Messaging system's internal structure If you are looking for instructions on the implementation of a custom message processor (a component that receives messages sent to a user), go to [Messaging custom components](https://docs.moodle.org/dev/Messaging_custom_components). -If you are looking for instructions on sending messages programatically within Moodle then read on... +If you are looking for instructions on sending messages programmatically within Moodle then read on... ## Overview @@ -31,13 +31,13 @@ The Message API code is contained within `lib/messagelib.php` and is automatical ## Functions -`message_send()` is the primary point of contact for the message API. Call it to send a message to a user. You can find a full description of the arguments that must be supplied at (link to phpdocs). There is also an example below. +`message_send()` is the primary point of contact for the message API. Call it to send a message to a user. See the php documentation for a full description of the arguments that must be supplied. There is also an example below. -## Message popup +## Message pop-up -A JavaScript popup can be displayed through a link to invite a user to message another. In order to use this feature, you need to require the JavaScript libraries using `message_messenger_requirejs()` and create a link with the attributes returned by `message_messenger_sendmessage_link_params()`. More in the examples. +A JavaScript pop-up can be displayed through a link to invite a user to message another. In order to use this feature, you need to require the JavaScript libraries using `message_messenger_requirejs()` and create a link with the attributes returned by `message_messenger_sendmessage_link_params()`. More in the examples. ## Examples @@ -45,28 +45,27 @@ A JavaScript popup can be displayed through a link to invite a user to message a The messages produced by a message provider is defined in the `/db/messages.php` file of a component. Below is code from the quiz module's `messages.php` file, shown as an example. -```php +```php title="mod/quiz/db/messages.php" defined('MOODLE_INTERNAL') || die(); -$messageproviders = array ( +$messageproviders = [ // Notify teacher that a student has submitted a quiz attempt - 'submission' => array ( - 'capability' => 'mod/quiz:emailnotifysubmission' - ), + 'submission' => [ + 'capability' => 'mod/quiz:emailnotifysubmission' + ], // Confirm a student's quiz attempt - 'confirmation' => array ( - 'capability' => 'mod/quiz:emailconfirmsubmission' - ) -); + 'confirmation' => [ + 'capability' => 'mod/quiz:emailconfirmsubmission' + ], +]; ``` The quiz can send two kinds of messages, quiz "submission" and "confirmation" notifications. Each message type is only available to users with the appropriate capability. Please note that the capability is checked at the system level context. Users who have this capability will have this message listed in their messaging preferences. You can omit the capability section if your message should be visible for all users. For example forum post notifications are available to all users. ```php -$messageproviders = array ( +$messageproviders = [ // Ordinary single forum posts - 'posts' => array ( - ) -); + 'posts' => [], +]; ``` When displaying your message types in a user's messaging preferences it will use a string from your component's language file called `messageprovider:messagename`. For example here are the relevant strings from the quiz's language file. @@ -81,13 +80,12 @@ Once your `messages.php` is complete you need to increase the version number of ### Setting defaults ```php title="The default processor can be set using an element of the array" - - 'mynotification' => [ - 'defaults' => [ - 'popup' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_LOGGEDIN + MESSAGE_DEFAULT_LOGGEDOFF, - 'email' => MESSAGE_PERMITTED - ], +'mynotification' => [ + 'defaults' => [ + 'pop-up' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_LOGGEDIN + MESSAGE_DEFAULT_LOGGEDOFF, + 'email' => MESSAGE_PERMITTED, ], +], ``` With that setting email will be permitted but disabled for each user by default. It can be turned on by each user through the `preferences/notification` preferences options (`/message/notificationpreferences.php?userid=X`) @@ -113,7 +111,7 @@ Note that if you change the values in message.php and then upgrade the plugin th Here is example code showing you how to actually send a notification message. The example shows the construction of a object with specific properties, which is then passed to the `message_send()` function that uses the information to send a message. -```php +```php title="Sending a message" $message = new \core\message\message(); $message->component = 'mod_yourmodule'; // Your plugin's name $message->name = 'mynotification'; // Your notification name from message.php @@ -127,19 +125,25 @@ $message->smallmessage = 'small message'; $message->notification = 1; // Because this is a notification generated from Moodle, not a user-to-user message $message->contexturl = (new \moodle_url('/course/'))->out(false); // A relevant URL for the notification $message->contexturlname = 'Course list'; // Link title explaining where users get to for the contexturl -$content = array('*' => array('header' => ' test ', 'footer' => ' test ')); // Extra content for specific processor +// Extra content for specific processor +$content = [ + '*' => [ + 'header' => ' test ', + 'footer' => ' test ', + ], +]; $message->set_additional_content('email', $content); // You probably don't need attachments but if you do, here is how to add one $usercontext = context_user::instance($user->id); -$file = new stdClass; +$file = new stdClass(); $file->contextid = $usercontext->id; $file->component = 'user'; -$file->filearea = 'private'; -$file->itemid = 0; -$file->filepath = '/'; -$file->filename = '1.txt'; -$file->source = 'test'; +$file->filearea = 'private'; +$file->itemid = 0; +$file->filepath = '/'; +$file->filename = '1.txt'; +$file->source = 'test'; $fs = get_file_storage(); $file = $fs->create_file_from_string($file, 'file1 content'); @@ -152,30 +156,30 @@ $messageid = message_send($message); ```php title="Before 2.9 message data used to be a stdClass object as shown below (This formation of a message will no longer work as of Moodle 3.6. Only a message object will be accepted):" $message = new stdClass(); -$message->component = 'mod_quiz'; //your component name -$message->name = 'submission'; //this is the message name from messages.php -$message->userfrom = $USER; -$message->userto = $touser; -$message->subject = $subject; -$message->fullmessage = $message; +$message->component = 'mod_quiz'; //your component name +$message->name = 'submission'; //this is the message name from messages.php +$message->userfrom = $USER; +$message->userto = $touser; +$message->subject = $subject; +$message->fullmessage = $message; $message->fullmessageformat = FORMAT_PLAIN; -$message->fullmessagehtml = ''; -$message->smallmessage = ''; -$message->notification = 1; //this is only set to 0 for personal messages between users +$message->fullmessagehtml = ''; +$message->smallmessage = ''; +$message->notification = 1; //this is only set to 0 for personal messages between users message_send($message); ``` -### How to set-up the message popup +### How to set-up the message pop-up -Here is example code showing you how to set-up the JavaScript popup link. +Here is example code showing you how to set-up the JavaScript pop-up link. ```php require_once('message/lib.php'); $userid = 2; -$userto = $DB->get_record('user', array('id' => $userid)); +$userto = $DB->get_record('user', ['id' => $userid]); message_messenger_requirejs(); -$url = new moodle_url('message/index.php', array('id' => $userto->id)); +$url = new moodle_url('message/index.php', ['id' => $userto->id]); $attributes = message_messenger_sendmessage_link_params($userto); echo html_writer::link($url, 'Send a message', $attributes); ``` @@ -192,19 +196,13 @@ Similarly, message_output plugins don't need to change, so no worries there. If you are doing things with messages, then you need to understand how the internals have changed. -The database tables have changed. Messages from Moodle components to a user (e.g. mod_quiz), telling them that something has happened (e.g. an attempt was submitted) have always been 'Notifications'. In the past, this was just a column in the `mdl_message` table. Now, messages and notifications are stored in completely separate tables. Notifications are in `mdl_notifications`. The strucutre of this table is very similar to the old `mdl_message` table which is now not used at all. Messages are in `mdl_messages`, and related tables, that now exist to support group messaging. Those tables join together like this: +The database tables have changed. Messages from Moodle components to a user (e.g. mod_quiz), telling them that something has happened (e.g. an attempt was submitted) have always been 'Notifications'. In the past, this was just a column in the `mdl_message` table. Now, messages and notifications are stored in completely separate tables. Notifications are in `mdl_notifications`. The structure of this table is very similar to the old `mdl_message` table which is now not used at all. Messages are in `mdl_messages`, and related tables, that now exist to support group messaging. Those tables join together like this: ```sql -SELECT * - -FROM mdl_messages m -JOIN mdl_message_conversations con ON con.id = m.conversationid -JOIN mdl_message_conversation_members mem ON mem.conversationid = con.id + SELECT * + FROM mdl_messages m + JOIN mdl_message_conversations con ON con.id = m.conversationid + JOIN mdl_message_conversation_members mem ON mem.conversationid = con.id LEFT JOIN mdl_message_user_actions act ON act.userid = mem.userid AND act.messageid = m.id - -ORDER BY m.timecreated, m.id, mem.userid, act.id + ORDER BY m.timecreated, m.id, mem.userid, act.id ``` - -## See also - -- [Core APIs](../../../apis.md) From 67f4042d753326f90c3b34efbb1a785fc9e93d90 Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Mon, 18 Sep 2023 19:59:46 +0800 Subject: [PATCH 34/37] [docs] Revert changes to versioned docs --- versioned_docs/version-4.1/apis.md | 6 +++--- versioned_docs/version-4.2/apis.md | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/versioned_docs/version-4.1/apis.md b/versioned_docs/version-4.1/apis.md index db3e0c43c3..9045fe5289 100644 --- a/versioned_docs/version-4.1/apis.md +++ b/versioned_docs/version-4.1/apis.md @@ -40,7 +40,7 @@ The [Page API](https://docs.moodle.org/dev/Page_API) is used to set up the curre ### Output API (output) -The [Output API](./apis/subsystems/output/index.md) is used to render the HTML for all parts of the page. +The [Output API](./apis/subsystems/output.md) is used to render the HTML for all parts of the page. ### String API (string) @@ -173,7 +173,7 @@ The [Rating API](https://docs.moodle.org/dev/Rating_API) lets you create AJAX ra ### Report builder API (reportbuilder) -The [Report builder API](https://docs.moodle.org/dev/Report_builder_API) allows you to create reports in your plugin, as well as providing custom reporting data which users can use to build their own reports. +The [Report builder API](../../docs/apis/core/reportbuilder/index.md) allows you to create reports in your plugin, as well as providing custom reporting data which users can use to build their own reports. ### RSS API (rss) @@ -213,7 +213,7 @@ The [https://docs.moodle.org/dev/OpenBadges_User_Documentation Badges] user docu ### Custom fields API (customfield) -The [Custom fields API](https://docs.moodle.org/dev/Custom_fields_API) allows you to configure and add custom fields for different entities +The [Custom fields API](../../docs/apis/core/customfields/index.md) allows you to configure and add custom fields for different entities ## Activity module APIs diff --git a/versioned_docs/version-4.2/apis.md b/versioned_docs/version-4.2/apis.md index db3e0c43c3..9045fe5289 100644 --- a/versioned_docs/version-4.2/apis.md +++ b/versioned_docs/version-4.2/apis.md @@ -40,7 +40,7 @@ The [Page API](https://docs.moodle.org/dev/Page_API) is used to set up the curre ### Output API (output) -The [Output API](./apis/subsystems/output/index.md) is used to render the HTML for all parts of the page. +The [Output API](./apis/subsystems/output.md) is used to render the HTML for all parts of the page. ### String API (string) @@ -173,7 +173,7 @@ The [Rating API](https://docs.moodle.org/dev/Rating_API) lets you create AJAX ra ### Report builder API (reportbuilder) -The [Report builder API](https://docs.moodle.org/dev/Report_builder_API) allows you to create reports in your plugin, as well as providing custom reporting data which users can use to build their own reports. +The [Report builder API](../../docs/apis/core/reportbuilder/index.md) allows you to create reports in your plugin, as well as providing custom reporting data which users can use to build their own reports. ### RSS API (rss) @@ -213,7 +213,7 @@ The [https://docs.moodle.org/dev/OpenBadges_User_Documentation Badges] user docu ### Custom fields API (customfield) -The [Custom fields API](https://docs.moodle.org/dev/Custom_fields_API) allows you to configure and add custom fields for different entities +The [Custom fields API](../../docs/apis/core/customfields/index.md) allows you to configure and add custom fields for different entities ## Activity module APIs From 3b3a58ba1ef464dbfee9d474fe90d58634f2d1a3 Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Mon, 18 Sep 2023 20:09:01 +0800 Subject: [PATCH 35/37] [docs] Fix broken links from rename --- .markdownlint-cli2.cjs | 3 +++ docs/apis/subsystems/output/index.md | 6 +++--- docs/apis/subsystems/task/index.md | 2 +- docs/guides/templates/index.md | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.markdownlint-cli2.cjs b/.markdownlint-cli2.cjs index 2b370c76e3..d8555e8917 100644 --- a/.markdownlint-cli2.cjs +++ b/.markdownlint-cli2.cjs @@ -245,6 +245,9 @@ config.renamedLinks = { renames: [{ oldFile: '/docs/apis/plugintypes/tinymce/index.md', newFile: '/docs/apis/plugintypes/tiny/legacy.md', + }, { + oldFile: '/docs/apis/subsystems/output.md', + newFile: '/docs/apis/subsystems/output/index.md', }, { oldFile: '/docs/apis/subsystems/tool/index.md', newFile: '/docs/apis/subsystems/admin/index.md', diff --git a/docs/apis/subsystems/output/index.md b/docs/apis/subsystems/output/index.md index 16b8191b1c..a1c478dc55 100644 --- a/docs/apis/subsystems/output/index.md +++ b/docs/apis/subsystems/output/index.md @@ -180,7 +180,7 @@ The template used in this plugin is located in the plugin's templates folder. Th ``` -This is the mustache template for this demo. It uses some bootstrap classes directly to position and style the content on the page. `{{sometext}}` is replaced with the variable from the context when this template is rendered. For more information on templates see [Templates](../../guides/templates/index.md). +This is the mustache template for this demo. It uses some bootstrap classes directly to position and style the content on the page. `{{sometext}}` is replaced with the variable from the context when this template is rendered. For more information on templates see [Templates](../../../guides/templates/index.md). ## Output Functions @@ -279,7 +279,7 @@ In earlier versions of Moodle, the third argument was integer `$courseid`. It is Those methods are designed to replace the old ```html_writer::tag(...)``` methods. Even if many of them are just wrappers around the old methods, they are more semantic and could be overridden by component renderers. ::: -While to render complex elements, you should use [templates](../../guides/templates/index.md), some simple elements can be rendered using the following functions: +While to render complex elements, you should use [templates](../../../guides/templates/index.md), some simple elements can be rendered using the following functions: #### container() @@ -338,4 +338,4 @@ In the standard Boost theme this method will output a span using the [Bootstrap - [HTML Guidelines](https://docs.moodle.org/dev/HTML_Guidelines) - [Output renderers](https://docs.moodle.org/dev/Output_renderers) - [Overriding a renderer](https://docs.moodle.org/dev/Overriding_a_renderer) -- [Templates](../../guides/templates/index.md) +- [Templates](../../../guides/templates/index.md) diff --git a/docs/apis/subsystems/task/index.md b/docs/apis/subsystems/task/index.md index 9bb1325e2f..83ccb5515c 100644 --- a/docs/apis/subsystems/task/index.md +++ b/docs/apis/subsystems/task/index.md @@ -95,7 +95,7 @@ The older syntax of cron.php or modname_cron() is still supported, and will be r ### Generating output -Since Moodle 3.5 it is safe to use the [Output API](../output.md) in cron tasks. Prior to this there may be cases where the Output API has not been initialised. +Since Moodle 3.5 it is safe to use the [Output API](../output/index.md) in cron tasks. Prior to this there may be cases where the Output API has not been initialised. In order to improve debugging information, it is good practice to call `mtrace` to log what's going on within a task execution: diff --git a/docs/guides/templates/index.md b/docs/guides/templates/index.md index 077045a258..85ddd48937 100644 --- a/docs/guides/templates/index.md +++ b/docs/guides/templates/index.md @@ -10,7 +10,7 @@ description: A guide to the features and use of Mustache templating in Moodle. Moodle makes use of the [Mustache](https://mustache.github.io) template system to render most of its HTML output, and in some other cases too. -Templates are defined as plain text, which typically includes HTML, and a range of Mustache tags and placeholders. THe Mustache placeholders are replaced with actual values during the render of the page. Mustache templates can be rendered both server-side in PHP, and client-side using JavaScript. Themes can overrides the templates defined in other components if required. +Templates are defined as plain text, which typically includes HTML, and a range of Mustache tags and placeholders. The Mustache placeholders are replaced with actual values during the render of the page. Mustache templates can be rendered both server-side in PHP, and client-side using JavaScript. Themes can overrides the templates defined in other components if required.
A simple example From 4e416739df51e604b2211a73136b63b59d02c72b Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Mon, 18 Sep 2023 20:29:16 +0800 Subject: [PATCH 36/37] [docs] Coding style updates --- docs/apis/subsystems/output/inplace.md | 140 ++++++++++++++++++------- 1 file changed, 100 insertions(+), 40 deletions(-) diff --git a/docs/apis/subsystems/output/inplace.md b/docs/apis/subsystems/output/inplace.md index fdab889d92..ad6224ef06 100644 --- a/docs/apis/subsystems/output/inplace.md +++ b/docs/apis/subsystems/output/inplace.md @@ -3,38 +3,51 @@ title: Inplace editable tags: - AJAX - Javascript +documentationDraft: true --- -import { Since } from '@site/src/components'; - - -inplace_editable is a mini-API introduced under [MDL-51802](https://tracker.moodle.org/browse/MDL-51802) for Moodle 3.1. It allows developers easily add in-place editing of a value on any page. The interface is the same as sections and activity name editing. It is implemented as AMD module using JQuery and is re-usable. +The `inplace_editable` element is a mini-API which allows developers to easily support editing of a value on any page. The interface is used in places such as the course section and activity name editing. ![inplace editable example.png](./_inplace/inplace_editable_example.png) ## Implementing inplace_editable in a plugin -The best way is to explain the usage on a simple example. Imagine we have plugin `tool_mytest` that needs to implement in-place editing of a field 'name' from db table `tool_mytest_mytable`. We are going to call this itemtype "mytestname". Each plugin (or core component) may use as many itemtypes as it needs. +The best way is to explain the usage on a simple example. Imagine we have plugin `tool_mytest` that needs to implement in-place editing of a field 'name' from db table `tool_mytest_mytable`. We are going to call this itemtype `mytestname`. Each plugin (or core component) may use as many item types as it needs. Define a callback in `/admin/tool/mytest/lib.php` that starts with the plugin name and ends with `_inplace_editable`: -```php +```php title="admin/tool/mytest/lib.php" function tool_mytest_inplace_editable($itemtype, $itemid, $newvalue) { + global $DB; + if ($itemtype === 'mytestname') { - global $DB; - $record = $DB->get_record('tool_mytest_mytable', array('id' => $itemid), '*', MUST_EXIST); - // Must call validate_context for either system, or course or course module context. + $record = $DB->get_record('tool_mytest_mytable', ['id' => $itemid], '*', MUST_EXIST); + + // Must call validate_context for either system, or course or course module context. // This will both check access and set current context. \external_api::validate_context(context_system::instance()); - // Check permission of the user to update this item. + + // Check permission of the user to update this item. require_capability('tool/mytest:update', context_system::instance()); + // Clean input and update the record. $newvalue = clean_param($newvalue, PARAM_NOTAGS); - $DB->update_record('tool_mytest_mytable', array('id' => $itemid, 'name' => $newvalue)); + + $DB->update_record('tool_mytest_mytable', ['id' => $itemid, 'name' => $newvalue)); + // Prepare the element for the output: $record->name = $newvalue; - return new \core\output\inplace_editable('tool_mytest', 'mytestname', $record->id, true, - format_string($record->name), $record->name, 'Edit mytest name', 'New value for ' . format_string($record->name)); + + return new \core\output\inplace_editable( + 'tool_mytest', + 'mytestname', + $record->id, + true, + format_string($record->name), + $record->name, + get_string('editmytestnamefield', 'tool_mytest'), + get_string('newvaluestring', 'tool_mytest', format_string($record->name)) + ); } } ``` @@ -42,18 +55,24 @@ function tool_mytest_inplace_editable($itemtype, $itemid, $newvalue) { In your renderer or wherever you actually display the name, use the same `inplace_editable` template: ```php -$tmpl = new \core\output\inplace_editable('tool_mytest', 'mytestname', $record->id, +$tmpl = new \core\output\inplace_editable( + 'tool_mytest', + 'mytestname', + $record->id, has_capability('tool/mytest:update', context_system::instance()), - format_string($record->name), $record->name, 'Edit mytest name', 'New value for ' . format_string($record->name)); + format_string($record->name), + $record->name, + new lang_string('editmytestnamefield', 'tool_mytest'), + new lang_string('newvaluestring', 'tool_mytest', format_string($record->name)) +); echo $OUTPUT->render($tmpl); ``` This was a very simplified example, in the real life you will probably want to: -- Create a function (or class extending `core\output\inplace_editable`) to form the instance of templateable object so you don't need to duplicate code; -- Use language strings for `edithint` and `editlabel`, best practice is to use `new lang_string` because these strings will not be needed if `editable=false` +- Create a function (or class extending `core\output\inplace_editable`) to form the instance of templatable object so you don't need to duplicate code; - Use an existing function to update a record (which hopefully also validates input value and triggers events) -- Add unit tests and behat tests. There is a useful behat step **I press key "13" in the field "New value for myname"** +- Add unit tests and behat tests ## Toggles and dropdowns @@ -63,40 +82,67 @@ Examples of dropdown setup (see also [example by overriding class](https://githu ```php $tagcollections = \core_tag_collection::get_collections_menu(true); -$tmpl = new \core\output\inplace_editable('core_tag', 'tagareacollection', $tagarea->id, $editable, - null, $value, $edithint, $editlabel); +$tmpl = new \core\output\inplace_editable( + 'core_tag', + 'tagareacollection', + $tagarea->id, + $editable, + + // Note that $displayvalue is not needed (null was passed in the example above). + // It will be automatically taken from options. + null, + + // $value must be an existing index from the $tagcollections array, + // otherwise exception will be thrown. + $value, + $edithint, + $editlabel +); $tmpl->set_type_select($tagcollections); -// Note that $displayvalue is not needed (null was passed in the example above) - it will be automatically taken from options. -// $value in the example above must be an existing index from the $tagcollections array, otherwise exception will be thrown. ``` Example of toggle setup (see also [example by overriding class](https://github.com/moodle/moodle/blob/master/tag/classes/output/tagareaenabled.php)): ```php -$tmpl = new \core\output\inplace_editable('core_tag', 'tagflag', $tag->id, $editable, $displayvalue, $value, $hint); -$tmpl->set_type_toggle(array(0, 1)); -// Note that $editlabel is not needed. -// $value must be an existing element of the array passed to set_type_toggle(), otherwise exception will be thrown. -// $displayvalue in toggles is usually an image, for example closed/open eye. It is easier to implement by -// overriding the class. In this case $displayvalue can be generated from $value during exporting. +$tmpl = new \core\output\inplace_editable( + 'core_tag', + + 'tagflag', + + $tag->id, + + $editable, + + // $displayvalue usually toggles an image, for example closed/open eye. + // It is easier to implement by overriding the class. + // In this case $displayvalue can be generated from $value during exporting. + $displayvalue, + + // $value must be an existing element of the array + // passed to set_type_toggle(), otherwise exception will be thrown. + $value, + + $hint, +); +$tmpl->set_type_toggle([0, 1]); ``` ## How does it work `inplace_editable` consists of -- Templateable/renderable **class core\output\inplace_editable** +- Templatable/renderable **class core\output\inplace_editable** - Template **core/inplace_editable** - JavaScript module **core/inplace_editable** - Web service **core_update_inplace_editable** available from AJAX All four call each other so it's hard to decide where we start explaining this circle of friends but let's start with web service. -1. **Web service** receives arguments (`$component`, `$itemtype`, `$itemid`, `$newvalue`) - it searches for the inplace_editable callback in the component. Then web service calls this callback as `{component}_inplace_editable($itemtype, $itemid, $newvalue)`, this must return templateable element which is sent back to the web service caller. Web service requires user to be logged in. **Any other `capability/access` checks must be performed inside the callback.** +1. **Web service** receives arguments (`$component`, `$itemtype`, `$itemid`, `$newvalue`) - it searches for the inplace_editable callback in the component. Then web service calls this callback as `{component}_inplace_editable($itemtype, $itemid, $newvalue)`, this must return templatable element which is sent back to the web service caller. Web service requires user to be logged in. **Any other `capability/access` checks must be performed inside the callback.** -2. **Templateable element** contains such properties as component, `itemtype`, `itemid`, `displayvalue`, `value`, `editlabel` and `edithint`. When used in a **template** It only renders the displayvalue and the edit link (with `title=edithint`). All other properties are rendered as `data-xxx` attributes. Template also ensures that JavaScript module is loaded. +2. **Templatable element** contains such properties as component, `itemtype`, `itemid`, `displayvalue`, `value`, `editlabel` and `edithint`. When used in a **template** It only renders the display value and the edit link (with `title=edithint`). All other properties are rendered as `data-xxx` attributes. Template also ensures that JavaScript module is loaded. -3. **JavaScript module** registers a listener to when the edit link is clicked and then it replaces the displayvalue with the text input box that allows to edit value. When user presses "Enter" the AJAX request is called to the web service and code from the component is executed. If web service throws an exception it is displayed for user as a popup. +3. **JavaScript module** registers a listener to when the edit link is clicked and then it replaces the display value with the text input box that allows to edit value. When user presses "Enter" the AJAX request is called to the web service and code from the component is executed. If web service throws an exception it is displayed for user as a popup. ## Events @@ -105,19 +151,33 @@ Plugin page can listen to JQuery events that are triggered on successful update ```php $PAGE->requires->js_amd_inline(" require(['jquery'], function(\$) { - $('body').on('updatefailed', '[data-inplaceeditable]', function(e) { - var exception = e.exception; // The exception object returned by the callback. - var newvalue = e.newvalue; // The value that user tried to udpated the element to. - e.preventDefault(); // This will prevent default error dialogue. + $('body').on('updatefailed', '[data-inplaceeditable]', (e) => { + // The exception object returned by the callback. + const exception = e.exception; + + // The value that user tried to udpated the element to. + const newvalue = e.newvalue; + + // This will prevent default error dialogue. + e.preventDefault(); + // Do your own error processing here. }); - $('body').on('updated', '[data-inplaceeditable]', function(e) { - var ajaxreturn = e.ajaxreturn; // Everything that web service returned. - var oldvalue = e.oldvalue; // Element value before editing (note, this is raw value and not display value). + $('body').on('updated', '[data-inplaceeditable]', (e) => { + // Everything that web service returned. + const ajaxreturn = e.ajaxreturn; + + // Element value before editing (note, this is raw value and not display value). + const oldvalue = e.oldvalue; + // Do your own stuff, for example update all other occurences of this element on the page. }); }); "); ``` -## See also +:::note + +The above examples are not recommended and just give an example of how these APIs work. + +::: From c44945f69413845b81e8f61461219c2da1eca550 Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Mon, 18 Sep 2023 20:43:58 +0800 Subject: [PATCH 37/37] [docs] Coding fixes --- docs/apis/core/customfields/index.md | 2 +- docs/apis/plugintypes/customfield/index.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/apis/core/customfields/index.md b/docs/apis/core/customfields/index.md index cddf2bfeff..79c3e15769 100644 --- a/docs/apis/core/customfields/index.md +++ b/docs/apis/core/customfields/index.md @@ -54,7 +54,7 @@ handler::setup_edit_page() // Sets page context/url/breadcrumb for the customfie ### Add custom fields to the instance edit form -Custom fields are added to the **instances**. For example, course custom fields are added to the courses, so `courseid` is the `instanceid`. In the example of [mod_surveybuilder](https://github.com/marinaglancy/moodle-mod_surveybuilder) we use `$USER->id` as the `instanceid` (which means that in this example one user can fill the survey in one module only once). In each case of using custom fields there should be a clear concept of an **instance**. `Instanceid` is required to save the data but it may be empty when we render the instance edit form (for example, the course is not yet created). +Custom fields are added to the **instances**. For example, course custom fields are added to the courses, so `courseid` is the `instanceid`. In the example of [`mod_surveybuilder`](https://github.com/marinaglancy/moodle-mod_surveybuilder) we use `$USER->id` as the `instanceid` (which means that in this example one user can fill the survey in one module only once). In each case of using custom fields there should be a clear concept of an **instance**. `Instanceid` is required to save the data but it may be empty when we render the instance edit form (for example, the course is not yet created). Developer must add custom field callbacks to the instance edit form. If the instance is "made up" (like in `mod_surveybuilder`), a new form has to be created with `id` field in it that will refer to the `instanceid`. diff --git a/docs/apis/plugintypes/customfield/index.md b/docs/apis/plugintypes/customfield/index.md index 920373ead2..8db51954b5 100644 --- a/docs/apis/plugintypes/customfield/index.md +++ b/docs/apis/plugintypes/customfield/index.md @@ -6,7 +6,7 @@ tags: - Custom field --- -Custom fields allow you to create field types to be used for custom fields. Instances of these field types can be added to the respective areas that implement [Custom fields API](/docs/apis/core/customfields). Currently in Moodle core only courses implement this API, however custom fields are also used in addon plugins for other areas. For example, if you want to display radio buttons on the course edit page, then you can add an instance of a radio custom field plugin to the Course custom fields configuration. +Custom fields allow you to create field types to be used for custom fields. Instances of these field types can be added to the respective areas that implement [Custom fields API](../../core/customfields/index.md). Currently in Moodle core only courses implement this API, however custom fields are also used in addon plugins for other areas. For example, if you want to display radio buttons on the course edit page, then you can add an instance of a radio custom field plugin to the Course custom fields configuration. import { Lang, @@ -125,5 +125,5 @@ The `instance_form_definition()` function adds any required field elements that ## See Also -- [Custom files API](/docs/apis/core/customfields) +- [Custom files API](../../core/customfields/index.md) - [User Profile Fields](https://docs.moodle.org/dev/User_profile_fields)