Skip to content

Commit

Permalink
Merge pull request #744 from crnfpp/inlinedit2
Browse files Browse the repository at this point in the history
[docs] inline edit
  • Loading branch information
andrewnicols authored Sep 18, 2023
2 parents 84d3bdd + 4e41673 commit 2be9397
Show file tree
Hide file tree
Showing 11 changed files with 202 additions and 13 deletions.
3 changes: 3 additions & 0 deletions .markdownlint-cli2.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
7 changes: 5 additions & 2 deletions data/migratedPages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -1480,10 +1483,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"
Expand Down
2 changes: 1 addition & 1 deletion docs/apis.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ The template used in this plugin is located in the plugin's templates folder. Th
</div>
```

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

Expand Down Expand Up @@ -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()

Expand Down Expand Up @@ -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)
183 changes: 183 additions & 0 deletions docs/apis/subsystems/output/inplace.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
---
title: Inplace editable
tags:
- AJAX
- Javascript
documentationDraft: true
---

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 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 title="admin/tool/mytest/lib.php"
function tool_mytest_inplace_editable($itemtype, $itemid, $newvalue) {
global $DB;

if ($itemtype === 'mytestname') {
$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.
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', ['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,
get_string('editmytestnamefield', 'tool_mytest'),
get_string('newvaluestring', 'tool_mytest', 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,
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 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

## 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,

// 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);
```

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 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

- 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 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. **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 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

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]', (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]', (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.
});
});
");
```

:::note

The above examples are not recommended and just give an example of how these APIs work.

:::
2 changes: 1 addition & 1 deletion docs/apis/subsystems/task/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down
2 changes: 1 addition & 1 deletion docs/guides/templates/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

<details>
<summary>A simple example</summary>
Expand Down
2 changes: 1 addition & 1 deletion general/releases/3.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
4 changes: 2 additions & 2 deletions versioned_docs/version-4.1/apis.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ The [Rating API](https://docs.moodle.org/dev/Rating_API) lets you create AJAX ra
<!-- cspell:ignore reportbuilder -->
### 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)

Expand Down Expand Up @@ -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

Expand Down
4 changes: 2 additions & 2 deletions versioned_docs/version-4.2/apis.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ The [Rating API](https://docs.moodle.org/dev/Rating_API) lets you create AJAX ra
<!-- cspell:ignore reportbuilder -->
### 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)

Expand Down Expand Up @@ -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

Expand Down

0 comments on commit 2be9397

Please sign in to comment.