MDL-78207 course: Add new module badges feature

The new activity card design proposed for Moodle 4.3 differentiates badge
information from other HTML content (displayed using the afterlink feature).
This commit adds a new activitybadge class that can be extended by any
module to display any content in a badge near the activity name. These
are the main features:
- The badge content is always plain text (no HTML).
- The badge style can be set (by default is initialized with badge-none,
but it can be set by any module).
- An optional URL to redirect the user when the badge is clicked.
- An optional ID to add the element in case the module wants to add some
JS to the badge events.
- Optionally, any other extra HTML attributes to the badge element (for
example, data attributes).
This commit is contained in:
Sara Arjona 2023-05-25 17:26:39 +02:00
parent b6849bd973
commit 6db715f5c3
8 changed files with 311 additions and 1 deletions

View File

@ -28,6 +28,7 @@ defined('MOODLE_INTERNAL') || die;
use core_course\external\course_summary_exporter;
use core_external\external_api;
use core_external\external_description;
use core_external\external_files;
use core_external\external_format_value;
use core_external\external_function_parameters;
@ -268,6 +269,10 @@ class core_course_external extends external_api {
$module['indent'] = $cm->indent;
$module['onclick'] = $cm->onclick;
$module['afterlink'] = $cm->afterlink;
$activitybadgedata = $cm->get_activitybadge();
if (!empty($activitybadgedata)) {
$module['activitybadge'] = $activitybadgedata;
}
$module['customdata'] = json_encode($cm->customdata);
$module['completion'] = $cm->completion;
$module['downloadcontent'] = $cm->downloadcontent;
@ -461,6 +466,7 @@ class core_course_external extends external_api {
'onclick' => new external_value(PARAM_RAW, 'Onclick action.', VALUE_OPTIONAL),
'afterlink' => new external_value(PARAM_RAW, 'After link info to be displayed.',
VALUE_OPTIONAL),
'activitybadge' => self::get_activitybadge_structure(),
'customdata' => new external_value(PARAM_RAW, 'Custom data (JSON encoded).', VALUE_OPTIONAL),
'noviewlink' => new external_value(PARAM_BOOL, 'Whether the module has no view page',
VALUE_OPTIONAL),
@ -533,6 +539,60 @@ class core_course_external extends external_api {
);
}
/**
* Returns description of activitybadge data.
*
* @return external_description
*/
protected static function get_activitybadge_structure(): external_description {
return new external_single_structure(
[
'badgecontent' => new external_value(
PARAM_TEXT,
'The content to be displayed in the activity badge',
VALUE_OPTIONAL
),
'badgestyle' => new external_value(
PARAM_TEXT,
'The style for the activity badge',
VALUE_OPTIONAL
),
'badgeurl' => new external_value(
PARAM_URL,
'An optional URL to redirect the user when the activity badge is clicked',
VALUE_OPTIONAL
),
'badgeelementid' => new external_value(
PARAM_ALPHANUMEXT,
'An optional id in case the module wants to add some code for the activity badge',
VALUE_OPTIONAL
),
'badgeextraattributes' => new external_multiple_structure(
new external_single_structure(
[
'name' => new external_value(
PARAM_TEXT,
'The attribute name',
VALUE_OPTIONAL
),
'value' => new external_value(
PARAM_TEXT,
'The attribute value',
VALUE_OPTIONAL
),
],
'Each of the attribute names and values',
VALUE_OPTIONAL
),
'An optional array of extra HTML attributes to add to the badge element',
VALUE_OPTIONAL
),
],
'Activity badge to display near the name',
VALUE_OPTIONAL
);
}
/**
* Returns description of method parameters
*

View File

@ -0,0 +1,129 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace core_courseformat\output;
use cm_info;
use core_courseformat\output\local\courseformat_named_templatable;
use core\output\named_templatable;
use renderer_base;
use stdClass;
/**
* Base class to render an activity badge.
*
* Plugins can extend this class and override some methods to customize the content to be displayed in the activity badge.
*
* @package core_courseformat
* @copyright 2023 Sara Arjona <sara@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class activitybadge implements named_templatable, \renderable {
use courseformat_named_templatable;
/** @var array Badge defined styles. */
public const STYLES = [
'none' => 'badge-none',
'dark' => 'badge-dark',
'danger' => 'badge-danger',
'warning' => 'badge-warning',
'info' => 'badge-info',
];
/** @var cm_info The course module information. */
protected $cminfo = null;
/** @var string The content to be displayed in the activity badge. */
protected $content = null;
/** @var string The style for the activity badge. */
protected $style = self::STYLES['none'];
/** @var \moodle_url An optional URL to redirect the user when the activity badge is clicked. */
protected $url = null;
/** @var string An optional element id in case the module wants to add some code for the activity badge (events, CSS...). */
protected $elementid = null;
/**
* @var array An optional array of extra HTML attributes to add to the badge element (for example, data attributes).
* The format for this array is [['name' => 'attr1', 'value' => 'attrval1'], ['name' => 'attr2', 'value' => 'attrval2']].
*/
protected $extraattributes = [];
/**
* Constructor.
*
* @param cm_info $cminfo The course module information.
*/
public function __construct(cm_info $cminfo) {
$this->cminfo = $cminfo;
}
/**
* Export this data so it can be used as the context for a mustache template.
*
* @param renderer_base $output typically, the renderer that's calling this function
* @return stdClass data context for a mustache template
*/
final public function export_for_template(renderer_base $output): stdClass {
$this->update_content();
if (empty($this->content)) {
return new stdClass();
}
$data = (object)[
'badgecontent' => $this->content,
'badgestyle' => $this->style,
];
if (!empty($this->url)) {
$data->badgeurl = $this->url->out();
}
if (!empty($this->elementid)) {
$data->badgeelementid = $this->elementid;
}
if (!empty($this->extraattributes)) {
$data->badgeextraattributes = $this->extraattributes;
}
return $data;
}
/**
* Creates an instance of activityclass for the given course module, in case it implements it.
*
* @param cm_info $cminfo
* @return self|null An instance of activityclass for the given module or null if the module doesn't implement it.
*/
final public static function create_instance(cm_info $cminfo): ?self {
$classname = '\mod_' . $cminfo->modname . '\output\courseformat\activitybadge';
if (!class_exists($classname)) {
return null;
}
return new $classname($cminfo);
}
/**
* This method will be called before exporting the template.
*
* It should be implemented by any module extending this class and will be in charge of updating any of the class attributes
* with the proper information that will be displayed in the activity badge (like the content or the badge style).
*/
abstract protected function update_content(): void;
}

