MDL-67585 core_course: add content_item_readonly_repository class

Add a read-only repository, which returns the list of content items
which can be added to a course. This class does not handle access
control nor user-related data pertaining to to core content_items.
This will be handled in a future commit in any calling code using
the repositories.
This commit is contained in:
Jake Dallimore 2020-01-20 14:11:33 +08:00
parent 94420851a5
commit e843336ee6
3 changed files with 301 additions and 0 deletions

View File

@ -0,0 +1,185 @@
<?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/>.
/**
* Contains class content_item_repository, for fetching content_items.
*
* @package core
* @subpackage course
* @copyright 2020 Jake Dallimore <jrhdallimore@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_course\local\repository;
defined('MOODLE_INTERNAL') || die();
use core_course\local\entity\content_item;
use core_course\local\entity\lang_string_title;
use core_course\local\entity\string_title;
/**
* The class content_item_repository, for reading content_items.
*
* @copyright 2020 Jake Dallimore <jrhdallimore@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class content_item_readonly_repository implements content_item_readonly_repository_interface {
/**
* Get the help string for content items representing core modules.
*
* @param string $modname the module name.
* @return string the help string, including help link.
*/
private function get_core_module_help_string(string $modname): string {
global $OUTPUT;
$help = '';
$sm = get_string_manager();
if ($sm->string_exists('modulename_help', $modname)) {
$help = get_string('modulename_help', $modname);
if ($sm->string_exists('modulename_link', $modname)) { // Link to further info in Moodle docs.
$link = get_string('modulename_link', $modname);
$linktext = get_string('morehelp');
$help .= \html_writer::tag('div', $OUTPUT->doc_link($link, $linktext, true), ['class' => 'helpdoclink']);
}
}
return $help;
}
/**
* Create a content_item object based on legacy data returned from the get_shortcuts hook implementations.
*
* @param \stdClass $item the stdClass of legacy data.
* @return content_item a content item object.
*/
private function content_item_from_legacy_data(\stdClass $item): content_item {
// Make sure the legacy data results in a content_item with id = 0.
// Even with an id, we can't uniquely identify the item, because we can't guarantee what component it came from.
// An id of -1, signifies this.
$item->id = -1;
if (is_string($item->title)) {
$item->title = new string_title($item->title);
} else if ($item->title instanceof \lang_string) {
$item->title = new lang_string_title($item->title->get_identifier(), $item->title->get_component());
}
// Legacy items had names which are in one of 2 forms:
// modname, i.e. 'assign' or
// modname:link, i.e. lti:http://etc...
// We need to grab the module name out to create the componentname.
$modname = (strpos($item->name, ':') !== false) ? explode(':', $item->name)[0] : $item->name;
return new content_item($item->id, $item->name, $item->title, $item->link, $item->icon, $item->help ?? '',
$item->archetype, 'mod_' . $modname);
}
/**
* Create a stdClass type object based on a content_item instance.
*
* @param content_item $contentitem
* @return \stdClass the legacy data.
*/
private function content_item_to_legacy_data(content_item $contentitem): \stdClass {
$item = new \stdClass();
$item->id = $contentitem->get_id();
$item->name = $contentitem->get_name();
$item->title = $contentitem->get_title();
$item->link = $contentitem->get_link();
$item->icon = $contentitem->get_icon();
$item->help = $contentitem->get_help();
$item->archetype = $contentitem->get_archetype();
$item->componentname = $contentitem->get_component_name();
return $item;
}
/**
* Get the list of potential content items for the given course.
*
* @param \stdClass $course the course
* @param \stdClass $user the user, to pass to plugins implementing callbacks.
* @return array the array of content_item objects
*/
public function find_all_for_course(\stdClass $course, \stdClass $user): array {
global $OUTPUT, $DB;
// Get all modules so we know which plugins are enabled and able to add content.
// Only module plugins may add content items.
$modules = $DB->get_records('modules', ['visible' => 1]);
$return = [];
// A moodle_url is expected and required by modules in their implementation of the hook 'get_shortcuts'.
$urlbase = new \moodle_url('/course/mod.php', ['id' => $course->id]);
// Now, generate the content_items.
foreach ($modules as $modid => $mod) {
// Create the content item for the module itself.
// If the module chooses to implement the hook, this may be thrown away.
$help = $this->get_core_module_help_string($mod->name);
$archetype = plugin_supports('mod', $mod->name, FEATURE_MOD_ARCHETYPE, MOD_ARCHETYPE_OTHER);
$contentitem = new content_item(
$mod->id,
$mod->name,
new lang_string_title("modulename", $mod->name),
new \moodle_url($urlbase, ['add' => $mod->name]),
$OUTPUT->pix_icon('icon', '', $mod->name, ['class' => 'icon']),
$help,
$archetype,
'mod_' . $mod->name
);
// Next step is to get the dynamically generated content items for ecah module, if provided.
// This is achieved by implementation of the hook, 'get_shortcuts'.
// Give each plugin implementing the hook the main entry for REFERENCE ONLY.
// The current hook, get_shortcuts, expects a stdClass representation of the core module content_item entry.
$modcontentitemreference = $this->content_item_to_legacy_data($contentitem);
// Next, get the content_items from the module callback, if implemented.
$items = component_callback($mod->name, 'get_shortcuts', [$modcontentitemreference], null);
if (!is_null($items)) {
foreach ($items as $item) {
// Fall back to the plugin parent value if the subtype didn't provide anything.
$item->archetype = $item->archetype ?? $contentitem->get_archetype();
$item->icon = $item->icon ?? $contentitem->get_icon();
// If the module provides the helplink property, append it to the help text to match the look and feel
// of the default course modules.
if (isset($item->help) && isset($item->helplink)) {
$linktext = get_string('morehelp');
$item->help .= \html_writer::tag('div',
$OUTPUT->doc_link($item->helplink, $linktext, true), ['class' => 'helpdoclink']);
}
// Create a content_item instance from the legacy callback data.
$plugincontentitem = $this->content_item_from_legacy_data($item);
$return[] = $plugincontentitem;
}
// If get_shortcuts() callback is defined, the default module action is not added.
// It is a responsibility of the callback to add it to the return value unless it is not needed.
continue;
}
// The callback get_shortcuts() was not found, use the default item for the activity chooser.
$return[] = $contentitem;
}
return $return;
}
}

