mirror of
https://github.com/moodle/moodle.git
synced 2025-04-14 04:52:36 +02:00
MDL-72099 core_contenbank: Add context navigation
This commit is contained in:
parent
9145d80b0b
commit
1d4edcb57b
@ -224,6 +224,37 @@ class contentbank {
|
||||
return $contents;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return all the context where a user has all the given capabilities.
|
||||
*
|
||||
* @param string $capability The capability the user needs to have.
|
||||
* @param int|null $userid Optional userid. $USER by default.
|
||||
* @return array Array of the courses and course categories where the user has the given capability.
|
||||
*/
|
||||
public function get_contexts_with_capabilities_by_user($capability = 'moodle/contentbank:access', $userid = null): array {
|
||||
global $USER;
|
||||
|
||||
if (!$userid) {
|
||||
$userid = $USER->id;
|
||||
}
|
||||
|
||||
$categoriescache = \cache::make('core', 'contentbank_allowed_categories');
|
||||
$coursescache = \cache::make('core', 'contentbank_allowed_courses');
|
||||
|
||||
$categories = $categoriescache->get($userid);
|
||||
$courses = $coursescache->get($userid);
|
||||
|
||||
if ($categories === false || $courses === false) {
|
||||
list($categories, $courses) = get_user_capability_contexts($capability, true, $userid, true,
|
||||
'shortname, ctxlevel, ctxinstance, ctxid', 'name, ctxlevel, ctxinstance, ctxid', 'shortname', 'name');
|
||||
$categoriescache->set($userid, $categories);
|
||||
$coursescache->set($userid, $courses);
|
||||
}
|
||||
|
||||
return [$categories, $courses];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create content from a file information.
|
||||
*
|
||||
|
@ -24,6 +24,7 @@
|
||||
|
||||
namespace core_contentbank\output;
|
||||
|
||||
use core_contentbank\contentbank;
|
||||
use renderable;
|
||||
use templatable;
|
||||
use renderer_base;
|
||||
@ -53,17 +54,29 @@ class bankcontent implements renderable, templatable {
|
||||
*/
|
||||
private $context;
|
||||
|
||||
/**
|
||||
* @var array Course categories that the user has access to.
|
||||
*/
|
||||
private $allowedcategories;
|
||||
|
||||
/**
|
||||
* @var array Courses that the user has access to.
|
||||
*/
|
||||
private $allowedcourses;
|
||||
|
||||
/**
|
||||
* Construct this renderable.
|
||||
*
|
||||
* @param \core_contentbank\content[] $contents Array of content bank contents.
|
||||
* @param array $toolbar List of content bank toolbar options.
|
||||
* @param array $toolbar List of content bank toolbar options.
|
||||
* @param \context $context Optional context to check (default null)
|
||||
* @param contentbank $cb Contenbank object.
|
||||
*/
|
||||
public function __construct(array $contents, array $toolbar, \context $context = null) {
|
||||
public function __construct(array $contents, array $toolbar, \context $context = null, contentbank $cb) {
|
||||
$this->contents = $contents;
|
||||
$this->toolbar = $toolbar;
|
||||
$this->context = $context;
|
||||
list($this->allowedcategories, $this->allowedcourses) = $cb->get_contexts_with_capabilities_by_user();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -73,7 +86,7 @@ class bankcontent implements renderable, templatable {
|
||||
* @return stdClass
|
||||
*/
|
||||
public function export_for_template(renderer_base $output): stdClass {
|
||||
global $PAGE;
|
||||
global $PAGE, $SITE;
|
||||
|
||||
$PAGE->requires->js_call_amd('core_contentbank/search', 'init');
|
||||
$PAGE->requires->js_call_amd('core_contentbank/sort', 'init');
|
||||
@ -118,6 +131,40 @@ class bankcontent implements renderable, templatable {
|
||||
$data->tools[] = $tool;
|
||||
}
|
||||
|
||||
$allowedcontexts = [];
|
||||
$systemcontext = \context_system::instance();
|
||||
if (has_capability('moodle/contentbank:access', $systemcontext)) {
|
||||
$allowedcontexts[$systemcontext->id] = get_string('coresystem');
|
||||
}
|
||||
$options = [];
|
||||
foreach ($this->allowedcategories as $allowedcategory) {
|
||||
$options[$allowedcategory->ctxid] = $allowedcategory->name;
|
||||
}
|
||||
if (!empty($options)) {
|
||||
$allowedcontexts['categories'] = [get_string('coursecategories') => $options];
|
||||
}
|
||||
$options = [];
|
||||
foreach ($this->allowedcourses as $allowedcourse) {
|
||||
// Don't add the frontpage course to the list.
|
||||
if ($allowedcourse->id != $SITE->id) {
|
||||
$options[$allowedcourse->ctxid] = $allowedcourse->shortname;
|
||||
}
|
||||
}
|
||||
if (!empty($options)) {
|
||||
$allowedcontexts['courses'] = [get_string('courses') => $options];
|
||||
}
|
||||
if (!empty($allowedcontexts)) {
|
||||
$url = new \moodle_url('/contentbank/index.php');
|
||||
$singleselect = new \single_select(
|
||||
$url,
|
||||
'contextid',
|
||||
$allowedcontexts,
|
||||
$this->context->id,
|
||||
get_string('choosecontext', 'core_contentbank')
|
||||
);
|
||||
$data->allowedcontexts = $singleselect->export_for_template($output);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
@ -116,7 +116,7 @@ if ($errormsg !== '' && get_string_manager()->string_exists($errormsg, 'core_con
|
||||
}
|
||||
|
||||
// Render the contentbank contents.
|
||||
$folder = new \core_contentbank\output\bankcontent($foldercontents, $toolbar, $context);
|
||||
$folder = new \core_contentbank\output\bankcontent($foldercontents, $toolbar, $context, $cb);
|
||||
echo $OUTPUT->render($folder);
|
||||
|
||||
echo $OUTPUT->box_end();
|
||||
|
@ -75,15 +75,41 @@
|
||||
{
|
||||
"icon": "i/export"
|
||||
}
|
||||
],
|
||||
"allowedcontexts": [
|
||||
{
|
||||
"name": "contextid",
|
||||
"method": "get",
|
||||
"action": "http://localhost/stable_master/contentbank/index.php",
|
||||
"options": [
|
||||
{
|
||||
"value": "1",
|
||||
"name": "System",
|
||||
"selected": true,
|
||||
"optgroup": false
|
||||
},
|
||||
{
|
||||
"value": "32",
|
||||
"name": "Category 1",
|
||||
"selected": false,
|
||||
"optgroup": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
<div class="content-bank-container {{#viewlist}}view-list{{/viewlist}} {{^viewlist}}view-grid{{/viewlist}}"
|
||||
data-region="contentbank">
|
||||
<div class="d-flex justify-content-between flex-column flex-sm-row">
|
||||
<div class="cb-search-container mb-2">
|
||||
{{>core_contentbank/bankcontent/search}}
|
||||
<div class="d-flex">
|
||||
<div class="cb-navigation-container mb-2 mr-2">
|
||||
{{>core_contentbank/bankcontent/navigation}}
|
||||
</div>
|
||||
<div class="cb-search-container mb-2">
|
||||
{{>core_contentbank/bankcontent/search}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="cb-toolbar-container mb-2 d-flex">
|
||||
{{>core_contentbank/bankcontent/toolbar}}
|
||||
|
27
contentbank/templates/bankcontent/navigation.mustache
Normal file
27
contentbank/templates/bankcontent/navigation.mustache
Normal file
@ -0,0 +1,27 @@
|
||||
{{!
|
||||
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_contentbank/bankcontent/navigation
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
}
|
||||
}}
|
||||
|
||||
{{#allowedcontexts}}
|
||||
{{> core/single_select }}
|
||||
{{/allowedcontexts}}
|
104
contentbank/tests/behat/navigate_content.feature
Normal file
104
contentbank/tests/behat/navigate_content.feature
Normal file
@ -0,0 +1,104 @@
|
||||
@core @core_contentbank @core_h5p @contentbank_h5p @_file_upload @javascript
|
||||
Feature: Navigate to different contexts in the content bank
|
||||
In order to navigate easily in the content bank
|
||||
I need to be able to view dropdown with all allowed contexts in the content bank
|
||||
|
||||
Background:
|
||||
Given I log in as "admin"
|
||||
And the following "categories" exist:
|
||||
| name | category | idnumber |
|
||||
| Cat 1 | 0 | CAT1 |
|
||||
| Cat 2 | 0 | CAT2 |
|
||||
And the following "courses" exist:
|
||||
| fullname | shortname | category |
|
||||
| Course 0 | C0 | |
|
||||
| Course 1 | C1 | CAT1 |
|
||||
| Course 2 | C2 | CAT2 |
|
||||
And I navigate to "H5P > Manage H5P content types" in site administration
|
||||
And I upload "h5p/tests/fixtures/filltheblanks.h5p" file to "H5P content type" filemanager
|
||||
And I click on "Upload H5P content types" "button" in the "#fitem_id_uploadlibraries" "css_element"
|
||||
And the following "contentbank content" exist:
|
||||
| contextlevel | reference | contenttype | user | contentname | filepath |
|
||||
| System | | contenttype_h5p | admin | santjordi.h5p | /h5p/tests/fixtures/filltheblanks.h5p |
|
||||
| Category | CAT1 | contenttype_h5p | admin | santjordi_rose.h5p | /h5p/tests/fixtures/filltheblanks.h5p |
|
||||
| Category | CAT2 | contenttype_h5p | admin | SantJordi_book | /h5p/tests/fixtures/filltheblanks.h5p |
|
||||
| Course | C0 | contenttype_h5p | admin | Dragon.h5p | /h5p/tests/fixtures/filltheblanks.h5p |
|
||||
| Course | C1 | contenttype_h5p | admin | princess.h5p | /h5p/tests/fixtures/filltheblanks.h5p |
|
||||
| Course | C2 | contenttype_h5p | admin | mathsbook.h5p | /h5p/tests/fixtures/filltheblanks.h5p |
|
||||
|
||||
Scenario: Admins can view and navigate to all the contexts in the content bank
|
||||
Given I am on site homepage
|
||||
And I turn editing mode on
|
||||
And I add the "Navigation" block if not present
|
||||
And I expand "Site pages" node
|
||||
When I click on "Content bank" "link"
|
||||
And "contextid" "select" should exist
|
||||
And the "contextid" select box should contain "System"
|
||||
And the "contextid" select box should contain "Cat 1"
|
||||
And the "contextid" select box should contain "Cat 2"
|
||||
And the "contextid" select box should contain "C0"
|
||||
And the "contextid" select box should contain "C1"
|
||||
And the "contextid" select box should contain "C2"
|
||||
And I should see "santjordi.h5p"
|
||||
And I should not see "santjordi_rose.h5p"
|
||||
And I should not see "Dragon.h5p"
|
||||
And I click on "contextid" "select"
|
||||
And I click on "Cat 1" "option"
|
||||
Then I should not see "santjordi.h5p"
|
||||
And I should see "santjordi_rose.h5p"
|
||||
And I should not see "Dragon.h5p"
|
||||
And I click on "contextid" "select"
|
||||
And I click on "C0" "option"
|
||||
And I should not see "santjordi.h5p"
|
||||
And I should not see "santjordi_rose.h5p"
|
||||
And I should see "Dragon.h5p"
|
||||
|
||||
Scenario: Teachers can view and navigate to contexts in the content bank based on their permissions
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname |
|
||||
| teacher | Joseba | Cilarte |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| teacher | C0 | editingteacher |
|
||||
| teacher | C1 | editingteacher |
|
||||
And I log out
|
||||
And I am on the "C0" "Course" page logged in as "teacher"
|
||||
And I turn editing mode on
|
||||
And I add the "Navigation" block if not present
|
||||
And I expand "Site pages" node
|
||||
When I click on "Content bank" "link"
|
||||
And "contextid" "select" should exist
|
||||
And the "contextid" select box should contain "C0"
|
||||
And the "contextid" select box should contain "C1"
|
||||
And the "contextid" select box should not contain "System"
|
||||
And the "contextid" select box should not contain "Cat 1"
|
||||
And the "contextid" select box should not contain "Cat 2"
|
||||
And the "contextid" select box should not contain "C2"
|
||||
And I should see "Dragon.h5p"
|
||||
And I should not see "princess.h5p"
|
||||
And I should not see "santjordi.h5p"
|
||||
And I should not see "santjordi_rose.h5p"
|
||||
And I click on "contextid" "select"
|
||||
And I click on "C1" "option"
|
||||
Then I should not see "Dragon.h5p"
|
||||
And I should see "princess.h5p"
|
||||
And I should not see "santjordi.h5p"
|
||||
And I should not see "santjordi_rose.h5p"
|
||||
And the following "role assigns" exist:
|
||||
| user | role | contextlevel | reference |
|
||||
| teacher | manager | Category | CAT1 |
|
||||
And I am on the "C0" "Course" page logged in as "teacher"
|
||||
And I expand "Site pages" node
|
||||
When I click on "Content bank" "link"
|
||||
And "contextid" "select" should exist
|
||||
And the "contextid" select box should contain "C0"
|
||||
And the "contextid" select box should contain "C1"
|
||||
And the "contextid" select box should contain "Cat 1"
|
||||
And the "contextid" select box should not contain "System"
|
||||
And the "contextid" select box should not contain "Cat 2"
|
||||
And the "contextid" select box should not contain "C2"
|
||||
And I should see "Dragon.h5p"
|
||||
And I click on "contextid" "select"
|
||||
And I click on "Cat 1" "option"
|
||||
And I should not see "Dragon.h5p"
|
||||
And I should see "santjordi_rose.h5p"
|
@ -41,6 +41,8 @@ $string['cachedef_calendar_subscriptions'] = 'Calendar subscriptions';
|
||||
$string['cachedef_calendar_categories'] = 'Calendar course categories that a user can access';
|
||||
$string['cachedef_capabilities'] = 'System capabilities list';
|
||||
$string['cachedef_config'] = 'Config settings';
|
||||
$string['cachedef_contentbank_allowed_categories'] = 'Allowed content bank course categories for current user';
|
||||
$string['cachedef_contentbank_allowed_courses'] = 'Allowed content bank courses for current user';
|
||||
$string['cachedef_contentbank_enabled_extensions'] = 'Allowed extensions and its supporter plugins in content bank';
|
||||
$string['cachedef_contentbank_context_extensions'] = 'Allowed extensions and its supporter plugins in a content bank context';
|
||||
$string['cachedef_coursecat'] = 'Course categories lists for particular user';
|
||||
|
@ -25,6 +25,7 @@
|
||||
$string['author'] = 'Author';
|
||||
$string['contentbank'] = 'Content bank';
|
||||
$string['close'] = 'Close';
|
||||
$string['choosecontext'] = 'Choose course or category...';
|
||||
$string['contentbankpreferences'] = 'Content bank preferences';
|
||||
$string['contentdeleted'] = 'The content has been deleted.';
|
||||
$string['contentname'] = 'Content name';
|
||||
|
@ -4101,6 +4101,116 @@ function count_role_users($roleid, context $context, $parent = false) {
|
||||
return $DB->count_records_sql($sql, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function gets the list of course and course category contexts that this user has a particular capability in.
|
||||
*
|
||||
* It is now reasonably efficient, but bear in mind that if there are users who have the capability
|
||||
* everywhere, it may return an array of all contexts.
|
||||
*
|
||||
* @param string $capability Capability in question
|
||||
* @param int $userid User ID or null for current user
|
||||
* @param bool $getcategories Wether to return also course_categories
|
||||
* @param bool $doanything True if 'doanything' is permitted (default)
|
||||
* @param string $coursefieldsexceptid Leave blank if you only need 'id' in the course records;
|
||||
* otherwise use a comma-separated list of the fields you require, not including id.
|
||||
* Add ctxid, ctxpath, ctxdepth etc to return course context information for preloading.
|
||||
* @param string $categoryfieldsexceptid Leave blank if you only need 'id' in the course records;
|
||||
* otherwise use a comma-separated list of the fields you require, not including id.
|
||||
* Add ctxid, ctxpath, ctxdepth etc to return course context information for preloading.
|
||||
* @param string $courseorderby If set, use a comma-separated list of fields from course
|
||||
* table with sql modifiers (DESC) if needed
|
||||
* @param string $categoryorderby If set, use a comma-separated list of fields from course_category
|
||||
* table with sql modifiers (DESC) if needed
|
||||
* @param int $limit Limit the number of courses to return on success. Zero equals all entries.
|
||||
* @return array Array of categories and courses.
|
||||
*/
|
||||
function get_user_capability_contexts(string $capability, bool $getcategories, $userid = null, $doanything = true,
|
||||
$coursefieldsexceptid = '', $categoryfieldsexceptid = '', $courseorderby = '',
|
||||
$categoryorderby = '', $limit = 0): array {
|
||||
global $DB, $USER;
|
||||
|
||||
// Default to current user.
|
||||
if (!$userid) {
|
||||
$userid = $USER->id;
|
||||
}
|
||||
|
||||
if ($doanything && is_siteadmin($userid)) {
|
||||
// If the user is a site admin and $doanything is enabled then there is no need to restrict
|
||||
// the list of courses.
|
||||
$contextlimitsql = '';
|
||||
$contextlimitparams = [];
|
||||
} else {
|
||||
// Gets SQL to limit contexts ('x' table) to those where the user has this capability.
|
||||
list ($contextlimitsql, $contextlimitparams) = \core\access\get_user_capability_course_helper::get_sql(
|
||||
$userid, $capability);
|
||||
if (!$contextlimitsql) {
|
||||
// If the does not have this capability in any context, return false without querying.
|
||||
return [false, false];
|
||||
}
|
||||
|
||||
$contextlimitsql = 'WHERE' . $contextlimitsql;
|
||||
}
|
||||
|
||||
$categories = [];
|
||||
if ($getcategories) {
|
||||
$fieldlist = \core\access\get_user_capability_course_helper::map_fieldnames($categoryfieldsexceptid);
|
||||
if ($categoryorderby) {
|
||||
$fields = explode(',', $categoryorderby);
|
||||
$orderby = '';
|
||||
foreach ($fields as $field) {
|
||||
if ($orderby) {
|
||||
$orderby .= ',';
|
||||
}
|
||||
$orderby .= 'c.'.$field;
|
||||
}
|
||||
$orderby = 'ORDER BY '.$orderby;
|
||||
}
|
||||
$rs = $DB->get_recordset_sql("
|
||||
SELECT c.id $fieldlist
|
||||
FROM {course_categories} c
|
||||
JOIN {context} x ON c.id = x.instanceid AND x.contextlevel = ?
|
||||
$contextlimitsql
|
||||
$orderby", array_merge([CONTEXT_COURSECAT], $contextlimitparams));
|
||||
$basedlimit = $limit;
|
||||
foreach ($rs as $category) {
|
||||
$categories[] = $category;
|
||||
$basedlimit--;
|
||||
if ($basedlimit == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$courses = [];
|
||||
$fieldlist = \core\access\get_user_capability_course_helper::map_fieldnames($coursefieldsexceptid);
|
||||
if ($courseorderby) {
|
||||
$fields = explode(',', $courseorderby);
|
||||
$courseorderby = '';
|
||||
foreach ($fields as $field) {
|
||||
if ($courseorderby) {
|
||||
$courseorderby .= ',';
|
||||
}
|
||||
$courseorderby .= 'c.'.$field;
|
||||
}
|
||||
$courseorderby = 'ORDER BY '.$courseorderby;
|
||||
}
|
||||
$rs = $DB->get_recordset_sql("
|
||||
SELECT c.id $fieldlist
|
||||
FROM {course} c
|
||||
JOIN {context} x ON c.id = x.instanceid AND x.contextlevel = ?
|
||||
$contextlimitsql
|
||||
$courseorderby", array_merge([CONTEXT_COURSE], $contextlimitparams));
|
||||
foreach ($rs as $course) {
|
||||
$courses[] = $course;
|
||||
$limit--;
|
||||
if ($limit == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
$rs->close();
|
||||
return [$categories, $courses];
|
||||
}
|
||||
|
||||
/**
|
||||
* This function gets the list of courses that this user has a particular capability in.
|
||||
*
|
||||
@ -4118,84 +4228,20 @@ function count_role_users($roleid, context $context, $parent = false) {
|
||||
* @param int $limit Limit the number of courses to return on success. Zero equals all entries.
|
||||
* @return array|bool Array of courses, if none found false is returned.
|
||||
*/
|
||||
function get_user_capability_course($capability, $userid = null, $doanything = true, $fieldsexceptid = '', $orderby = '',
|
||||
$limit = 0) {
|
||||
global $DB, $USER;
|
||||
|
||||
// Default to current user.
|
||||
if (!$userid) {
|
||||
$userid = $USER->id;
|
||||
}
|
||||
|
||||
if ($doanything && is_siteadmin($userid)) {
|
||||
// If the user is a site admin and $doanything is enabled then there is no need to restrict
|
||||
// the list of courses.
|
||||
$contextlimitsql = '';
|
||||
$contextlimitparams = [];
|
||||
} else {
|
||||
// Gets SQL to limit contexts ('x' table) to those where the user has this capability.
|
||||
list ($contextlimitsql, $contextlimitparams) = \core\access\get_user_capability_course_helper::get_sql(
|
||||
$userid, $capability);
|
||||
if (!$contextlimitsql) {
|
||||
// If the does not have this capability in any context, return false without querying.
|
||||
return false;
|
||||
}
|
||||
|
||||
$contextlimitsql = 'WHERE' . $contextlimitsql;
|
||||
}
|
||||
|
||||
// Convert fields list and ordering
|
||||
$fieldlist = '';
|
||||
if ($fieldsexceptid) {
|
||||
$fields = array_map('trim', explode(',', $fieldsexceptid));
|
||||
foreach ($fields as $field) {
|
||||
// Context fields have a different alias.
|
||||
if (strpos($field, 'ctx') === 0) {
|
||||
switch($field) {
|
||||
case 'ctxlevel' :
|
||||
$realfield = 'contextlevel';
|
||||
break;
|
||||
case 'ctxinstance' :
|
||||
$realfield = 'instanceid';
|
||||
break;
|
||||
default:
|
||||
$realfield = substr($field, 3);
|
||||
break;
|
||||
}
|
||||
$fieldlist .= ',x.' . $realfield . ' AS ' . $field;
|
||||
} else {
|
||||
$fieldlist .= ',c.'.$field;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($orderby) {
|
||||
$fields = explode(',', $orderby);
|
||||
$orderby = '';
|
||||
foreach ($fields as $field) {
|
||||
if ($orderby) {
|
||||
$orderby .= ',';
|
||||
}
|
||||
$orderby .= 'c.'.$field;
|
||||
}
|
||||
$orderby = 'ORDER BY '.$orderby;
|
||||
}
|
||||
|
||||
$courses = array();
|
||||
$rs = $DB->get_recordset_sql("
|
||||
SELECT c.id $fieldlist
|
||||
FROM {course} c
|
||||
JOIN {context} x ON c.id = x.instanceid AND x.contextlevel = ?
|
||||
$contextlimitsql
|
||||
$orderby", array_merge([CONTEXT_COURSE], $contextlimitparams));
|
||||
foreach ($rs as $course) {
|
||||
$courses[] = $course;
|
||||
$limit--;
|
||||
if ($limit == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
$rs->close();
|
||||
return empty($courses) ? false : $courses;
|
||||
function get_user_capability_course($capability, $userid = null, $doanything = true, $fieldsexceptid = '',
|
||||
$orderby = '', $limit = 0) {
|
||||
list($categories, $courses) = get_user_capability_contexts(
|
||||
$capability,
|
||||
false,
|
||||
$userid,
|
||||
$doanything,
|
||||
$fieldsexceptid,
|
||||
'',
|
||||
$orderby,
|
||||
'',
|
||||
$limit
|
||||
);
|
||||
return $courses;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -429,4 +429,39 @@ class get_user_capability_course_helper {
|
||||
return self::create_sql($root);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Map fieldnames to get ready for the SQL query.
|
||||
*
|
||||
* @param string $fieldsexceptid A comma-separated list of the fields you require, not including id.
|
||||
* Add ctxid, ctxpath, ctxdepth etc to return course context information for preloading.
|
||||
* @return string Mapped field list for the SQL query.
|
||||
*/
|
||||
public static function map_fieldnames(string $fieldsexceptid = ''): string {
|
||||
// Convert fields list and ordering.
|
||||
$fieldlist = '';
|
||||
if ($fieldsexceptid) {
|
||||
$fields = array_map('trim', explode(',', $fieldsexceptid));
|
||||
foreach ($fields as $field) {
|
||||
// Context fields have a different alias.
|
||||
if (strpos($field, 'ctx') === 0) {
|
||||
switch($field) {
|
||||
case 'ctxlevel' :
|
||||
$realfield = 'contextlevel';
|
||||
break;
|
||||
case 'ctxinstance' :
|
||||
$realfield = 'instanceid';
|
||||
break;
|
||||
default:
|
||||
$realfield = substr($field, 3);
|
||||
break;
|
||||
}
|
||||
$fieldlist .= ',x.' . $realfield . ' AS ' . $field;
|
||||
} else {
|
||||
$fieldlist .= ',c.'.$field;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $fieldlist;
|
||||
}
|
||||
}
|
||||
|
@ -493,4 +493,27 @@ $definitions = array(
|
||||
'staticacceleration' => true,
|
||||
'datasource' => '\core_course\cache\course_image',
|
||||
],
|
||||
|
||||
// Cache the course categories where the user has access the content bank.
|
||||
'contentbank_allowed_categories' => [
|
||||
'mode' => cache_store::MODE_SESSION,
|
||||
'simplekeys' => true,
|
||||
'simpledata' => true,
|
||||
'invalidationevents' => [
|
||||
'changesincoursecat',
|
||||
'changesincategoryenrolment',
|
||||
],
|
||||
],
|
||||
|
||||
// Cache the courses where the user has access the content bank.
|
||||
'contentbank_allowed_courses' => [
|
||||
'mode' => cache_store::MODE_SESSION,
|
||||
'simplekeys' => true,
|
||||
'simpledata' => true,
|
||||
'invalidationevents' => [
|
||||
'changesincoursecat',
|
||||
'changesincategoryenrolment',
|
||||
'changesincourse',
|
||||
],
|
||||
],
|
||||
);
|
||||
|
@ -2230,6 +2230,89 @@ class core_accesslib_testcase extends advanced_testcase {
|
||||
$this->assert_course_ids([SITEID, $c1->id, $c2->id], $courses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests get_user_capability_contexts() which checks a capability across all courses and categories.
|
||||
* Testing for categories only because courses results are covered by test_get_user_capability_course.
|
||||
*/
|
||||
public function test_get_user_capability_contexts() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$generator = $this->getDataGenerator();
|
||||
$cap = 'moodle/contentbank:access';
|
||||
$defaultcategoryid = 1;
|
||||
|
||||
// The structure being created here is this:
|
||||
//
|
||||
// All tests work with the single capability 'moodle/contentbank:access'.
|
||||
// ROLE DEF/OVERRIDE .
|
||||
// Role: Allow Prohibit Empty .
|
||||
// System ALLOW PROHIBIT .
|
||||
// cat1 PREVENT ALLOW ALLOW .
|
||||
// cat3 ALLOW PROHIBIT .
|
||||
// cat2 PROHIBIT PROHIBIT PROHIBIT .
|
||||
|
||||
// Create a role which allows contentbank:access and one that prohibits it, and one neither.
|
||||
$allowroleid = $generator->create_role();
|
||||
$prohibitroleid = $generator->create_role();
|
||||
$emptyroleid = $generator->create_role();
|
||||
$systemcontext = context_system::instance();
|
||||
assign_capability($cap, CAP_ALLOW, $allowroleid, $systemcontext->id);
|
||||
assign_capability($cap, CAP_PROHIBIT, $prohibitroleid, $systemcontext->id);
|
||||
|
||||
// Create three categories (two of them nested).
|
||||
$cat1 = $generator->create_category();
|
||||
$cat2 = $generator->create_category();
|
||||
$cat3 = $generator->create_category(['parent' => $cat1->id]);
|
||||
|
||||
// Category overrides: in cat 1, empty role is allowed; in cat 2, empty role is prevented.
|
||||
assign_capability($cap, CAP_ALLOW, $emptyroleid,
|
||||
context_coursecat::instance($cat1->id)->id);
|
||||
assign_capability($cap, CAP_PREVENT, $emptyroleid,
|
||||
context_coursecat::instance($cat2->id)->id);
|
||||
|
||||
// Course category overrides: in cat1, allow role is prevented and prohibit role is allowed;
|
||||
// in Cat2, allow role is prohibited.
|
||||
assign_capability($cap, CAP_PREVENT, $allowroleid,
|
||||
context_coursecat::instance($cat1->id)->id);
|
||||
assign_capability($cap, CAP_ALLOW, $prohibitroleid,
|
||||
context_coursecat::instance($cat1->id)->id);
|
||||
assign_capability($cap, CAP_PROHIBIT, $allowroleid,
|
||||
context_coursecat::instance($cat2->id)->id);
|
||||
|
||||
// User 1 has no roles except default user role.
|
||||
$u1 = $generator->create_user();
|
||||
|
||||
// It returns false (annoyingly) if there are no course categories.
|
||||
list($categories, $courses) = get_user_capability_contexts($cap, true, $u1->id, true, '', '', '', 'id');
|
||||
$this->assertFalse($categories);
|
||||
|
||||
// User 2 has allow role (system wide).
|
||||
$u2 = $generator->create_user();
|
||||
role_assign($allowroleid, $u2->id, $systemcontext->id);
|
||||
|
||||
// Should get $defaultcategory only. cat2 is prohibited; cat1 is prevented, so cat3 is not allowed.
|
||||
list($categories, $courses) = get_user_capability_contexts($cap, true, $u2->id, true, '', '', '', 'id');
|
||||
// Using same assert_course_ids helper even when we are checking course category ids.
|
||||
$this->assert_course_ids([$defaultcategoryid], $categories);
|
||||
|
||||
// User 3 has empty role (system wide).
|
||||
$u3 = $generator->create_user();
|
||||
role_assign($emptyroleid, $u3->id, $systemcontext->id);
|
||||
|
||||
// Should get cat1 and cat3. cat2 is prohibited; no access to system level.
|
||||
list($categories, $courses) = get_user_capability_contexts($cap, true, $u3->id, true, '', '', '', 'id');
|
||||
$this->assert_course_ids([$cat1->id, $cat3->id], $categories);
|
||||
|
||||
// User 4 has prohibit role (system wide).
|
||||
$u4 = $generator->create_user();
|
||||
role_assign($prohibitroleid, $u4->id, $systemcontext->id);
|
||||
|
||||
// Should not get any, because all of them are prohibited at system level.
|
||||
// Even if we try to allow an specific category.
|
||||
list($categories, $courses) = get_user_capability_contexts($cap, true, $u4->id, true, '', '', '', 'id');
|
||||
$this->assertFalse($categories);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts an array of course ids to make the above test script shorter.
|
||||
*
|
||||
|
@ -29,7 +29,7 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$version = 2021101300.00; // YYYYMMDD = weekly release date of this DEV branch.
|
||||
$version = 2021101300.01; // YYYYMMDD = weekly release date of this DEV branch.
|
||||
// RR = release increments - 00 in DEV branches.
|
||||
// .XX = incremental changes.
|
||||
$release = '4.0dev+ (Build: 20211013)'; // Human-friendly version name
|
||||
|
Loading…
x
Reference in New Issue
Block a user