View File

@ -31,6 +31,7 @@ use core_availability\info_module;
use core_completion\cm_completion_details;
use core_course\output\activity_information;
use core_courseformat\base as course_format;
use core_courseformat\output\activitybadge;
use core_courseformat\output\local\courseformat_named_templatable;
use renderable;
use renderer_base;
@ -186,6 +187,12 @@ class cm implements named_templatable, renderable {
);
$data->altcontent = (empty($altcontent)) ? false : $altcontent;
$data->afterlink = $this->mod->afterlink;
$activitybadgedata = $this->mod->get_activitybadge($output);
if (!empty($activitybadgedata)) {
$data->activitybadge = $activitybadgedata;
}
return !empty($data->altcontent);
}

View File

@ -25,7 +25,23 @@
"displayvalue" : "<a class=\"aalink\" href=\"#\"><span class=\"instancename\">Activity example</span></a>"
},
"hasname": "true",
"afterlink": "<span class=\"badge badge-primary\">30 unread messages</span>",
"afterlink": "<span class=\"resourcelinkdetails\">24.7&nbsp;KB · Uploaded 26/05/23, 16:29</span>",
"activitybadge": {
"badgecontent": "PDF",
"badgestyle": "badge-none",
"badgeurl": "http://example.org/help",
"badgeelementid": "myelementid",
"badgeextraattributes": [
{
"name": "data-el1name",
"value": "el1value"
},
{
"name": "data-el2name",
"value": "el2value"
}
]
},
"hasextras": true,
"extras": ["<span class=\"badge badge-secondary\">[extras]</span>"],
"activityinfo": {
@ -69,6 +85,13 @@
{{> core_courseformat/local/content/cm/cmname }}
{{/ core_courseformat/local/content/cm/cmname }}
{{/cmname}}
{{#activitybadge}}
{{$ core_courseformat/local/content/cm/activitybadge }}
{{> core_courseformat/local/content/cm/activitybadge }}
{{/ core_courseformat/local/content/cm/activitybadge }}
{{/activitybadge}}
{{#activityinfo}}
<div class="activity-info mt-1 mt-md-0">
{{$ core_courseformat/local/content/cm/activity_info}}

View File

@ -0,0 +1,55 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template core_courseformat/local/content/cm/activitybadge
Container to display activity badge information such as:
- Unread messages for forums
- File types for resources
Example context (json):
{
"badgecontent": "PDF",
"badgestyle": "badge-none",
"badgeurl": "http://example.org/help",
"badgeelementid": "myelementid",
"badgeextraattributes": [
{
"name": "data-el1name",
"value": "el1value"
},
{
"name": "data-el2name",
"value": "el2value"
}
]
}
}}
<div
{{#badgeelementid}}id="{{.}}"{{/badgeelementid}}
class="m-2 d-flex align-items-center"
{{#badgeextraattributes}} {{name}}="{{value}}" {{/badgeextraattributes}}
>
<div class="activitybadge badge badge-pill {{badgestyle}}">
{{#badgeurl}}
<a href="{{.}}">{{badgecontent}}</a>
{{/badgeurl}}
{{^badgeurl}}
{{badgecontent}}
{{/badgeurl}}
</div>
</div>

View File

@ -2,6 +2,17 @@ This files describes API changes for course formats
Overview of this plugin type at http://docs.moodle.org/dev/Course_formats
=== 4.3 ===
* New core_courseformat\output\activitybadge class that can be extended by any module to display content near the activity name.
The content of the afterlink feature has been moved to the end of the activity card so modules using it should check this new
feature which might fit better.
Some considerations about the activitybadge feature:
- The badge content is always plain text (no HTML).
- The badge style can be set (by default is initialized with badge-none, but it can be set by any module).
- An optional URL to redirect the user when the badge is clicked.
- An optional ID to add the element in case the module wants to add some JS to the badge events.
- Optionally, any other extra HTML attributes to the badge element (for example, data attributes).
=== 4.2 ===
* New core_courseformat\base::get_context() to get the course context directly from the format instance.
* New core_courseformat\base::delete_module() method. Now format plugins can extend the activity deletion logic

View File

@ -3,6 +3,8 @@ information provided here is intended especially for developers.
=== 4.3 ===
* The `core_course_renderer::course_section_cm_completion` method has been removed, and can no longer be used
* External function core_course_external::get_course_contents() now returns a new field activitybadge with the data to display
the activity badge when the module implements it.
=== 4.2 ===
* course/mod.php now accepts parameter beforemod for adding course modules. It contains the course module id

View File

@ -32,6 +32,7 @@ if (!defined('MAX_MODINFO_CACHE_SIZE')) {
define('MAX_MODINFO_CACHE_SIZE', 10);
}
use core_courseformat\output\activitybadge;
/**
* Information about a course that is cached in the course table 'modinfo' field (and then in
@ -1756,6 +1757,28 @@ class cm_info implements IteratorAggregate {
return $this->afterlink;
}
/**
* Get the activity badge data associated to this course module (if the module supports it).
* Modules can use this method to provide additional data to be displayed in the activity badge.
*
* @param renderer_base $output Output render to use, or null for default (global)
* @return stdClass|null The activitybadge data (badgecontent, badgestyle...) or null if the module doesn't implement it.
*/
public function get_activitybadge(?renderer_base $output = null): ?stdClass {
global $OUTPUT;
$activibybadgeclass = activitybadge::create_instance($this);
if (empty($activibybadgeclass)) {
return null;
}
if (!isset($output)) {
$output = $OUTPUT;
}
return $activibybadgeclass->export_for_template($output);
}
/**
* Note: Will collect view data, if not already obtained.
* @return string Extra HTML code to display after editing icons (e.g. more icons)