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 1/4] [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 67f4042d753326f90c3b34efbb1a785fc9e93d90 Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Mon, 18 Sep 2023 19:59:46 +0800 Subject: [PATCH 2/4] [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 3/4] [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 4/4] [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. + +:::