mirror of
https://github.com/moodle/moodle.git
synced 2025-01-18 22:08:20 +01:00
MDL-67585 core_course: add hook get_all_content_items
Returns all content items which are provided by the plugin, irrespective of whether or not a user can see an item in a particular course. This is used to generate a global list of content items, allowing for admin level features to be added.
This commit is contained in:
parent
520add19d6
commit
57dfcf951d
@ -76,4 +76,13 @@ class caching_content_item_readonly_repository implements content_item_readonly_
|
||||
$this->cachestore->set($key, $contentitems);
|
||||
return $contentitems;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all the content items made available by core and plugins.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function find_all(): array {
|
||||
return $this->contentitemrepository->find_all();
|
||||
}
|
||||
}
|
||||
|
@ -141,6 +141,28 @@ class content_item_readonly_repository implements content_item_readonly_reposito
|
||||
return $contentitems;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the content items for a subplugin.
|
||||
*
|
||||
* @param string $parentpluginname
|
||||
* @param content_item $modulecontentitem
|
||||
* @return array
|
||||
*/
|
||||
private function get_subplugin_all_content_items(string $parentpluginname, content_item $modulecontentitem): array {
|
||||
$contentitems = [];
|
||||
$pluginmanager = \core_plugin_manager::instance();
|
||||
foreach ($pluginmanager->get_subplugins_of_plugin($parentpluginname) as $subpluginname => $subplugin) {
|
||||
// Call the hook, but with a copy of the module content item data.
|
||||
$spcontentitems = component_callback($subpluginname, 'get_all_content_items', [$modulecontentitem], null);
|
||||
if (!is_null($spcontentitems)) {
|
||||
foreach ($spcontentitems as $spcontentitem) {
|
||||
$contentitems[] = $spcontentitem;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $contentitems;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to make sure any legacy items have certain properties, which, if missing are inherited from the parent module item.
|
||||
*
|
||||
@ -155,6 +177,60 @@ class content_item_readonly_repository implements content_item_readonly_reposito
|
||||
return $legacyitem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all the available content items, not restricted to course or user.
|
||||
*
|
||||
* @return array the array of content items.
|
||||
*/
|
||||
public function find_all(): 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 = [];
|
||||
|
||||
// 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(''), // No course scope, so just an empty link.
|
||||
$OUTPUT->pix_icon('icon', '', $mod->name, ['class' => 'icon']),
|
||||
$help,
|
||||
$archetype,
|
||||
'mod_' . $mod->name
|
||||
);
|
||||
|
||||
$modcontentitemreference = clone($contentitem);
|
||||
|
||||
if (component_callback_exists('mod_' . $mod->name, 'get_all_content_items')) {
|
||||
// Call the module hooks for this module.
|
||||
$plugincontentitems = component_callback('mod_' . $mod->name, 'get_all_content_items',
|
||||
[$modcontentitemreference], []);
|
||||
if (!empty($plugincontentitems)) {
|
||||
array_push($return, ...$plugincontentitems);
|
||||
}
|
||||
|
||||
// Now, get those for subplugins of the module.
|
||||
$subplugincontentitems = $this->get_subplugin_all_content_items('mod_' . $mod->name, $modcontentitemreference);
|
||||
if (!empty($subplugincontentitems)) {
|
||||
array_push($return, ...$subplugincontentitems);
|
||||
}
|
||||
} else {
|
||||
// Neither callback was found, so just use the default module content item.
|
||||
$return[] = $contentitem;
|
||||
}
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of potential content items for the given course.
|
||||
*
|
||||
|
@ -38,4 +38,11 @@ interface content_item_readonly_repository_interface {
|
||||
* @return array the array of content items.
|
||||
*/
|
||||
public function find_all_for_course(\stdClass $course, \stdClass $user): array;
|
||||
|
||||
/**
|
||||
* Find all content items that can be presented, irrespective of course.
|
||||
*
|
||||
* @return array the array of content items.
|
||||
*/
|
||||
public function find_all(): array;
|
||||
}
|
||||
|
@ -49,6 +49,30 @@ class content_item_service {
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all content items which may be added to courses, irrespective of course caps, for site admin views, etc.
|
||||
*
|
||||
* @return array the array of exported content items.
|
||||
*/
|
||||
public function get_all_content_items(): array {
|
||||
global $PAGE;
|
||||
$allcontentitems = $this->repository->find_all();
|
||||
|
||||
// Export the objects to get the formatted objects for transfer/display.
|
||||
$ciexporter = new course_content_items_exporter(
|
||||
$allcontentitems,
|
||||
['context' => \context_system::instance()]
|
||||
);
|
||||
$exported = $ciexporter->export($PAGE->get_renderer('core'));
|
||||
|
||||
// Sort by title for return.
|
||||
usort($exported->content_items, function($a, $b) {
|
||||
return $a->title > $b->title;
|
||||
});
|
||||
|
||||
return $exported->content_items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a representation of the available content items, for a user in a course.
|
||||
*
|
||||
|
@ -72,4 +72,33 @@ class content_item_readonly_repository_testcase extends \advanced_testcase {
|
||||
$items = $cir->find_all_for_course($course, $user);
|
||||
$this->assertArrayNotHasKey($module->name, $items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test confirming that all content items can be fetched, even those which require certain caps when in a course.
|
||||
*/
|
||||
public function test_find_all() {
|
||||
$this->resetAfterTest();
|
||||
global $DB;
|
||||
|
||||
// We'll compare our results to those which are course-specific.
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$user = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
|
||||
$teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
|
||||
assign_capability('mod/lti:addmanualinstance', CAP_PROHIBIT, $teacherrole->id, \context_course::instance($course->id));
|
||||
$cir = new content_item_readonly_repository();
|
||||
|
||||
// Course specific - lti won't be returned as the user doesn't have the required cap.
|
||||
$forcourse = $cir->find_all_for_course($course, $user);
|
||||
$forcourse = array_filter($forcourse, function($contentitem) {
|
||||
return $contentitem->get_name() === 'lti';
|
||||
});
|
||||
$this->assertEmpty($forcourse);
|
||||
|
||||
// All - all items are returned, including lti.
|
||||
$all = $cir->find_all();
|
||||
$all = array_filter($all, function($contentitem) {
|
||||
return $contentitem->get_name() === 'lti';
|
||||
});
|
||||
$this->assertCount(1, $all);
|
||||
}
|
||||
}
|
||||
|
@ -103,4 +103,34 @@ class services_content_item_service_testcase extends \advanced_testcase {
|
||||
$this->assertStringContainsString('sr=7', $item->link);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test confirming that all content items can be fetched irrespective of permissions.
|
||||
*/
|
||||
public function test_get_all_content_items() {
|
||||
$this->resetAfterTest();
|
||||
global $DB;
|
||||
|
||||
// Create a user in a course.
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$user = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
|
||||
|
||||
$cis = new content_item_service(new content_item_readonly_repository());
|
||||
$allcontentitems = $cis->get_all_content_items();
|
||||
$coursecontentitems = $cis->get_content_items_for_user_in_course($user, $course);
|
||||
|
||||
// The call to get_all_content_items() should return the same items as for the course,
|
||||
// given the user in an editing teacher and can add manual lti instances.
|
||||
$this->assertEquals(array_column($allcontentitems, 'name'), array_column($coursecontentitems, 'name'));
|
||||
|
||||
// Now removing the cap 'mod/lti:addinstance'. This will restrict those items returned by the course-specific method.
|
||||
$teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
|
||||
assign_capability('mod/lti:addinstance', CAP_PROHIBIT, $teacherrole->id, \context_course::instance($course->id));
|
||||
|
||||
// Verify that all items, including lti, are still returned by the get_all_content_items() call.
|
||||
$allcontentitems = $cis->get_all_content_items();
|
||||
$coursecontentitems = $cis->get_content_items_for_user_in_course($user, $course);
|
||||
$this->assertNotContains('lti', array_column($coursecontentitems, 'name'));
|
||||
$this->assertContains('lti', array_column($allcontentitems, 'name'));
|
||||
}
|
||||
}
|
||||
|
@ -295,6 +295,67 @@ function lti_get_course_content_items(\core_course\local\entity\content_item $de
|
||||
return $types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all content items which can be added to any course.
|
||||
*
|
||||
* @param \core_course\local\entity\content_item $defaultmodulecontentitem
|
||||
* @return array the array of content items.
|
||||
*/
|
||||
function mod_lti_get_all_content_items(\core_course\local\entity\content_item $defaultmodulecontentitem): array {
|
||||
global $OUTPUT, $CFG;
|
||||
require_once($CFG->dirroot . '/mod/lti/locallib.php'); // For access to constants.
|
||||
|
||||
// The 'External tool' entry (the main module content item), should always take the id of 1.
|
||||
$types = [new \core_course\local\entity\content_item(
|
||||
1,
|
||||
$defaultmodulecontentitem->get_name(),
|
||||
$defaultmodulecontentitem->get_title(),
|
||||
$defaultmodulecontentitem->get_link(),
|
||||
$defaultmodulecontentitem->get_icon(),
|
||||
$defaultmodulecontentitem->get_help(),
|
||||
$defaultmodulecontentitem->get_archetype(),
|
||||
$defaultmodulecontentitem->get_component_name()
|
||||
)];
|
||||
|
||||
foreach (lti_get_lti_types() as $ltitype) {
|
||||
if ($ltitype->coursevisible != LTI_COURSEVISIBLE_ACTIVITYCHOOSER) {
|
||||
continue;
|
||||
}
|
||||
$type = new stdClass();
|
||||
$type->id = $ltitype->id;
|
||||
$type->modclass = MOD_CLASS_ACTIVITY;
|
||||
$type->name = 'lti_type_' . $ltitype->id;
|
||||
// Clean the name. We don't want tags here.
|
||||
$type->title = clean_param($ltitype->name, PARAM_NOTAGS);
|
||||
$trimmeddescription = trim($ltitype->description);
|
||||
$type->help = '';
|
||||
if ($trimmeddescription != '') {
|
||||
// Clean the description. We don't want tags here.
|
||||
$type->help = clean_param($trimmeddescription, PARAM_NOTAGS);
|
||||
$type->helplink = get_string('modulename_shortcut_link', 'lti');
|
||||
}
|
||||
if (empty($ltitype->icon)) {
|
||||
$type->icon = $OUTPUT->pix_icon('icon', '', 'lti', array('class' => 'icon'));
|
||||
} else {
|
||||
$type->icon = html_writer::empty_tag('img', array('src' => $ltitype->icon, 'alt' => $ltitype->name, 'class' => 'icon'));
|
||||
}
|
||||
$type->link = new moodle_url('/course/modedit.php', array('add' => 'lti', 'return' => 0, 'typeid' => $ltitype->id));
|
||||
|
||||
$types[] = new \core_course\local\entity\content_item(
|
||||
$type->id + 1,
|
||||
$type->name,
|
||||
new \core_course\local\entity\string_title($type->title),
|
||||
$type->link,
|
||||
$type->icon,
|
||||
$type->help,
|
||||
$defaultmodulecontentitem->get_archetype(),
|
||||
$defaultmodulecontentitem->get_component_name()
|
||||
);
|
||||
}
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a coursemodule object, this function returns the extra
|
||||
* information needed to print this activity in various places.
|
||||
|
@ -437,5 +437,20 @@ class mod_lti_lib_testcase extends advanced_testcase {
|
||||
$this->assertContains($sitetoolrecord->id + 1, $ids);
|
||||
$this->assertContains($course2toolrecord->id + 1, $ids);
|
||||
$this->assertNotContains($sitetoolrecordnonchooser->id + 1, $ids);
|
||||
|
||||
// When fetching all content items, we expect to see all items available in activity choosers (in any course),
|
||||
// plus the default module content item ('external tool').
|
||||
$this->setAdminUser();
|
||||
$allitems = mod_lti_get_all_content_items($defaultmodulecontentitem);
|
||||
$this->assertCount(4, $allitems);
|
||||
$ids = [];
|
||||
foreach ($allitems as $item) {
|
||||
$ids[] = $item->get_id();
|
||||
}
|
||||
$this->assertContains(1, $ids);
|
||||
$this->assertContains($sitetoolrecord->id + 1, $ids);
|
||||
$this->assertContains($course1toolrecord->id + 1, $ids);
|
||||
$this->assertContains($course2toolrecord->id + 1, $ids);
|
||||
$this->assertNotContains($sitetoolrecordnonchooser->id + 1, $ids);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user