Skip to content

Commit 68198bf

Browse files
authored
Merge pull request #1404 from ferranrecio/MDL-85509-main
[MDL-85509] activity overview webservices
2 parents 858fbfa + 6f23c95 commit 68198bf

File tree

2 files changed

+248
-4
lines changed

2 files changed

+248
-4
lines changed

docs/apis/plugintypes/mod/courseoverview.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class overview extends activityoverviewbase {
3737

3838
### The `overviewitem` class
3939

40-
The `core_courseformat\local\overview\overviewitem` class represents a specific activity overview item. Currently, items are used only as cells in the course overview table, but they may be used in other places like the course page or the activity page in the future.
40+
The `core_courseformat\local\overview\overviewitem` class represents a specific activity overview item. Currently, items are used only as cells in the course overview table, but they may be used in other places like the course page or the activity page in the future. Also, they could be exported to the mobile APP or other external systems.
4141

4242
The class has the following mandatory properties:
4343

@@ -49,6 +49,8 @@ Also, the class has the following optional properties:
4949

5050
- **`textalign`** (`core\output\local\properties\text_align`): Indicates the preferred text alignment for the parent container. Notice the type hint is not string but `text_align` enum, this limit only to valid text alignment values.
5151
- **`alertcount`** (`integer`) and **`alertlabel`** (`string`): Some items may have an alert count to inform the user of pending actions. This information is not displayed in the table (must be included also in the content) but is added as a data attribute and may be used in other places in the future, such as filtering or mobile app notifications.
52+
- **`extradata`** (`stdClass`): Use this property to include any additional data your plugin needs to provide. While it is not used in the overview page, it will be included in the web service data exported for the mobile app, or for future uses.
53+
- **`key`** (`string`): A unique identifier for the overview item within the displayed context. Typically, the key is assigned automatically based on the context, so you do not need to specify it when instantiating an overview item. For example, the overview table will automatically set the key when retrieving activity overview items.
5254

5355
### Extra Overview Items
5456

@@ -295,3 +297,15 @@ $courseid = required_param('id', PARAM_INT);
295297
```
296298

297299
Once done, the plugin can deprecate any method, output or renderer related to the previous index page.
300+
301+
## Webservice and Moodle APP support
302+
303+
<Since version="5.1" issueNumber="MDL-85509" />
304+
305+
The Moodle Mobile app does not currently support the course overview table, but this feature is planned for future releases. All data from the overview table is already available via the `core_courseformat_get_overview_information` web service.
306+
307+
This web service returns overview items for all activities in a course, including any extra overview items provided by plugins. To ensure your plugin is compatible with the Moodle Mobile app, follow these guidelines:
308+
309+
- If your overview item content is a plain string, use only basic HTML tags such as `<a>`, `<span>`, `<strong>`, and `<em>`. Avoid using special CSS classes, including Bootstrap or other frameworks, as these are not supported by the app.
310+
- If your overview item content is a renderable object, ensure that it also implements the `exportable` interface. This interface provides a stable data structure for use in web services and is required for mobile compatibility.
311+
- If your plugin requires custom visual elements that need more information than the standard output class provides, use the `extradata` property to supply any additional data for the APP. This property is included in the web service response and will be accessible by your custom templates in the Moodle app when the course overview table is implemented.

docs/apis/subsystems/output/index.md

Lines changed: 233 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -387,10 +387,10 @@ Some interesting parameters for this function are:
387387
- `classes`: The classes of the paragraph. Note that this parameter is a comma-separated list of classes, not an array.
388388
- `id`: An optional id of the paragraph.
389389

390-
#### sr_text()
390+
#### visually_hidden_text()
391391

392392
```php
393-
function sr_text(string $contents): string
393+
function visually_hidden_text(string $contents): string
394394
```
395395

396396
This function should be used to:
@@ -404,7 +404,7 @@ Some interesting parameters for this function are:
404404
In the standard Boost theme this method will output a span using the [Bootstrap screen reader class](https://getbootstrap.com/docs/4.0/getting-started/accessibility/#visually-hidden-content):
405405

406406
```html
407-
<span class="sr-only">Contents</span>
407+
<span class="visually-hidden">Contents</span>
408408
```
409409

410410
### Other
@@ -505,6 +505,236 @@ $task->initialise_stored_progress(); // Creates a stored progress record, so the
505505

506506
With the stored progress bars, you can update the progress either via iterations, by passing in the total amount expected and then the current iteration, using `->update()`(see: previous example), this will calculate the percentage complete for you. Or you can use `->update_full()` to manually set the percentage complete.
507507

508+
## Reusing Output Classes in Web Services
509+
510+
<Since version="5.1" issueNumber="MDL-85509" />
511+
512+
The `renderable` interface and its `export_for_template` method are intended for preparing data for template rendering. However, the structure and content of this exported data are closely tied to the template's requirements, which may evolve over time. As a result, this data is not stable enough for use in web services, where a consistent and predictable data structure is required for API consumers.
513+
514+
However, many output classes contain useful logic that can be reused in web services. To enable this, you should implement the `externable` interface in your output class. The `externable` interface is specifically designed to provide a stable data structure for web services, ensuring consistency and reliability across API versions.
515+
516+
When implementing the `externable` interface, you should also create an accompanying `exporter` class for your output. The `externable` interface defines methods that allow consumers to reliably export data:
517+
518+
- `get_exporter`: Returns an instance of the exporter class used to export the data.
519+
- `get_read_structure`: Wraps the exporter's static `get_read_structure` method, allowing web services to parse the returned data structure.
520+
- `read_properties_definition`: Wraps the exporter's static `read_properties_definition` method, enabling the combination of properties from multiple exporters.
521+
522+
By following this approach, you can reuse output class logic in web services while maintaining a stable and well-defined API.
523+
524+
<details>
525+
<summary>Here is an example of how to implement the `externable` interface in your output class.</summary>
526+
527+
```php
528+
529+
namespace mod_MYPLUGIN\output;
530+
531+
use cm_info;
532+
use core\output\externable;
533+
use core\output\named_templatable;
534+
use core\output\renderable;
535+
use core\output\renderer_base;
536+
use core_courseformat\base as course_format;
537+
use mod_MYPLUGIN\external\myname_exporter;
538+
use stdClass;
539+
540+
class myname implements externable, named_templatable, renderable {
541+
public function __construct(
542+
/** @var cm_info The course module. */
543+
public cm_info $cm,
544+
) {
545+
}
546+
547+
#[\Override]
548+
public function export_for_template(renderer_base $output): stdClass {
549+
// This method is used to prepare data for rendering in a template.
550+
// It is related to the `templatable` interface and could return an object or array.
551+
$cm = $this->cm;
552+
$result = (object) [
553+
'activityname' => \core_external\util::format_string($cm->name, $cm->context, true),
554+
'activityurl' => $cm->url,
555+
'hidden' => empty($cm->visible),
556+
'extraclasses' => 'mystyle fw-bold',
557+
];
558+
if ($cm->is_stealth()) {
559+
$result->extrawarning = get_string('stealth', 'mod_MYPLUGIN');
560+
}
561+
return $result;
562+
}
563+
564+
#[\Override]
565+
public function get_template_name(renderer_base $renderer): string {
566+
// This method is used to specify the template name for rendering.
567+
// It is not used for webservice clients, but it is required by the named_templatable interface.
568+
return 'core_courseformat/local/overview/activityname';
569+
}
570+
571+
#[\Override]
572+
public function get_exporter(?\core\context $context = null): myname_exporter {
573+
$context = $context ?? \core\context\system::instance();
574+
return new myname_exporter($this, ['context' => $context]);
575+
}
576+
577+
#[\Override]
578+
public static function get_read_structure(
579+
int $required = VALUE_REQUIRED,
580+
mixed $default = null
581+
): \core_external\external_single_structure {
582+
return myname_exporter::get_read_structure($required, $default);
583+
}
584+
585+
#[\Override]
586+
public static function read_properties_definition(): array {
587+
return myname_exporter::read_properties_definition();
588+
}
589+
590+
// The rest of getter and other methods can be implemented here.
591+
}
592+
```
593+
594+
</details>
595+
596+
:::important PHPUnitTests are required
597+
598+
To ensure the stability of exported data, any class implementing `externable` must provide a PHPUnit test that verifies both the structure and values of the exported data.
599+
600+
:::
601+
602+
### Using `exporters` with `exportable` objects.
603+
604+
The `exporter` class is used to define how the data from an `externable` object should be exported for webservice clients. It provides a structured way to define the properties and related data that will be returned by the webservice.
605+
606+
<details>
607+
<summary>Here is an example of how to implement an `exporter` to an `externable` output.</summary>
608+
609+
This is an example of an exporter for the `myname` output class we created above.
610+
611+
```php
612+
namespace mod_MYPLUGIN\external;
613+
614+
use core\external\exporter;
615+
use mod_MYPLUGIN\output\myname;
616+
617+
class myname_exporter extends exporter {
618+
/**
619+
* Constructor with parameter type hints.
620+
*
621+
* @param action_link $data The action link data to export.
622+
* @param array $related Related data for the exporter.
623+
*/
624+
public function __construct(
625+
myname $data,
626+
array $related = [],
627+
) {
628+
parent::__construct($data, $related);
629+
}
630+
631+
#[\Override]
632+
protected static function define_properties(): array {
633+
return [];
634+
}
635+
636+
#[\Override]
637+
protected static function define_related() {
638+
// Most exporter need to define the context as related data to parse texts.
639+
return [
640+
'context' => 'context',
641+
];
642+
}
643+
644+
#[\Override]
645+
protected static function define_other_properties() {
646+
return [
647+
'activityname' => [
648+
'type' => PARAM_TEXT,
649+
'null' => NULL_NOT_ALLOWED,
650+
'description' => 'The name of the activity.',
651+
],
652+
'activityurl' => [
653+
'type' => PARAM_URL,
654+
'null' => NULL_ALLOWED,
655+
'description' => 'The URL of the activity.',
656+
],
657+
'hidden' => [
658+
'type' => PARAM_BOOL,
659+
'null' => NULL_NOT_ALLOWED,
660+
'description' => 'Whether the activity is hidden.',
661+
],
662+
];
663+
}
664+
665+
#[\Override]
666+
protected function get_other_values(\renderer_base $output) {
667+
/** @var \cm_info $cm */
668+
$cm = $this->data->cm;
669+
670+
return [
671+
'activityname' => \core_external\util::format_string($cm->name, $cm->context, true),
672+
'activityurl' => $cm->url,
673+
'hidden' => empty($cm->visible),
674+
];
675+
}
676+
}
677+
```
678+
679+
</details>
680+
681+
Once the exporter is implemented, the webservice can use it to return the data in a consistent.
682+
683+
<details>
684+
<summary>Here is an example of how to implement a webservice using an `externable` output.</summary>
685+
686+
```php
687+
namespace mod_MYPLUGIN\external;
688+
689+
use core_external\external_api;
690+
use core_external\external_function_parameters;
691+
use core_external\external_single_structure;
692+
use core_external\external_value;
693+
use core\output\renderer_helper;
694+
use mod_MYPLUGIN\output\myname;
695+
use stdClass;
696+
697+
class get_my_name extends external_api {
698+
public static function execute_parameters(): external_function_parameters {
699+
return new external_function_parameters([
700+
'cmid' => new external_value(PARAM_INT, 'Course module id', VALUE_REQUIRED),
701+
]);
702+
}
703+
704+
public static function execute(int $cmid): stdClass {
705+
[
706+
'cmid' => $cmid,
707+
] = external_api::validate_parameters(self::execute_parameters(), [
708+
'cmid' => $cmid,
709+
]);
710+
711+
[$course, $cm] = get_course_and_cm_from_cmid($cmid);
712+
713+
$context = \core\context\module::instance($cm->id);
714+
self::validate_context($context);
715+
716+
// Check any capability required to view the activity.
717+
718+
$page = \core\di::get(renderer_helper::class)->get_core_renderer();
719+
$output = new myname($cm);
720+
721+
$exporter = $output->get_exporter($context);
722+
return $exporter->export($renderer);
723+
}
724+
725+
/**
726+
* Webservice returns.
727+
*
728+
* @return external_single_structure
729+
*/
730+
public static function execute_returns(): external_single_structure {
731+
return myname::get_read_structure();
732+
}
733+
}
734+
```
735+
736+
</details>
737+
508738
## See also
509739

510740
- [HTML Guidelines](https://docs.moodle.org/dev/HTML_Guidelines)

0 commit comments

Comments
 (0)