View File

@ -0,0 +1,41 @@
<?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/>.
/**
* Contains the interface content_item_readonly_repository_interface, defining operations for readonly content item repositories.
*
* This interface is not considered a published interface and serves to govern internal, local repository objects only.
* All calling code should use instances of the service classes, and should not interact with repositories directly.
*
* @package core
* @subpackage course
* @copyright 2020 Jake Dallimore <jrhdallimore@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_course\local\repository;
defined('MOODLE_INTERNAL') || die();
interface content_item_readonly_repository_interface {
/**
* Find all content items for a given course and user.
*
* @param \stdClass $course the course object.
* @param \stdClass $user the user object.
* @return array the array of content items.
*/
public function find_all_for_course(\stdClass $course, \stdClass $user): array;
}

View File

@ -0,0 +1,75 @@
<?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/>.
/**
* Contains the test class for the content_item_readonly_repository class.
*
* @package core
* @subpackage course
* @copyright 2020 Jake Dallimore <jrhdallimore@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tests\core_course;
defined('MOODLE_INTERNAL') || die();
use core_course\local\entity\content_item;
use core_course\local\repository\content_item_readonly_repository;
/**
* The test class for the content_item_readonly_repository class.
*
* @copyright 2020 Jake Dallimore <jrhdallimore@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class content_item_readonly_repository_testcase extends \advanced_testcase {
/**
* Test the repository method, find_all_for_course().
*/
public function test_find_all_for_course() {
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$user = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
$cir = new content_item_readonly_repository();
$items = $cir->find_all_for_course($course, $user);
foreach ($items as $key => $item) {
$this->assertInstanceOf(content_item::class, $item);
$this->assertEquals($course->id, $item->get_link()->param('id'));
$this->assertNotNull($item->get_link()->param('add'));
}
}
/**
* Test verifying that content items for hidden modules are not returned.
*/
public function test_find_all_for_course_hidden_module() {
$this->resetAfterTest();
global $DB;
$course = $this->getDataGenerator()->create_course();
$user = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
$cir = new content_item_readonly_repository();
// Hide a module.
$module = $DB->get_record('modules', ['id' => 1]);
$DB->set_field("modules", "visible", "0", ["id" => $module->id]);
$items = $cir->find_all_for_course($course, $user);
$this->assertArrayNotHasKey($module->name, $items);
}
}