Merge branch 'MDL-78207-master' of https://github.com/sarjona/moodle

This commit is contained in:
Ilya Tregubov
2023-06-15 09:21:11 +08:00
21 changed files with 729 additions and 47 deletions
@@ -129,4 +129,4 @@ Feature: Add customised file types
And I expand all fieldsets
And I set the field "Show type" to "1"
And I press "Save and return to course"
Then I should see "Froggy file"
Then I should see "FROG" in the "div.activitybadge" "css_element"
+60
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
*
@@ -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 <[email protected]>
* @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;
}
@@ -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);
}
@@ -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,11 +85,12 @@
{{> core_courseformat/local/content/cm/cmname }}
{{/ core_courseformat/local/content/cm/cmname }}
{{/cmname}}
{{#afterlink}}
<div class="afterlink d-flex align-items-center ml-3">
{{{afterlink}}}
</div>
{{/afterlink}}
{{#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">
@@ -125,3 +142,9 @@
{{/ core_courseformat/local/content/cm/availability }}
{{/modavailability}}
</div>
{{#afterlink}}
<div class="afterlink d-flex align-items-center mt-3">
{{{afterlink}}}
</div>
{{/afterlink}}
@@ -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>
@@ -0,0 +1,208 @@
<?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 stdClass;
/**
* Tests for activitybadge class.
*
* @package core_courseformat
* @copyright 2023 Sara Arjona <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @coversDefaultClass \core_courseformat\output\activitybadge
*/
class activitybadge_test extends \advanced_testcase {
/**
* Test the behaviour of create_instance() and export_for_template() attributes.
* @runInSeparateProcess
*
* @covers ::export_for_template
* @covers ::create_instance
*/
public function test_activitybadge_export_for_template() {
$this->resetAfterTest();
$this->setAdminUser();
$data = $this->setup_scenario();
$user = $this->getDataGenerator()->create_user(['trackforums' => 1]);
$this->getDataGenerator()->enrol_user(
$user->id,
$data->course->id,
'student'
);
$this->setUser($user);
$renderer = $data->renderer;
// The activitybadge for a file with all options enabled shouldn't be empty.
$class = activitybadge::create_instance($data->fileshowtype);
$result = $class->export_for_template($renderer);
$this->check_activitybadge($result, 'TXT', 'badge-none');
// The activitybadge for a file with Show type option disabled should be empty.
$class = activitybadge::create_instance($data->filehidetype);
$result = $class->export_for_template($renderer);
$this->check_activitybadge($result);
// The activitybadge for a forum with unread messages shouldn't be empty.
$class = activitybadge::create_instance($data->forumunread);
$result = $class->export_for_template($renderer);
$this->check_activitybadge($result, '1 unread post', 'badge-dark');
// The activitybadge for a forum without unread messages should be empty.
$class = activitybadge::create_instance($data->forumread);
$result = $class->export_for_template($renderer);
$this->check_activitybadge($result);
// The activitybadge for an assignment should be empty.
$class = activitybadge::create_instance($data->assign);
$this->assertNull($class);
// The activitybadge for a label should be empty.
$class = activitybadge::create_instance($data->label);
$this->assertNull($class);
}
/**
* Setup the default scenario, creating some activities:
* - A forum with one unread message from the teacher.
* - Another forum without unread messages.
* - A file with all the appearance options enabled.
* - A file with the "Show type" option disabled.
* - An assignment.
* - A label.
*
* @return stdClass the scenario instances.
*/
private function setup_scenario(): stdClass {
global $PAGE;
$course = $this->getDataGenerator()->create_course(['numsections' => 1]);
// Enrol editing teacher to the course.
$teacher = $this->getDataGenerator()->create_user();
$this->getDataGenerator()->enrol_user(
$teacher->id,
$course->id,
'editingteacher'
);
$this->setUser($teacher);
// Create a forum with tracking forced and add a discussion.
$record = new stdClass();
$record->introformat = FORMAT_HTML;
$record->course = $course->id;
$record->trackingtype = FORUM_TRACKING_FORCED;
$forumread = $this->getDataGenerator()->create_module('forum', $record);
$forumunread = $this->getDataGenerator()->create_module('forum', $record);
$record = new stdClass();
$record->course = $course->id;
$record->userid = $teacher->id;
$record->forum = $forumunread->id;
$discussion = $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
// Create a file with all the options enabled.
$record = (object)[
'course' => $course->id,
'showsize' => 1,
'showtype' => 1,
'showdate' => 1,
];
$fileshowtype = self::getDataGenerator()->create_module('resource', $record);
// Create a file with Show type disabled.
$record = (object)[
'course' => $course->id,
'showsize' => 1,
'showtype' => 0,
'showdate' => 1,
];
$filehidetype = self::getDataGenerator()->create_module('resource', $record);
// Create an assignment and a label.
$assign = $this->getDataGenerator()->create_module('assign', ['course' => $course->id]);
$label = $this->getDataGenerator()->create_module('label', ['course' => $course->id]);
rebuild_course_cache($course->id, true);
$renderer = course_get_format($course->id)->get_renderer($PAGE);
$modinfo = get_fast_modinfo($course->id);
return (object)[
'course' => $course,
'forumunread' => $modinfo->get_cm($forumunread->cmid),
'discussion' => $discussion,
'forumread' => $modinfo->get_cm($forumread->cmid),
'fileshowtype' => $modinfo->get_cm($fileshowtype->cmid),
'filehidetype' => $modinfo->get_cm($filehidetype->cmid),
'assign' => $modinfo->get_cm($assign->cmid),
'label' => $modinfo->get_cm($label->cmid),
'renderer' => $renderer,
];
}
/**
* Method to check if the result of the export_from_template is the expected.
*
* @param stdClass $result The result of the export_from_template() call.
* @param string|null $content The expected activitybadge content.
* @param string|null $style The expected activitybadge style.
* @param string|null $url The expected activitybadge url.
* @param string|null $elementid The expected activitybadge element id.
* @param array|null $extra The expected activitybadge extra attributes.
*/
private function check_activitybadge(
stdClass $result,
?string $content = null,
?string $style = null,
?string $url = null,
?string $elementid = null,
?array $extra = null
): void {
if (is_null($content)) {
$this->assertObjectNotHasAttribute('badgecontent', $result);
} else {
$this->assertEquals($content, $result->badgecontent);
}
if (is_null($style)) {
$this->assertObjectNotHasAttribute('badgestyle', $result);
} else {
$this->assertEquals($style, $result->badgestyle);
}
if (is_null($url)) {
$this->assertObjectNotHasAttribute('badgeurl', $result);
} else {
$this->assertEquals($url, $result->badgeurl);
}
if (is_null($elementid)) {
$this->assertObjectNotHasAttribute('badgeelementid', $result);
} else {
$this->assertEquals($elementid, $result->badgeelementid);
}
if (is_null($extra)) {
$this->assertObjectNotHasAttribute('badgeextraattributes', $result);
} else {
$this->assertEquals($extra, $result->badgeextraattributes);
}
}
}
+11
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
+22 -3
View File
@@ -1110,6 +1110,15 @@ class externallib_test extends externallib_advanced_testcase {
$CFG->forum_allowforcedreadtracking = 1;
list($course, $forumcm, $datacm, $pagecm, $labelcm, $urlcm) = $this->prepare_get_course_contents_test();
// Create a resource with all the appearance options enabled. By default it's a text file and will be added to section 1.
$record = (object) [
'course' => $course->id,
'showsize' => 1,
'showtype' => 1,
'showdate' => 1,
];
$resource = self::getDataGenerator()->create_module('resource', $record);
// We first run the test as admin.
$this->setAdminUser();
$sections = core_course_external::get_course_contents($course->id, array());
@@ -1126,9 +1135,13 @@ class externallib_test extends externallib_advanced_testcase {
$this->assertEquals($formattedtext, $module['description']);
$this->assertEquals($forumcm->instance, $module['instance']);
$this->assertEquals(context_module::instance($forumcm->id)->id, $module['contextid']);
$this->assertStringContainsString('1 unread post', $module['afterlink']);
$this->assertFalse($module['noviewlink']);
$this->assertNotEmpty($module['description']); // Module showdescription is on.
// Afterlink for forums has been removed; it has been moved to the new activity badge content.
$this->assertEmpty($module['afterlink']);
$this->assertEquals('1 unread post', $module['activitybadge']['badgecontent']);
$this->assertEquals('badge-dark', $module['activitybadge']['badgestyle']);
$testexecuted = $testexecuted + 2;
} else if ($module['id'] == $labelcm->id and $module['modname'] == 'label') {
$cm = $modinfo->cms[$labelcm->id];
@@ -1145,6 +1158,12 @@ class externallib_test extends externallib_advanced_testcase {
$this->assertFalse($module['noviewlink']);
$this->assertArrayNotHasKey('description', $module);
$testexecuted = $testexecuted + 1;
} else if ($module['instance'] == $resource->id && $module['modname'] == 'resource') {
// Resources have both, afterlink for the size and the update date and activitybadge for the file type.
$this->assertStringContainsString('32 bytes', $module['afterlink']);
$this->assertEquals('TXT', $module['activitybadge']['badgecontent']);
$this->assertEquals('badge-none', $module['activitybadge']['badgestyle']);
$testexecuted = $testexecuted + 1;
}
}
foreach ($sections[2]['modules'] as $module) {
@@ -1157,10 +1176,10 @@ class externallib_test extends externallib_advanced_testcase {
$CFG->forum_allowforcedreadtracking = 0; // Recover original value.
forum_tp_count_forum_unread_posts($forumcm, $course, true); // Reset static cache for further tests.
$this->assertEquals(5, $testexecuted);
$this->assertEquals(6, $testexecuted);
$this->assertEquals(0, $sections[0]['section']);
$this->assertCount(6, $sections[0]['modules']);
$this->assertCount(7, $sections[0]['modules']);
$this->assertCount(1, $sections[1]['modules']);
$this->assertCount(1, $sections[2]['modules']);
$this->assertCount(1, $sections[3]['modules']); // One module for the section with availability restrictions.
+2
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
@@ -31,8 +31,10 @@ Feature: Add a new custom file type
When I add a "File" to section "1" and I fill the form with:
| Name | Test file |
| Select files | files/tests/fixtures/custom_filetype.mdlr |
| Show size | 1 |
| Show type | 1 |
| Display resource description | 1 |
And I am on "Course 1" course homepage
Then I should see "Test file"
And I should see "Moodle rules" in the "span.resourcelinkdetails" "css_element"
And I should see "MDLR" in the "div.activitybadge" "css_element"
And I should not see "MDLR" in the "span.resourcelinkdetails" "css_element"
+23
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)
+33
View File
@@ -583,6 +583,39 @@ class modinfolib_test extends advanced_testcase {
}
/**
* Tests for function cm_info::get_activitybadge().
*
* @covers \cm_info::get_activitybadge
*/
public function test_cm_info_get_activitybadge(): void {
global $PAGE;
$this->resetAfterTest();
$this->setAdminUser();
$course = $this->getDataGenerator()->create_course();
$forum = $this->getDataGenerator()->create_module('forum', ['course' => $course->id]);
$resource = $this->getDataGenerator()->create_module('resource', ['course' => $course->id]);
$assign = $this->getDataGenerator()->create_module('assign', ['course' => $course->id]);
$label = $this->getDataGenerator()->create_module('label', ['course' => $course->id]);
$renderer = $PAGE->get_renderer('core');
$modinfo = get_fast_modinfo($course->id);
// Forum and resource implements the activitybadge feature.
$cminfo = $modinfo->get_cm($forum->cmid);
$this->assertNotNull($cminfo->get_activitybadge($renderer));
$cminfo = $modinfo->get_cm($resource->cmid);
$this->assertNotNull($cminfo->get_activitybadge($renderer));
// Assign and label don't implement the activitybadge feature (at least for now).
$cminfo = $modinfo->get_cm($assign->cmid);
$this->assertNull($cminfo->get_activitybadge($renderer));
$cminfo = $modinfo->get_cm($label->cmid);
$this->assertNull($cminfo->get_activitybadge($renderer));
}
/**
* Tests the availability property that has been added to course modules
* and sections (just to see that it is correctly saved and accessed).
@@ -0,0 +1,47 @@
<?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 mod_forum\output\courseformat;
/**
* Activity badge forum class, used for rendering unread messages.
*
* @package mod_forum
* @copyright 2023 Sara Arjona <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class activitybadge extends \core_courseformat\output\activitybadge {
/**
* This method will be called before exporting the template.
*/
protected function update_content(): void {
global $CFG;
require_once($CFG->dirroot . '/mod/forum/lib.php');
if (forum_tp_can_track_forums()) {
if ($unread = forum_tp_count_forum_unread_posts($this->cminfo, $this->cminfo->get_course())) {
if ($unread == 1) {
$this->content = get_string('unreadpostsone', 'forum');
} else {
$this->content = get_string('unreadpostsnumber', 'forum', $unread);
}
$this->style = self::STYLES['dark'];
}
}
}
}
-22
View File
@@ -5487,28 +5487,6 @@ function forum_extend_settings_navigation(settings_navigation $settingsnav, navi
}
}
/**
* Adds information about unread messages, that is only required for the course view page (and
* similar), to the course-module object.
* @param cm_info $cm Course-module object
*/
function forum_cm_info_view(cm_info $cm) {
global $CFG;
if (forum_tp_can_track_forums()) {
if ($unread = forum_tp_count_forum_unread_posts($cm, $cm->get_course())) {
$out = '<span class="badge badge-secondary">';
if ($unread == 1) {
$out .= get_string('unreadpostsone', 'forum');
} else {
$out .= get_string('unreadpostsnumber', 'forum', $unread);
}
$out .= '</span>';
$cm->set_after_link($out);
}
}
}
/**
* Return a list of page types
* @param string $pagetype current page type
@@ -0,0 +1,35 @@
<?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 mod_resource\output\courseformat;
/**
* Activity badge resource class, used for displaying the file type.
*
* @package mod_resource
* @copyright 2023 Sara Arjona <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class activitybadge extends \core_courseformat\output\activitybadge {
/**
* This method will be called before exporting the template.
*/
protected function update_content(): void {
$options = (object) ['displayoptions' => $this->cminfo->customdata['displayoptions']];
$this->content = resource_get_optional_filetype($options, $this->cminfo);
}
}
+4 -4
View File
@@ -102,10 +102,10 @@ $string['printintroexplain'] = 'Display resource description below content? Some
$string['privacy:metadata'] = 'The File resource plugin does not store any personal data.';
$string['resource:addinstance'] = 'Add a new resource';
$string['resourcecontent'] = 'Files and subfolders';
$string['resourcedetails_sizetype'] = '{$a->size} {$a->type}';
$string['resourcedetails_sizedate'] = '{$a->size} {$a->date}';
$string['resourcedetails_typedate'] = '{$a->type} {$a->date}';
$string['resourcedetails_sizetypedate'] = '{$a->size} {$a->type} {$a->date}';
$string['resourcedetails_sizetype'] = '{$a->size} · {$a->type}';
$string['resourcedetails_sizedate'] = '{$a->size} · {$a->date}';
$string['resourcedetails_typedate'] = '{$a->type} · {$a->date}';
$string['resourcedetails_sizetypedate'] = '{$a->size} · {$a->type} · {$a->date}';
$string['resource:exportresource'] = 'Export resource';
$string['resource:view'] = 'View resource';
$string['search:activity'] = 'File';
+1 -1
View File
@@ -271,7 +271,7 @@ function resource_cm_info_view(cm_info $cm) {
require_once($CFG->dirroot . '/mod/resource/locallib.php');
$resource = (object) ['displayoptions' => $cm->customdata['displayoptions']];
$details = resource_get_optional_details($resource, $cm);
$details = resource_get_optional_details($resource, $cm, false);
if ($details) {
$cm->set_after_link(' ' . html_writer::tag('span', $details,
array('class' => 'resourcelinkdetails')));
+34 -4
View File
@@ -291,6 +291,7 @@ function resource_get_file_details($resource, $cm) {
if ($mainfile) {
$filedetails['type'] = get_mimetype_description($mainfile);
$filedetails['mimetype'] = $mainfile->get_mimetype();
$filedetails['extension'] = strtoupper(resourcelib_get_extension($mainfile->get_filename()));
// Only show type if it is not unknown.
if ($filedetails['type'] === get_mimetype_description('document/unknown')) {
$filedetails['type'] = '';
@@ -328,15 +329,16 @@ function resource_get_file_details($resource, $cm) {
*
* @param object $resource Resource table row (only property 'displayoptions' is used here)
* @param object $cm Course-module table row
* @param bool $showtype Whether the file type should be displayed or not (regardless the display option is enabled).
* @return string Size and type or empty string if show options are not enabled
*/
function resource_get_optional_details($resource, $cm) {
function resource_get_optional_details($resource, $cm, bool $showtype = true) {
global $DB;
$details = '';
$options = empty($resource->displayoptions) ? [] : (array) unserialize_array($resource->displayoptions);
if (!empty($options['showsize']) || !empty($options['showtype']) || !empty($options['showdate'])) {
if (!empty($options['showsize']) || ($showtype && !empty($options['showtype'])) || !empty($options['showdate'])) {
if (!array_key_exists('filedetails', $options)) {
$filedetails = resource_get_file_details($resource, $cm);
} else {
@@ -354,9 +356,9 @@ function resource_get_optional_details($resource, $cm) {
$infodisplayed += 1;
}
}
if (!empty($options['showtype'])) {
if ($showtype && !empty($options['showtype'])) {
if (!empty($filedetails['type'])) {
$type = $filedetails['type'];
$type = $filedetails['extension'];
$langstring .= 'type';
$infodisplayed += 1;
}
@@ -385,6 +387,34 @@ function resource_get_optional_details($resource, $cm) {
return $details;
}
/**
* Gets optional file type extension for a resource, depending on resource settings.
*
* @param object $resource Resource table row (only property 'displayoptions' is used here)
* @param object $cm Course-module table row
* @return string File extension or null if showtype option is not enabled
*/
function resource_get_optional_filetype($resource, $cm): ?string {
$filetype = null;
$options = empty($resource->displayoptions) ? [] : (array) unserialize_array($resource->displayoptions);
if (empty($options['showtype'])) {
// Show type option is disabled; early return null filetype.
return $filetype;
}
if (!array_key_exists('filedetails', $options)) {
$filedetails = resource_get_file_details($resource, $cm);
} else {
$filedetails = $options['filedetails'];
}
if (!empty($filedetails['type'])) {
$filetype = $filedetails['extension'];
}
return $filetype;
}
/**
* Get resource introduction.
*
@@ -44,19 +44,34 @@ Feature: Teacher can specify different display options for the resource
| Show upload/modified date | <showdate> |
And I press "Save and display"
Then I <seesize> see "6 bytes" in the ".resourcedetails" "css_element"
And I <seetype> see "Text file" in the ".resourcedetails" "css_element"
And I <seetype> see "TXT" in the ".resourcedetails" "css_element"
And I <seedate> see "Uploaded" in the ".resourcedetails" "css_element"
And I am on "Course 1" course homepage
And I <seesize> see "6 bytes" in the ".activity.resource .resourcelinkdetails" "css_element"
And I <seetype> see "Text file" in the ".activity.resource .resourcelinkdetails" "css_element"
And I <seetype> see "TXT" in the ".activity.resource .activitybadge" "css_element"
And I <seedate> see "Uploaded" in the ".activity.resource .resourcelinkdetails" "css_element"
Examples:
| showsize | showtype | showdate | seesize | seetype | seedate |
| 1 | 0 | 0 | should | should not | should not |
| 0 | 1 | 0 | should not | should | should not |
| 0 | 0 | 1 | should not | should not | should |
| 1 | 1 | 0 | should | should | should not |
| 1 | 0 | 1 | should | should not | should |
| 0 | 1 | 1 | should not | should | should |
| 1 | 1 | 1 | should | should | should |
Scenario: Specifying only show type for a file resource
When I am on the "Myfile" "resource activity editing" page
And I set the following fields to these values:
| display | 5 |
| Show size | 0 |
| Show type | 1 |
| Show upload/modified date | 0 |
And I press "Save and display"
Then I should see "TXT" in the ".resourcedetails" "css_element"
Then I should not see "6 bytes" in the ".resourcedetails" "css_element"
And I should see "TXT" in the ".resourcedetails" "css_element"
And I should not see "Uploaded" in the ".resourcedetails" "css_element"
And I am on "Course 1" course homepage
And I should see "TXT" in the ".activity.resource .activitybadge" "css_element"
And ".activity.resource .resourcelinkdetails" "css_element" should not exist
+7 -2
View File
@@ -1,5 +1,10 @@
This files describes API changes in the quiz code.
This file describes API changes in the resource code.
=== 4.3 ===
* Function resource_get_optional_details() has now one new parameter, $showtype, to decide whether the file type should be
displayed or not (regardless the display option is enabled).
=== 4.0 ===
* Functions resource_print_heading and resource_print_intro have been deprecated in favour for the activity header.
* Functions resource_print_heading and resource_print_intro have been deprecated in favour for the activity header.