Merge branch 'MDL-10965-master' of git://github.com/marinaglancy/moodle
This commit is contained in:
@@ -234,10 +234,6 @@ class data_registry_page implements renderable, templatable {
|
||||
|
||||
$coursecontext = \context_course::instance($course->id);
|
||||
|
||||
if (!$course->visible && !has_capability('moodle/course:viewhiddencourses', $coursecontext)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$coursenode = [
|
||||
'text' => shorten_text(format_string($course->shortname, true, ['context' => $coursecontext])),
|
||||
'contextid' => $coursecontext->id,
|
||||
|
||||
@@ -47,8 +47,7 @@ class tool_generator_make_testplan_form extends moodleform {
|
||||
tool_generator_testplan_backend::get_size_choices());
|
||||
$mform->setDefault('size', tool_generator_testplan_backend::DEFAULT_SIZE);
|
||||
|
||||
$mform->addElement('select', 'courseid', get_string('targetcourse', 'tool_generator'),
|
||||
tool_generator_testplan_backend::get_course_options());
|
||||
$mform->addElement('course', 'courseid', get_string('targetcourse', 'tool_generator'));
|
||||
|
||||
$mform->addElement('advcheckbox', 'updateuserspassword', get_string('updateuserspassword', 'tool_generator'));
|
||||
$mform->addHelpButton('updateuserspassword', 'updateuserspassword', 'tool_generator');
|
||||
|
||||
@@ -71,25 +71,6 @@ class tool_generator_testplan_backend extends tool_generator_backend {
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of courses that can be used used to generate a test.
|
||||
*
|
||||
* @return array The list of options as courseid => name
|
||||
*/
|
||||
public static function get_course_options() {
|
||||
$courses = get_courses('all', 'c.sortorder ASC', 'c.id, c.shortname, c.fullname');
|
||||
if (!$courses) {
|
||||
print_error('error_nocourses', 'tool_generator');
|
||||
}
|
||||
|
||||
$options = array();
|
||||
unset($courses[1]);
|
||||
foreach ($courses as $course) {
|
||||
$options[$course->id] = $course->fullname . '(' . $course->shortname . ')';
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for moodle-performance-comparison project URL.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
This files describes API changes in core libraries and APIs,
|
||||
information provided here is intended especially for developers.
|
||||
|
||||
=== 3.7 ===
|
||||
|
||||
* Function tool_generator_testplan_backend::get_course_options() is removed, the 'course' form element is used instead.
|
||||
@@ -78,22 +78,10 @@ class award_criteria_courseset extends award_criteria {
|
||||
require_once($CFG->dirroot . '/course/lib.php');
|
||||
$buttonarray = array();
|
||||
|
||||
// Get courses with enabled completion.
|
||||
$courses = $DB->get_records('course', array('enablecompletion' => COMPLETION_ENABLED));
|
||||
if (!empty($courses)) {
|
||||
$list = core_course_category::make_categories_list();
|
||||
|
||||
$select = array();
|
||||
$selected = array();
|
||||
foreach ($courses as $c) {
|
||||
$select[$c->id] = $list[$c->category] . ' / ' . format_string($c->fullname, true, array('context' => context_course::instance($c->id)));
|
||||
}
|
||||
|
||||
if ($this->id !== 0) {
|
||||
$selected = array_keys($this->params);
|
||||
}
|
||||
$settings = array('multiple' => 'multiple', 'size' => 20, 'style' => 'width:300px');
|
||||
$mform->addElement('select', 'courses', get_string('addcourse', 'badges'), $select, $settings);
|
||||
$hasselectablecourses = core_course_category::search_courses(['onlywithcompletion' => true], ['limit' => 1]);
|
||||
if ($hasselectablecourses) {
|
||||
$settings = array('multiple' => 'multiple', 'onlywithcompletion' => 1);
|
||||
$mform->addElement('course', 'courses', get_string('addcourse', 'badges'), $settings);
|
||||
$mform->addRule('courses', get_string('requiredcourse', 'badges'), 'required');
|
||||
$mform->addHelpButton('courses', 'addcourse', 'badges');
|
||||
|
||||
@@ -104,6 +92,7 @@ class award_criteria_courseset extends award_criteria {
|
||||
$mform->addElement('hidden', 'addcourse', 'addcourse');
|
||||
$mform->setType('addcourse', PARAM_TEXT);
|
||||
if ($this->id !== 0) {
|
||||
$selected = array_keys($this->params);
|
||||
$mform->setDefault('courses', $selected);
|
||||
}
|
||||
$mform->setType('agg', PARAM_INT);
|
||||
|
||||
@@ -54,6 +54,11 @@ class block_course_list extends block_list {
|
||||
}
|
||||
}
|
||||
|
||||
$allcourselink =
|
||||
(has_capability('moodle/course:update', context_system::instance())
|
||||
|| empty($CFG->block_course_list_hideallcourseslink)) &&
|
||||
core_course_category::user_top();
|
||||
|
||||
if (empty($CFG->disablemycourses) and isloggedin() and !isguestuser() and
|
||||
!(has_capability('moodle/course:update', context_system::instance()) and $adminseesall)) { // Just print My Courses
|
||||
if ($courses = enrol_get_my_courses()) {
|
||||
@@ -65,7 +70,7 @@ class block_course_list extends block_list {
|
||||
}
|
||||
$this->title = get_string('mycourses');
|
||||
/// If we can update any course of the view all isn't hidden, show the view all courses link
|
||||
if (has_capability('moodle/course:update', context_system::instance()) || empty($CFG->block_course_list_hideallcourseslink)) {
|
||||
if ($allcourselink) {
|
||||
$this->content->footer = "<a href=\"$CFG->wwwroot/course/index.php\">".get_string("fulllistofcourses")."</a> ...";
|
||||
}
|
||||
}
|
||||
@@ -75,8 +80,9 @@ class block_course_list extends block_list {
|
||||
}
|
||||
}
|
||||
|
||||
$categories = core_course_category::get(0)->get_children(); // Parent = 0 ie top-level categories only
|
||||
if ($categories) { //Check we have categories
|
||||
// User is not enrolled in any courses, show list of available categories or courses (if there is only one category).
|
||||
$topcategory = core_course_category::top();
|
||||
if ($topcategory->is_uservisible() && ($categories = $topcategory->get_children())) { // Check we have categories.
|
||||
if (count($categories) > 1 || (count($categories) == 1 && $DB->count_records('course') > 200)) { // Just print top level category links
|
||||
foreach ($categories as $category) {
|
||||
$categoryname = $category->get_formatted_name();
|
||||
@@ -84,13 +90,13 @@ class block_course_list extends block_list {
|
||||
$this->content->items[]="<a $linkcss href=\"$CFG->wwwroot/course/index.php?categoryid=$category->id\">".$icon . $categoryname . "</a>";
|
||||
}
|
||||
/// If we can update any course of the view all isn't hidden, show the view all courses link
|
||||
if (has_capability('moodle/course:update', context_system::instance()) || empty($CFG->block_course_list_hideallcourseslink)) {
|
||||
if ($allcourselink) {
|
||||
$this->content->footer .= "<a href=\"$CFG->wwwroot/course/index.php\">".get_string('fulllistofcourses').'</a> ...';
|
||||
}
|
||||
$this->title = get_string('categories');
|
||||
} else { // Just print course names of single category
|
||||
$category = array_shift($categories);
|
||||
$courses = get_courses($category->id);
|
||||
$courses = $category->get_courses();
|
||||
|
||||
if ($courses) {
|
||||
foreach ($courses as $course) {
|
||||
@@ -98,12 +104,12 @@ class block_course_list extends block_list {
|
||||
$linkcss = $course->visible ? "" : " class=\"dimmed\" ";
|
||||
|
||||
$this->content->items[]="<a $linkcss title=\""
|
||||
. format_string($course->shortname, true, array('context' => $coursecontext))."\" ".
|
||||
. s($course->get_formatted_shortname())."\" ".
|
||||
"href=\"$CFG->wwwroot/course/view.php?id=$course->id\">"
|
||||
.$icon. format_string(get_course_display_name_for_list($course), true, array('context' => context_course::instance($course->id))) . "</a>";
|
||||
.$icon. $course->get_formatted_name() . "</a>";
|
||||
}
|
||||
/// If we can update any course of the view all isn't hidden, show the view all courses link
|
||||
if (has_capability('moodle/course:update', context_system::instance()) || empty($CFG->block_course_list_hideallcourseslink)) {
|
||||
if ($allcourselink) {
|
||||
$this->content->footer .= "<a href=\"$CFG->wwwroot/course/index.php\">".get_string('fulllistofcourses').'</a> ...';
|
||||
}
|
||||
$this->get_remote_courses();
|
||||
|
||||
+2
-3
@@ -48,9 +48,8 @@ function block_html_pluginfile($course, $birecord_or_cm, $context, $filearea, $a
|
||||
$parentcontext = $context->get_parent_context();
|
||||
if ($parentcontext->contextlevel === CONTEXT_COURSECAT) {
|
||||
// Check if category is visible and user can view this category.
|
||||
$category = $DB->get_record('course_categories', array('id' => $parentcontext->instanceid), '*', MUST_EXIST);
|
||||
if (!$category->visible) {
|
||||
require_capability('moodle/category:viewhiddencategories', $parentcontext);
|
||||
if (!core_course_category::get($parentcontext->instanceid, IGNORE_MISSING)) {
|
||||
send_file_not_found();
|
||||
}
|
||||
} else if ($parentcontext->contextlevel === CONTEXT_USER && $parentcontext->instanceid != $USER->id) {
|
||||
// The block is in the context of a user, it is only visible to the user who it belongs to.
|
||||
|
||||
+4
-1
@@ -2352,8 +2352,11 @@ function calendar_get_default_courses($courseid = null, $fields = '*', $canmanag
|
||||
$fieldlist = explode(',', $fields);
|
||||
|
||||
$prefixedfields = array_map(function($value) {
|
||||
return 'c.' . trim($value);
|
||||
return 'c.' . trim(strtolower($value));
|
||||
}, $fieldlist);
|
||||
if (!in_array('c.visible', $prefixedfields) && !in_array('c.*', $prefixedfields)) {
|
||||
$prefixedfields[] = 'c.visible';
|
||||
}
|
||||
$courses = get_courses('all', 'c.shortname', implode(',', $prefixedfields));
|
||||
} else {
|
||||
$courses = enrol_get_users_courses($userid, true, $fields);
|
||||
|
||||
@@ -83,6 +83,7 @@ $course = get_course($courseid);
|
||||
if ($courseid != SITEID && !empty($courseid)) {
|
||||
navigation_node::override_active_url(new moodle_url('/course/view.php', array('id' => $course->id)));
|
||||
} else if (!empty($categoryid)) {
|
||||
core_course_category::get($categoryid); // Check that category exists and can be accessed.
|
||||
$PAGE->set_category_by_id($categoryid);
|
||||
navigation_node::override_active_url(new moodle_url('/course/index.php', array('categoryid' => $categoryid)));
|
||||
} else {
|
||||
|
||||
+201
-85
@@ -113,10 +113,7 @@ class core_course_category implements renderable, cacheable_object, IteratorAggr
|
||||
protected $theme = false;
|
||||
|
||||
/** @var bool */
|
||||
protected $fromcache;
|
||||
|
||||
/** @var bool */
|
||||
protected $hasmanagecapability = null;
|
||||
protected $fromcache = false;
|
||||
|
||||
/**
|
||||
* Magic setter method, we do not want anybody to modify properties from the outside
|
||||
@@ -232,17 +229,17 @@ class core_course_category implements renderable, cacheable_object, IteratorAggr
|
||||
*/
|
||||
public static function get($id, $strictness = MUST_EXIST, $alwaysreturnhidden = false, $user = null) {
|
||||
if (!$id) {
|
||||
if (!isset(self::$coursecat0)) {
|
||||
$record = new stdClass();
|
||||
$record->id = 0;
|
||||
$record->visible = 1;
|
||||
$record->depth = 0;
|
||||
$record->path = '';
|
||||
$record->locked = 0;
|
||||
self::$coursecat0 = new self($record);
|
||||
// Top-level category.
|
||||
if ($alwaysreturnhidden || self::top()->is_uservisible()) {
|
||||
return self::top();
|
||||
}
|
||||
return self::$coursecat0;
|
||||
if ($strictness == MUST_EXIST) {
|
||||
throw new moodle_exception('cannotviewcategory');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Try to get category from cache or retrieve from the DB.
|
||||
$coursecatrecordcache = cache::make('core', 'coursecatrecords');
|
||||
$coursecat = $coursecatrecordcache->get($id);
|
||||
if ($coursecat === false) {
|
||||
@@ -253,14 +250,66 @@ class core_course_category implements renderable, cacheable_object, IteratorAggr
|
||||
$coursecatrecordcache->set($id, $coursecat);
|
||||
}
|
||||
}
|
||||
if ($coursecat && ($alwaysreturnhidden || $coursecat->is_uservisible($user))) {
|
||||
return $coursecat;
|
||||
} else {
|
||||
|
||||
if (!$coursecat) {
|
||||
// Course category not found.
|
||||
if ($strictness == MUST_EXIST) {
|
||||
throw new moodle_exception('unknowncategory');
|
||||
}
|
||||
$coursecat = null;
|
||||
} else if (!$alwaysreturnhidden && !$coursecat->is_uservisible($user)) {
|
||||
// Course category is found but user can not access it.
|
||||
if ($strictness == MUST_EXIST) {
|
||||
throw new moodle_exception('cannotviewcategory');
|
||||
}
|
||||
$coursecat = null;
|
||||
}
|
||||
return null;
|
||||
return $coursecat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the pseudo-category representing the whole system (id=0, context_system)
|
||||
*
|
||||
* @return core_course_category
|
||||
*/
|
||||
public static function top() {
|
||||
if (!isset(self::$coursecat0)) {
|
||||
$record = new stdClass();
|
||||
$record->id = 0;
|
||||
$record->visible = 1;
|
||||
$record->depth = 0;
|
||||
$record->path = '';
|
||||
$record->locked = 0;
|
||||
self::$coursecat0 = new self($record);
|
||||
}
|
||||
return self::$coursecat0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the top-most category for the current user
|
||||
*
|
||||
* Examples:
|
||||
* 1. User can browse courses everywhere - return self::top() - pseudo-category with id=0
|
||||
* 2. User does not have capability to browse courses on the system level but
|
||||
* has it in ONE course category - return this course category
|
||||
* 3. User has capability to browse courses in two course categories - return self::top()
|
||||
*
|
||||
* @return core_course_category|null
|
||||
*/
|
||||
public static function user_top() {
|
||||
$children = self::top()->get_children();
|
||||
if (count($children) == 1) {
|
||||
// User has access to only one category on the top level. Return this category as "user top category".
|
||||
return reset($children);
|
||||
}
|
||||
if (count($children) > 1) {
|
||||
// User has access to more than one category on the top level. Return the top as "user top category".
|
||||
// In this case user actually may not have capability 'moodle/course:browse' on the top level.
|
||||
return self::top();
|
||||
}
|
||||
// User can not access any categories on the top level.
|
||||
// TODO MDL-10965 find ANY/ALL categories in the tree where user has access to.
|
||||
return self::get(0, IGNORE_MISSING);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -343,7 +392,7 @@ class core_course_category implements renderable, cacheable_object, IteratorAggr
|
||||
* @return core_course_category
|
||||
*/
|
||||
public static function get_default() {
|
||||
if ($visiblechildren = self::get(0)->get_children()) {
|
||||
if ($visiblechildren = self::top()->get_children()) {
|
||||
$defcategory = reset($visiblechildren);
|
||||
} else {
|
||||
$toplevelcategories = self::get_tree(0);
|
||||
@@ -358,6 +407,9 @@ class core_course_category implements renderable, cacheable_object, IteratorAggr
|
||||
* during {@link fix_course_sortorder()}
|
||||
*/
|
||||
protected function restore() {
|
||||
if (!$this->id) {
|
||||
return;
|
||||
}
|
||||
// Update all fields in the current object.
|
||||
$newrecord = self::get($this->id, MUST_EXIST, true);
|
||||
foreach (self::$coursecatfields as $key => $unused) {
|
||||
@@ -419,7 +471,7 @@ class core_course_category implements renderable, cacheable_object, IteratorAggr
|
||||
}
|
||||
|
||||
if (empty($data->parent)) {
|
||||
$parent = self::get(0);
|
||||
$parent = self::top();
|
||||
} else {
|
||||
$parent = self::get($data->parent, MUST_EXIST, true);
|
||||
}
|
||||
@@ -589,8 +641,48 @@ class core_course_category implements renderable, cacheable_object, IteratorAggr
|
||||
* @return bool
|
||||
*/
|
||||
public function is_uservisible($user = null) {
|
||||
return !$this->id || $this->visible ||
|
||||
has_capability('moodle/category:viewhiddencategories', $this->get_context(), $user);
|
||||
return self::can_view_category($this, $user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if current user has access to the category
|
||||
*
|
||||
* @param stdClass|core_course_category $category
|
||||
* @param int|stdClass $user The user id or object. By default (null) checks access for the current user.
|
||||
* @return bool
|
||||
*/
|
||||
public static function can_view_category($category, $user = null) {
|
||||
if (!$category->id) {
|
||||
return has_capability('moodle/course:browse', context_system::instance(), $user);
|
||||
}
|
||||
$context = context_coursecat::instance($category->id);
|
||||
if (!$category->visible && !has_capability('moodle/category:viewhiddencategories', $context, $user)) {
|
||||
return false;
|
||||
}
|
||||
return has_capability('moodle/course:browse', $context, $user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if current user can view course information or enrolment page.
|
||||
*
|
||||
* This method does not check if user is already enrolled in the course
|
||||
*
|
||||
* @param stdClass $course course object (must have 'id', 'visible' and 'category' fields)
|
||||
* @param null|stdClass $user The user id or object. By default (null) checks access for the current user.
|
||||
*/
|
||||
public static function can_view_course_info($course, $user = null) {
|
||||
if ($course->id == SITEID) {
|
||||
return true;
|
||||
}
|
||||
if (!$course->visible) {
|
||||
$coursecontext = context_course::instance($course->id);
|
||||
if (!has_capability('moodle/course:viewhiddencourses', $coursecontext, $user)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
$categorycontext = isset($course->category) ? context_coursecat::instance($course->category) :
|
||||
context_course::instance($course->id)->get_parent_context();
|
||||
return has_capability('moodle/course:browse', $categorycontext, $user);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -680,12 +772,29 @@ class core_course_category implements renderable, cacheable_object, IteratorAggr
|
||||
* Returns number of ALL categories in the system regardless if
|
||||
* they are visible to current user or not
|
||||
*
|
||||
* @deprecated since Moodle 3.7
|
||||
* @return int
|
||||
*/
|
||||
public static function count_all() {
|
||||
debugging('Method core_course_category::count_all() is deprecated. Please use ' .
|
||||
'core_course_category::is_simple_site()', DEBUG_DEVELOPER);
|
||||
return self::get_tree('countall');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the site has only one category and it is visible and available.
|
||||
*
|
||||
* In many situations we won't show this category at all
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_simple_site() {
|
||||
if (self::get_tree('countall') != 1) {
|
||||
return false;
|
||||
}
|
||||
$default = self::get_default();
|
||||
return $default->visible && $default->is_uservisible();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves number of records from course_categories table
|
||||
*
|
||||
@@ -977,9 +1086,9 @@ class core_course_category implements renderable, cacheable_object, IteratorAggr
|
||||
*
|
||||
* @param string $whereclause
|
||||
* @param array $params
|
||||
* @param array $options may indicate that summary and/or coursecontacts need to be retrieved
|
||||
* @param array $options may indicate that summary needs to be retrieved
|
||||
* @param bool $checkvisibility if true, capability 'moodle/course:viewhiddencourses' will be checked
|
||||
* on not visible courses
|
||||
* on not visible courses and 'moodle/course:browse' on all courses
|
||||
* @return array array of stdClass objects
|
||||
*/
|
||||
protected static function get_course_records($whereclause, $params, $options, $checkvisibility = false) {
|
||||
@@ -1002,29 +1111,21 @@ class core_course_category implements renderable, cacheable_object, IteratorAggr
|
||||
array('contextcourse' => CONTEXT_COURSE) + $params);
|
||||
|
||||
if ($checkvisibility) {
|
||||
$mycourses = enrol_get_my_courses();
|
||||
// Loop through all records and make sure we only return the courses accessible by user.
|
||||
foreach ($list as $course) {
|
||||
if (isset($list[$course->id]->hassummary)) {
|
||||
$list[$course->id]->hassummary = strlen($list[$course->id]->hassummary) > 0;
|
||||
}
|
||||
if (empty($course->visible)) {
|
||||
// Load context only if we need to check capability.
|
||||
context_helper::preload_from_record($course);
|
||||
if (!has_capability('moodle/course:viewhiddencourses', context_course::instance($course->id))) {
|
||||
unset($list[$course->id]);
|
||||
}
|
||||
context_helper::preload_from_record($course);
|
||||
$context = context_course::instance($course->id);
|
||||
// Check that course is accessible by user.
|
||||
if (!array_key_exists($course->id, $mycourses) && !self::can_view_course_info($course)) {
|
||||
unset($list[$course->id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Preload course contacts if necessary.
|
||||
if (!empty($options['coursecontacts'])) {
|
||||
self::preload_course_contacts($list);
|
||||
}
|
||||
// Preload custom fields if necessary - saves DB queries later to do it for each course separately.
|
||||
if (!empty($options['customfields'])) {
|
||||
self::preload_custom_fields($list);
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
@@ -1041,10 +1142,11 @@ class core_course_category implements renderable, cacheable_object, IteratorAggr
|
||||
if (($invisibleids = $coursecatcache->get('ic'. $this->id)) === false) {
|
||||
// We never checked visible children before.
|
||||
$hidden = self::get_tree($this->id.'i');
|
||||
$catids = self::get_tree($this->id);
|
||||
$invisibleids = array();
|
||||
if ($hidden) {
|
||||
if ($catids) {
|
||||
// Preload categories contexts.
|
||||
list($sql, $params) = $DB->get_in_or_equal($hidden, SQL_PARAMS_NAMED, 'id');
|
||||
list($sql, $params) = $DB->get_in_or_equal($catids, SQL_PARAMS_NAMED, 'id');
|
||||
$ctxselect = context_helper::get_preload_record_columns_sql('ctx');
|
||||
$contexts = $DB->get_records_sql("SELECT $ctxselect FROM {context} ctx
|
||||
WHERE ctx.contextlevel = :contextcoursecat AND ctx.instanceid ".$sql,
|
||||
@@ -1052,9 +1154,10 @@ class core_course_category implements renderable, cacheable_object, IteratorAggr
|
||||
foreach ($contexts as $record) {
|
||||
context_helper::preload_from_record($record);
|
||||
}
|
||||
// Check that user has 'viewhiddencategories' capability for each hidden category.
|
||||
foreach ($hidden as $id) {
|
||||
if (!has_capability('moodle/category:viewhiddencategories', context_coursecat::instance($id))) {
|
||||
// Check access for each category.
|
||||
foreach ($catids as $id) {
|
||||
$cat = (object)['id' => $id, 'visible' => in_array($id, $hidden) ? 0 : 1];
|
||||
if (!self::can_view_category($cat)) {
|
||||
$invisibleids[] = $id;
|
||||
}
|
||||
}
|
||||
@@ -1391,6 +1494,7 @@ class core_course_category implements renderable, cacheable_object, IteratorAggr
|
||||
* - blocklist - id of block (if we are searching for courses containing specific block0
|
||||
* - modulelist - name of module (if we are searching for courses containing specific module
|
||||
* - tagid - id of tag
|
||||
* - onlywithcompletion - set to true if we only need courses with completion enabled
|
||||
* @param array $options display options, same as in get_courses() except 'recursive' is ignored -
|
||||
* search is always category-independent
|
||||
* @param array $requiredcapabilities List of capabilities required to see return course.
|
||||
@@ -1447,8 +1551,13 @@ class core_course_category implements renderable, cacheable_object, IteratorAggr
|
||||
if (empty($search['blocklist']) && empty($search['modulelist']) && empty($search['tagid'])) {
|
||||
// Search courses that have specified words in their names/summaries.
|
||||
$searchterms = preg_split('|\s+|', trim($search['search']), 0, PREG_SPLIT_NO_EMPTY);
|
||||
|
||||
$courselist = get_courses_search($searchterms, 'c.sortorder ASC', 0, 9999999, $totalcount, $requiredcapabilities);
|
||||
$searchcond = $searchcondparams = [];
|
||||
if (!empty($search['onlywithcompletion'])) {
|
||||
$searchcond = ['c.enablecompletion = :p1'];
|
||||
$searchcondparams = ['p1' => 1];
|
||||
}
|
||||
$courselist = get_courses_search($searchterms, 'c.sortorder ASC', 0, 9999999, $totalcount,
|
||||
$requiredcapabilities, $searchcond, $searchcondparams);
|
||||
self::sort_records($courselist, $sortfields);
|
||||
$coursecatcache->set($cachekey, array_keys($courselist));
|
||||
$coursecatcache->set($cntcachekey, $totalcount);
|
||||
@@ -1602,10 +1711,9 @@ class core_course_category implements renderable, cacheable_object, IteratorAggr
|
||||
$limit = !empty($options['limit']) ? $options['limit'] : null;
|
||||
$sortfields = !empty($options['sort']) ? $options['sort'] : array('sortorder' => 1);
|
||||
|
||||
// Check if this category is hidden.
|
||||
// Also 0-category never has courses unless this is recursive call.
|
||||
if (!$this->is_uservisible() || (!$this->id && !$recursive)) {
|
||||
return array();
|
||||
if (!$this->id && !$recursive) {
|
||||
// There are no courses on system level unless we need recursive list.
|
||||
return [];
|
||||
}
|
||||
|
||||
$coursecatcache = cache::make('core', 'coursecat');
|
||||
@@ -2402,7 +2510,9 @@ class core_course_category implements renderable, cacheable_object, IteratorAggr
|
||||
$thislist = preg_split('|,|', $thislist, -1, PREG_SPLIT_NO_EMPTY);
|
||||
}
|
||||
} else if ($baselist !== false) {
|
||||
$thislist = array_keys($baselist);
|
||||
$thislist = array_keys(array_filter($baselist, function($el) {
|
||||
return $el['name'] !== false;
|
||||
}));
|
||||
}
|
||||
|
||||
if ($baselist === false) {
|
||||
@@ -2416,24 +2526,18 @@ class core_course_category implements renderable, cacheable_object, IteratorAggr
|
||||
$baselist = array();
|
||||
$thislist = array();
|
||||
foreach ($rs as $record) {
|
||||
// If the category's parent is not visible to the user, it is not visible as well.
|
||||
if (!$record->parent || isset($baselist[$record->parent])) {
|
||||
context_helper::preload_from_record($record);
|
||||
$context = context_coursecat::instance($record->id);
|
||||
if (!$record->visible && !has_capability('moodle/category:viewhiddencategories', $context)) {
|
||||
// No cap to view category, added to neither $baselist nor $thislist.
|
||||
continue;
|
||||
}
|
||||
$baselist[$record->id] = array(
|
||||
'name' => format_string($record->name, true, array('context' => $context)),
|
||||
'path' => $record->path
|
||||
);
|
||||
if (!empty($requiredcapability) && !has_all_capabilities($requiredcapability, $context)) {
|
||||
// No required capability, added to $baselist but not to $thislist.
|
||||
continue;
|
||||
}
|
||||
$thislist[] = $record->id;
|
||||
context_helper::preload_from_record($record);
|
||||
$context = context_coursecat::instance($record->id);
|
||||
$canview = self::can_view_category($record);
|
||||
$baselist[$record->id] = array(
|
||||
'name' => $canview ? format_string($record->name, true, array('context' => $context)) : false,
|
||||
'path' => $record->path
|
||||
);
|
||||
if (!$canview || (!empty($requiredcapability) && !has_all_capabilities($requiredcapability, $context))) {
|
||||
// No required capability, added to $baselist but not to $thislist.
|
||||
continue;
|
||||
}
|
||||
$thislist[] = $record->id;
|
||||
}
|
||||
$rs->close();
|
||||
$coursecatcache->set($basecachekey, $baselist);
|
||||
@@ -2448,9 +2552,11 @@ class core_course_category implements renderable, cacheable_object, IteratorAggr
|
||||
$contexts = $DB->get_records_sql($sql, array('contextcoursecat' => CONTEXT_COURSECAT));
|
||||
$thislist = array();
|
||||
foreach (array_keys($baselist) as $id) {
|
||||
context_helper::preload_from_record($contexts[$id]);
|
||||
if (has_all_capabilities($requiredcapability, context_coursecat::instance($id))) {
|
||||
$thislist[] = $id;
|
||||
if ($baselist[$id]['name'] !== false) {
|
||||
context_helper::preload_from_record($contexts[$id]);
|
||||
if (has_all_capabilities($requiredcapability, context_coursecat::instance($id))) {
|
||||
$thislist[] = $id;
|
||||
}
|
||||
}
|
||||
}
|
||||
$coursecatcache->set($thiscachekey, join(',', $thislist));
|
||||
@@ -2463,7 +2569,9 @@ class core_course_category implements renderable, cacheable_object, IteratorAggr
|
||||
if (!$excludeid || !in_array($excludeid, $path)) {
|
||||
$namechunks = array();
|
||||
foreach ($path as $parentid) {
|
||||
$namechunks[] = $baselist[$parentid]['name'];
|
||||
if (array_key_exists($parentid, $baselist) && $baselist[$parentid]['name'] !== false) {
|
||||
$namechunks[] = $baselist[$parentid]['name'];
|
||||
}
|
||||
}
|
||||
$names[$id] = join($separator, $namechunks);
|
||||
}
|
||||
@@ -2529,7 +2637,7 @@ class core_course_category implements renderable, cacheable_object, IteratorAggr
|
||||
* @return bool
|
||||
*/
|
||||
public static function can_create_top_level_category() {
|
||||
return has_capability('moodle/category:manage', context_system::instance());
|
||||
return self::top()->has_manage_capability();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2550,10 +2658,10 @@ class core_course_category implements renderable, cacheable_object, IteratorAggr
|
||||
* @return bool
|
||||
*/
|
||||
public function has_manage_capability() {
|
||||
if ($this->hasmanagecapability === null) {
|
||||
$this->hasmanagecapability = has_capability('moodle/category:manage', $this->get_context());
|
||||
if (!$this->is_uservisible()) {
|
||||
return false;
|
||||
}
|
||||
return $this->hasmanagecapability;
|
||||
return has_capability('moodle/category:manage', $this->get_context());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2561,7 +2669,7 @@ class core_course_category implements renderable, cacheable_object, IteratorAggr
|
||||
* @return bool
|
||||
*/
|
||||
public function parent_has_manage_capability() {
|
||||
return has_capability('moodle/category:manage', get_category_or_system_context($this->parent));
|
||||
return ($parent = $this->get_parent_coursecat()) && $parent->has_manage_capability();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2595,7 +2703,7 @@ class core_course_category implements renderable, cacheable_object, IteratorAggr
|
||||
* @return bool
|
||||
*/
|
||||
public function can_change_sortorder() {
|
||||
return $this->id && $this->get_parent_coursecat()->can_resort_subcategories();
|
||||
return ($parent = $this->get_parent_coursecat()) && $parent->can_resort_subcategories();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2603,7 +2711,7 @@ class core_course_category implements renderable, cacheable_object, IteratorAggr
|
||||
* @return bool
|
||||
*/
|
||||
public function can_create_course() {
|
||||
return has_capability('moodle/course:create', $this->get_context());
|
||||
return $this->is_uservisible() && has_capability('moodle/course:create', $this->get_context());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2619,7 +2727,7 @@ class core_course_category implements renderable, cacheable_object, IteratorAggr
|
||||
* @return bool
|
||||
*/
|
||||
public function can_review_roles() {
|
||||
return has_capability('moodle/role:assign', $this->get_context());
|
||||
return $this->is_uservisible() && has_capability('moodle/role:assign', $this->get_context());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2627,7 +2735,8 @@ class core_course_category implements renderable, cacheable_object, IteratorAggr
|
||||
* @return bool
|
||||
*/
|
||||
public function can_review_permissions() {
|
||||
return has_any_capability(array(
|
||||
return $this->is_uservisible() &&
|
||||
has_any_capability(array(
|
||||
'moodle/role:assign',
|
||||
'moodle/role:safeoverride',
|
||||
'moodle/role:override',
|
||||
@@ -2640,7 +2749,8 @@ class core_course_category implements renderable, cacheable_object, IteratorAggr
|
||||
* @return bool
|
||||
*/
|
||||
public function can_review_cohorts() {
|
||||
return has_any_capability(array('moodle/cohort:view', 'moodle/cohort:manage'), $this->get_context());
|
||||
return $this->is_uservisible() &&
|
||||
has_any_capability(array('moodle/cohort:view', 'moodle/cohort:manage'), $this->get_context());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2648,8 +2758,9 @@ class core_course_category implements renderable, cacheable_object, IteratorAggr
|
||||
* @return bool
|
||||
*/
|
||||
public function can_review_filters() {
|
||||
return has_capability('moodle/filter:manage', $this->get_context()) &&
|
||||
count(filter_get_available_in_context($this->get_context())) > 0;
|
||||
return $this->is_uservisible() &&
|
||||
has_capability('moodle/filter:manage', $this->get_context()) &&
|
||||
count(filter_get_available_in_context($this->get_context())) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2681,7 +2792,7 @@ class core_course_category implements renderable, cacheable_object, IteratorAggr
|
||||
* @return bool
|
||||
*/
|
||||
public function can_restore_courses_into() {
|
||||
return has_capability('moodle/restore:restorecourse', $this->get_context());
|
||||
return $this->is_uservisible() && has_capability('moodle/restore:restorecourse', $this->get_context());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2845,10 +2956,15 @@ class core_course_category implements renderable, cacheable_object, IteratorAggr
|
||||
/**
|
||||
* Returns the parent core_course_category object for this category.
|
||||
*
|
||||
* @return core_course_category
|
||||
* Only returns parent if it exists and is visible to the current user
|
||||
*
|
||||
* @return core_course_category|null
|
||||
*/
|
||||
public function get_parent_coursecat() {
|
||||
return self::get($this->parent);
|
||||
if (!$this->id) {
|
||||
return null;
|
||||
}
|
||||
return self::get($this->parent, IGNORE_MISSING);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -415,14 +415,6 @@ class core_course_list_element implements IteratorAggregate {
|
||||
return context_course::instance($this->__get('id'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this course is visible to the current user.
|
||||
* @return bool
|
||||
*/
|
||||
public function is_uservisible() {
|
||||
return $this->visible || has_capability('moodle/course:viewhiddencourses', $this->get_context());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the current user can review enrolments for this course.
|
||||
*
|
||||
|
||||
@@ -403,12 +403,10 @@ class helper {
|
||||
$baseurl = new \moodle_url('/course/management.php', $params);
|
||||
$actions = array();
|
||||
// View.
|
||||
if ($course->is_uservisible()) {
|
||||
$actions['view'] = array(
|
||||
'url' => new \moodle_url('/course/view.php', array('id' => $course->id)),
|
||||
'string' => \get_string('view')
|
||||
);
|
||||
}
|
||||
$actions['view'] = array(
|
||||
'url' => new \moodle_url('/course/view.php', array('id' => $course->id)),
|
||||
'string' => \get_string('view')
|
||||
);
|
||||
// Edit.
|
||||
if ($course->can_edit()) {
|
||||
$actions['edit'] = array(
|
||||
@@ -802,13 +800,14 @@ class helper {
|
||||
$searchcriteria = array('modulelist' => $modulelist);
|
||||
}
|
||||
|
||||
$courses = \core_course_category::get(0)->search_courses($searchcriteria, array(
|
||||
$topcat = \core_course_category::top();
|
||||
$courses = $topcat->search_courses($searchcriteria, array(
|
||||
'recursive' => true,
|
||||
'offset' => $page * $perpage,
|
||||
'limit' => $perpage,
|
||||
'sort' => array('fullname' => 1)
|
||||
));
|
||||
$totalcount = \core_course_category::get(0)->search_courses_count($searchcriteria, array('recursive' => true));
|
||||
$totalcount = $topcat->search_courses_count($searchcriteria, array('recursive' => true));
|
||||
|
||||
return array($courses, \count($courses), $totalcount);
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ class core_course_management_renderer extends plugin_renderer_base {
|
||||
$catatlevel[] = array_shift($selectedparents);
|
||||
$catatlevel = array_unique($catatlevel);
|
||||
|
||||
$listing = core_course_category::get(0)->get_children();
|
||||
$listing = core_course_category::top()->get_children();
|
||||
|
||||
$attributes = array(
|
||||
'class' => 'ml-1 list-unstyled',
|
||||
@@ -200,10 +200,10 @@ class core_course_management_renderer extends plugin_renderer_base {
|
||||
'aria-expanded' => $isexpanded ? 'true' : 'false'
|
||||
);
|
||||
$text = $category->get_formatted_name();
|
||||
if ($category->parent) {
|
||||
if (($parent = $category->get_parent_coursecat()) && $parent->id) {
|
||||
$a = new stdClass;
|
||||
$a->category = $text;
|
||||
$a->parentcategory = $category->get_parent_coursecat()->get_formatted_name();
|
||||
$a->parentcategory = $parent->get_formatted_name();
|
||||
$textlabel = get_string('categorysubcategoryof', 'moodle', $a);
|
||||
}
|
||||
$courseicon = $this->output->pix_icon('i/course', get_string('courses'));
|
||||
@@ -327,7 +327,7 @@ class core_course_management_renderer extends plugin_renderer_base {
|
||||
$cancreatecategory = $category && $category->can_create_subcategory();
|
||||
$cancreatecategory = $cancreatecategory || core_course_category::can_create_top_level_category();
|
||||
if ($category === null) {
|
||||
$category = core_course_category::get(0);
|
||||
$category = core_course_category::top();
|
||||
}
|
||||
|
||||
if ($cancreatecategory) {
|
||||
@@ -459,8 +459,8 @@ class core_course_management_renderer extends plugin_renderer_base {
|
||||
}
|
||||
if (core_course_category::can_change_parent_any()) {
|
||||
$options = array();
|
||||
if (has_capability('moodle/category:manage', context_system::instance())) {
|
||||
$options[0] = core_course_category::get(0)->get_formatted_name();
|
||||
if (core_course_category::top()->has_manage_capability()) {
|
||||
$options[0] = core_course_category::top()->get_formatted_name();
|
||||
}
|
||||
$options += core_course_category::make_categories_list('moodle/category:manage');
|
||||
$select = html_writer::select(
|
||||
|
||||
@@ -113,9 +113,7 @@ class course extends \core_search\base {
|
||||
return \core_search\manager::ACCESS_DELETED;
|
||||
}
|
||||
|
||||
$coursecontext = \context_course::instance($course->id);
|
||||
|
||||
if ($course->visible || has_capability('moodle/course:viewhiddencourses', $coursecontext)) {
|
||||
if (\core_course_category::can_view_course_info($course)) {
|
||||
return \core_search\manager::ACCESS_GRANTED;
|
||||
}
|
||||
|
||||
|
||||
@@ -135,8 +135,7 @@ class customfield extends \core_search\base {
|
||||
if (!$course) {
|
||||
return \core_search\manager::ACCESS_DELETED;
|
||||
}
|
||||
$coursecontext = \context_course::instance($course->id);
|
||||
if ($course->visible || has_capability('moodle/course:viewhiddencourses', $coursecontext)) {
|
||||
if (\core_course_category::can_view_course_info($course)) {
|
||||
return \core_search\manager::ACCESS_GRANTED;
|
||||
}
|
||||
return \core_search\manager::ACCESS_DENIED;
|
||||
|
||||
+19
-47
@@ -128,57 +128,29 @@ class course_completion_form extends moodleform {
|
||||
}
|
||||
|
||||
// Get applicable courses (prerequisites).
|
||||
$courses = $DB->get_records_sql("
|
||||
SELECT DISTINCT c.id, c.category, c.fullname, cc.id AS selected
|
||||
FROM {course} c
|
||||
LEFT JOIN {course_completion_criteria} cc ON cc.courseinstance = c.id AND cc.course = {$course->id}
|
||||
INNER JOIN {course_completion_criteria} ccc ON ccc.course = c.id
|
||||
WHERE c.enablecompletion = ".COMPLETION_ENABLED."
|
||||
AND c.id <> {$course->id}");
|
||||
|
||||
if (!empty($courses)) {
|
||||
// Get category list.
|
||||
$list = core_course_category::make_categories_list();
|
||||
|
||||
// Get course list for select box.
|
||||
$selectbox = array();
|
||||
$selected = array();
|
||||
foreach ($courses as $c) {
|
||||
$selectbox[$c->id] = $list[$c->category] . ' / ' . format_string($c->fullname, true,
|
||||
array('context' => context_course::instance($c->id)));
|
||||
|
||||
// If already selected ...
|
||||
if ($c->selected) {
|
||||
$selected[] = $c->id;
|
||||
}
|
||||
}
|
||||
|
||||
$selectedcourses = $DB->get_fieldset_sql("SELECT cc.courseinstance
|
||||
FROM {course_completion_criteria} cc WHERE cc.course = ?", [$course->id]);
|
||||
$hasselectablecourses = core_course_category::search_courses(['onlywithcompletion' => true], ['limit' => 2]);
|
||||
unset($hasselectablecourses[$course->id]);
|
||||
if ($hasselectablecourses) {
|
||||
// Show multiselect box.
|
||||
$mform->addElement('select', 'criteria_course', get_string('coursesavailable', 'completion'), $selectbox,
|
||||
array('multiple' => 'multiple', 'size' => 6));
|
||||
$mform->addElement('course', 'criteria_course', get_string('coursesavailable', 'completion'),
|
||||
array('multiple' => 'multiple', 'onlywithcompletion' => true, 'exclude' => $course->id));
|
||||
$mform->setDefault('criteria_course', $selectedcourses);
|
||||
|
||||
// Select current criteria.
|
||||
$mform->setDefault('criteria_course', $selected);
|
||||
|
||||
// Explain list.
|
||||
$mform->addElement('static', 'criteria_courses_explaination', '', get_string('coursesavailableexplaination', 'completion'));
|
||||
|
||||
if (count($courses) > 1) {
|
||||
// Map aggregation methods to context-sensitive human readable dropdown menu.
|
||||
$courseaggregationmenu = array();
|
||||
foreach ($aggregation_methods as $methodcode => $methodname) {
|
||||
if ($methodcode === COMPLETION_AGGREGATION_ALL) {
|
||||
$courseaggregationmenu[COMPLETION_AGGREGATION_ALL] = get_string('courseaggregation_all', 'core_completion');
|
||||
} else if ($methodcode === COMPLETION_AGGREGATION_ANY) {
|
||||
$courseaggregationmenu[COMPLETION_AGGREGATION_ANY] = get_string('courseaggregation_any', 'core_completion');
|
||||
} else {
|
||||
$courseaggregationmenu[$methodcode] = $methodname;
|
||||
}
|
||||
// Map aggregation methods to context-sensitive human readable dropdown menu.
|
||||
$courseaggregationmenu = array();
|
||||
foreach ($aggregation_methods as $methodcode => $methodname) {
|
||||
if ($methodcode === COMPLETION_AGGREGATION_ALL) {
|
||||
$courseaggregationmenu[COMPLETION_AGGREGATION_ALL] = get_string('courseaggregation_all', 'core_completion');
|
||||
} else if ($methodcode === COMPLETION_AGGREGATION_ANY) {
|
||||
$courseaggregationmenu[COMPLETION_AGGREGATION_ANY] = get_string('courseaggregation_any', 'core_completion');
|
||||
} else {
|
||||
$courseaggregationmenu[$methodcode] = $methodname;
|
||||
}
|
||||
$mform->addElement('select', 'course_aggregation', get_string('courseaggregation', 'core_completion'), $courseaggregationmenu);
|
||||
$mform->setDefault('course_aggregation', $completion->get_aggregation_method(COMPLETION_CRITERIA_TYPE_COURSE));
|
||||
}
|
||||
|
||||
$mform->addElement('select', 'course_aggregation', get_string('courseaggregation', 'core_completion'), $courseaggregationmenu);
|
||||
$mform->setDefault('course_aggregation', $completion->get_aggregation_method(COMPLETION_CRITERIA_TYPE_COURSE));
|
||||
} else {
|
||||
$mform->addElement('static', 'nocourses', '', get_string('err_nocourses', 'completion'));
|
||||
}
|
||||
|
||||
+17
-4
@@ -1870,7 +1870,7 @@ class core_course_external extends external_api {
|
||||
if (!isset($excludedcats[$category->id])) {
|
||||
|
||||
// Final check to see if the category is visible to the user.
|
||||
if ($category->visible or has_capability('moodle/category:viewhiddencategories', $context)) {
|
||||
if (core_course_category::can_view_category($category)) {
|
||||
|
||||
$categoryinfo = array();
|
||||
$categoryinfo['id'] = $category->id;
|
||||
@@ -2373,6 +2373,8 @@ class core_course_external extends external_api {
|
||||
'Optional list of required capabilities (used to filter the list)', VALUE_DEFAULT, array()
|
||||
),
|
||||
'limittoenrolled' => new external_value(PARAM_BOOL, 'limit to enrolled courses', VALUE_DEFAULT, 0),
|
||||
'onlywithcompletion' => new external_value(PARAM_BOOL, 'limit to courses where completion is enabled',
|
||||
VALUE_DEFAULT, 0),
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -2469,6 +2471,7 @@ class core_course_external extends external_api {
|
||||
* @param int $perpage Items per page
|
||||
* @param array $requiredcapabilities Optional list of required capabilities (used to filter the list).
|
||||
* @param int $limittoenrolled Limit to only enrolled courses
|
||||
* @param int onlywithcompletion Limit to only courses where completion is enabled
|
||||
* @return array of course objects and warnings
|
||||
* @since Moodle 3.0
|
||||
* @throws moodle_exception
|
||||
@@ -2478,7 +2481,8 @@ class core_course_external extends external_api {
|
||||
$page=0,
|
||||
$perpage=0,
|
||||
$requiredcapabilities=array(),
|
||||
$limittoenrolled=0) {
|
||||
$limittoenrolled=0,
|
||||
$onlywithcompletion=0) {
|
||||
global $CFG;
|
||||
|
||||
$warnings = array();
|
||||
@@ -2488,7 +2492,9 @@ class core_course_external extends external_api {
|
||||
'criteriavalue' => $criteriavalue,
|
||||
'page' => $page,
|
||||
'perpage' => $perpage,
|
||||
'requiredcapabilities' => $requiredcapabilities
|
||||
'requiredcapabilities' => $requiredcapabilities,
|
||||
'limittoenrolled' => $limittoenrolled,
|
||||
'onlywithcompletion' => $onlywithcompletion
|
||||
);
|
||||
$params = self::validate_parameters(self::search_courses_parameters(), $parameters);
|
||||
self::validate_context(context_system::instance());
|
||||
@@ -2514,6 +2520,9 @@ class core_course_external extends external_api {
|
||||
// Prepare the search API options.
|
||||
$searchcriteria = array();
|
||||
$searchcriteria[$params['criterianame']] = $params['criteriavalue'];
|
||||
if ($params['onlywithcompletion']) {
|
||||
$searchcriteria['onlywithcompletion'] = true;
|
||||
}
|
||||
|
||||
$options = array();
|
||||
if ($params['perpage'] != 0) {
|
||||
@@ -3235,14 +3244,18 @@ class core_course_external extends external_api {
|
||||
}
|
||||
// Get the public course information, even if we are not enrolled.
|
||||
$courseinlist = new core_course_list_element($course);
|
||||
$coursesdata[$course->id] = self::get_course_public_information($courseinlist, $context);
|
||||
|
||||
// Now, check if we have access to the course.
|
||||
try {
|
||||
self::validate_context($context);
|
||||
} catch (Exception $e) {
|
||||
// User can not access the course, check if they can see the public information about the course and return it.
|
||||
if (core_course_category::can_view_course_info($course)) {
|
||||
$coursesdata[$course->id] = self::get_course_public_information($courseinlist, $context);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
$coursesdata[$course->id] = self::get_course_public_information($courseinlist, $context);
|
||||
// Return information for any user that can access the course.
|
||||
$coursefields = array('format', 'showgrades', 'newsitems', 'startdate', 'enddate', 'maxbytes', 'showreports', 'visible',
|
||||
'groupmode', 'groupmodeforce', 'defaultgroupingid', 'enablecompletion', 'completionnotify', 'lang', 'theme',
|
||||
|
||||
+21
-20
@@ -29,39 +29,40 @@ require_once($CFG->dirroot. '/course/lib.php');
|
||||
$categoryid = optional_param('categoryid', 0, PARAM_INT); // Category id
|
||||
$site = get_site();
|
||||
|
||||
if ($CFG->forcelogin) {
|
||||
require_login();
|
||||
}
|
||||
|
||||
$heading = $site->fullname;
|
||||
if ($categoryid) {
|
||||
$category = core_course_category::get($categoryid); // This will validate access.
|
||||
$PAGE->set_category_by_id($categoryid);
|
||||
$PAGE->set_url(new moodle_url('/course/index.php', array('categoryid' => $categoryid)));
|
||||
$PAGE->set_pagetype('course-index-category');
|
||||
// And the object has been loaded for us no need for another DB call
|
||||
$category = $PAGE->category;
|
||||
} else {
|
||||
// Check if there is only one category, if so use that.
|
||||
if (core_course_category::count_all() == 1) {
|
||||
$category = core_course_category::get_default();
|
||||
|
||||
$categoryid = $category->id;
|
||||
$heading = $category->get_formatted_name();
|
||||
} else if ($category = core_course_category::user_top()) {
|
||||
// Check if there is only one top-level category, if so use that.
|
||||
$categoryid = $category->id;
|
||||
$PAGE->set_url('/course/index.php');
|
||||
if ($category->is_uservisible() && $categoryid) {
|
||||
$PAGE->set_category_by_id($categoryid);
|
||||
$PAGE->set_pagetype('course-index-category');
|
||||
$PAGE->set_context($category->get_context());
|
||||
if (!core_course_category::is_simple_site()) {
|
||||
$PAGE->set_url(new moodle_url('/course/index.php', array('categoryid' => $categoryid)));
|
||||
$heading = $category->get_formatted_name();
|
||||
}
|
||||
} else {
|
||||
$PAGE->set_context(context_system::instance());
|
||||
}
|
||||
|
||||
$PAGE->set_url('/course/index.php');
|
||||
$PAGE->set_pagetype('course-index-category');
|
||||
} else {
|
||||
throw new moodle_exception('cannotviewcategory');
|
||||
}
|
||||
|
||||
$PAGE->set_pagelayout('coursecategory');
|
||||
$courserenderer = $PAGE->get_renderer('core', 'course');
|
||||
|
||||
if ($CFG->forcelogin) {
|
||||
require_login();
|
||||
}
|
||||
|
||||
if ($categoryid && !$category->visible && !has_capability('moodle/category:viewhiddencategories', $PAGE->context)) {
|
||||
throw new moodle_exception('unknowncategory');
|
||||
}
|
||||
|
||||
$PAGE->set_heading($site->fullname);
|
||||
$PAGE->set_heading($heading);
|
||||
$content = $courserenderer->course_category($categoryid);
|
||||
|
||||
echo $OUTPUT->header();
|
||||
|
||||
+2
-2
@@ -48,8 +48,8 @@
|
||||
}
|
||||
|
||||
$context = context_course::instance($course->id);
|
||||
if (!$course->visible and !has_capability('moodle/course:viewhiddencourses', $context)) {
|
||||
print_error('coursehidden', '', $CFG->wwwroot .'/');
|
||||
if (!core_course_category::can_view_course_info($course) && !is_enrolled($context, null, '', true)) {
|
||||
print_error('cannotviewcategory', '', $CFG->wwwroot .'/');
|
||||
}
|
||||
|
||||
$PAGE->set_course($course);
|
||||
|
||||
+1
-1
@@ -762,7 +762,7 @@ function print_course_request_buttons($context) {
|
||||
echo $OUTPUT->single_button(new moodle_url('/course/request.php'), get_string('requestcourse'), 'get');
|
||||
}
|
||||
/// Print a button to manage pending requests
|
||||
if ($context->contextlevel == CONTEXT_SYSTEM && has_capability('moodle/site:approvecourse', $context)) {
|
||||
if (has_capability('moodle/site:approvecourse', $context)) {
|
||||
$disabled = !$DB->record_exists('course_request', array());
|
||||
echo $OUTPUT->single_button(new moodle_url('/course/pending.php'), get_string('coursespending'), 'get', array('disabled' => $disabled));
|
||||
}
|
||||
|
||||
@@ -68,8 +68,9 @@ if ($courseid) {
|
||||
} else {
|
||||
$course = null;
|
||||
$courseid = null;
|
||||
$category = core_course_category::get_default();
|
||||
$categoryid = $category->id;
|
||||
$topchildren = core_course_category::top()->get_children();
|
||||
$category = reset($topchildren);
|
||||
$categoryid = $category ? $category->id : 0;
|
||||
$context = context_coursecat::instance($category->id);
|
||||
$url->param('categoryid', $category->id);
|
||||
}
|
||||
@@ -378,9 +379,9 @@ if ($action !== false && confirm_sesskey()) {
|
||||
}
|
||||
$categories = core_course_category::get_many($categoryids);
|
||||
} else if ($for === 'allcategories') {
|
||||
if ($sortcategoriesby && core_course_category::get(0)->can_resort_subcategories()) {
|
||||
if ($sortcategoriesby && core_course_category::top()->can_resort_subcategories()) {
|
||||
\core_course\management\helper::action_category_resort_subcategories(
|
||||
core_course_category::get(0), $sortcategoriesby);
|
||||
core_course_category::top(), $sortcategoriesby);
|
||||
}
|
||||
$categorieslist = core_course_category::make_categories_list('moodle/category:manage');
|
||||
$categoryids = array_keys($categorieslist);
|
||||
|
||||
+60
-47
@@ -1582,35 +1582,38 @@ class core_course_renderer extends plugin_renderer_base {
|
||||
*/
|
||||
public function course_category($category) {
|
||||
global $CFG;
|
||||
$coursecat = core_course_category::get(is_object($category) ? $category->id : $category);
|
||||
$usertop = core_course_category::user_top();
|
||||
if (empty($category)) {
|
||||
$coursecat = $usertop;
|
||||
} else if (is_object($category) && $category instanceof core_course_category) {
|
||||
$coursecat = $category;
|
||||
} else {
|
||||
$coursecat = core_course_category::get(is_object($category) ? $category->id : $category);
|
||||
}
|
||||
$site = get_site();
|
||||
$output = '';
|
||||
|
||||
if (can_edit_in_category($coursecat->id)) {
|
||||
if ($coursecat->can_create_course() || $coursecat->has_manage_capability()) {
|
||||
// Add 'Manage' button if user has permissions to edit this category.
|
||||
$managebutton = $this->single_button(new moodle_url('/course/management.php',
|
||||
array('categoryid' => $coursecat->id)), get_string('managecourses'), 'get');
|
||||
$this->page->set_button($managebutton);
|
||||
}
|
||||
if (!$coursecat->id) {
|
||||
if (core_course_category::count_all() == 1) {
|
||||
// There exists only one category in the system, do not display link to it
|
||||
$coursecat = core_course_category::get_default();
|
||||
$strfulllistofcourses = get_string('fulllistofcourses');
|
||||
$this->page->set_title("$site->shortname: $strfulllistofcourses");
|
||||
} else {
|
||||
$strcategories = get_string('categories');
|
||||
$this->page->set_title("$site->shortname: $strcategories");
|
||||
}
|
||||
|
||||
if (core_course_category::is_simple_site()) {
|
||||
// There is only one category in the system, do not display link to it.
|
||||
$strfulllistofcourses = get_string('fulllistofcourses');
|
||||
$this->page->set_title("$site->shortname: $strfulllistofcourses");
|
||||
} else if (!$coursecat->id || !$coursecat->is_uservisible()) {
|
||||
$strcategories = get_string('categories');
|
||||
$this->page->set_title("$site->shortname: $strcategories");
|
||||
} else {
|
||||
$title = $site->shortname;
|
||||
if (core_course_category::count_all() > 1) {
|
||||
$title .= ": ". $coursecat->get_formatted_name();
|
||||
}
|
||||
$this->page->set_title($title);
|
||||
$strfulllistofcourses = get_string('fulllistofcourses');
|
||||
$this->page->set_title("$site->shortname: $strfulllistofcourses");
|
||||
|
||||
// Print the category selector
|
||||
if (core_course_category::count_all() > 1) {
|
||||
$categorieslist = core_course_category::make_categories_list();
|
||||
if (count($categorieslist) > 1) {
|
||||
$output .= html_writer::start_tag('div', array('class' => 'categorypicker'));
|
||||
$select = new single_select(new moodle_url('/course/index.php'), 'categoryid',
|
||||
core_course_category::make_categories_list(), $coursecat->id, null, 'switchcategory');
|
||||
@@ -1644,13 +1647,13 @@ class core_course_renderer extends plugin_renderer_base {
|
||||
}
|
||||
$coursedisplayoptions['limit'] = $perpage;
|
||||
$catdisplayoptions['limit'] = $perpage;
|
||||
if ($browse === 'courses' || !$coursecat->has_children()) {
|
||||
if ($browse === 'courses' || !$coursecat->get_children_count()) {
|
||||
$coursedisplayoptions['offset'] = $page * $perpage;
|
||||
$coursedisplayoptions['paginationurl'] = new moodle_url($baseurl, array('browse' => 'courses'));
|
||||
$catdisplayoptions['nodisplay'] = true;
|
||||
$catdisplayoptions['viewmoreurl'] = new moodle_url($baseurl, array('browse' => 'categories'));
|
||||
$catdisplayoptions['viewmoretext'] = new lang_string('viewallsubcategories');
|
||||
} else if ($browse === 'categories' || !$coursecat->has_courses()) {
|
||||
} else if ($browse === 'categories' || !$coursecat->get_courses_count()) {
|
||||
$coursedisplayoptions['nodisplay'] = true;
|
||||
$catdisplayoptions['offset'] = $page * $perpage;
|
||||
$catdisplayoptions['paginationurl'] = new moodle_url($baseurl, array('browse' => 'categories'));
|
||||
@@ -1670,24 +1673,23 @@ class core_course_renderer extends plugin_renderer_base {
|
||||
|
||||
// Add action buttons
|
||||
$output .= $this->container_start('buttons');
|
||||
$context = get_category_or_system_context($coursecat->id);
|
||||
if (has_capability('moodle/course:create', $context)) {
|
||||
// Print link to create a new course, for the 1st available category.
|
||||
if ($coursecat->id) {
|
||||
$url = new moodle_url('/course/edit.php', array('category' => $coursecat->id, 'returnto' => 'category'));
|
||||
} else {
|
||||
$url = new moodle_url('/course/edit.php', array('category' => $CFG->defaultrequestcategory, 'returnto' => 'topcat'));
|
||||
if ($coursecat->is_uservisible()) {
|
||||
$context = get_category_or_system_context($coursecat->id);
|
||||
if (has_capability('moodle/course:create', $context)) {
|
||||
// Print link to create a new course, for the 1st available category.
|
||||
if ($coursecat->id) {
|
||||
$url = new moodle_url('/course/edit.php', array('category' => $coursecat->id, 'returnto' => 'category'));
|
||||
} else {
|
||||
$url = new moodle_url('/course/edit.php',
|
||||
array('category' => $CFG->defaultrequestcategory, 'returnto' => 'topcat'));
|
||||
}
|
||||
$output .= $this->single_button($url, get_string('addnewcourse'), 'get');
|
||||
}
|
||||
$output .= $this->single_button($url, get_string('addnewcourse'), 'get');
|
||||
}
|
||||
ob_start();
|
||||
if (core_course_category::count_all() == 1) {
|
||||
print_course_request_buttons(context_system::instance());
|
||||
} else {
|
||||
ob_start();
|
||||
print_course_request_buttons($context);
|
||||
$output .= ob_get_contents();
|
||||
ob_end_clean();
|
||||
}
|
||||
$output .= ob_get_contents();
|
||||
ob_end_clean();
|
||||
$output .= $this->container_end();
|
||||
|
||||
return $output;
|
||||
@@ -1828,7 +1830,7 @@ class core_course_renderer extends plugin_renderer_base {
|
||||
if (empty($displayoptions)) {
|
||||
$displayoptions = array();
|
||||
}
|
||||
$showcategories = core_course_category::count_all() > 1;
|
||||
$showcategories = !core_course_category::is_simple_site();
|
||||
$displayoptions += array('limit' => $CFG->coursesperpage, 'offset' => 0);
|
||||
$chelper = new coursecat_helper();
|
||||
$searchcriteria = array('tagid' => $tagid, 'ctx' => $ctx, 'rec' => $rec);
|
||||
@@ -1950,15 +1952,15 @@ class core_course_renderer extends plugin_renderer_base {
|
||||
if (!empty($courses) || !empty($rcourses) || !empty($rhosts)) {
|
||||
|
||||
$chelper = new coursecat_helper();
|
||||
$totalcount = count($courses);
|
||||
if (count($courses) > $CFG->frontpagecourselimit) {
|
||||
// There are more enrolled courses than we can display, display link to 'My courses'.
|
||||
$totalcount = count($courses);
|
||||
$courses = array_slice($courses, 0, $CFG->frontpagecourselimit, true);
|
||||
$chelper->set_courses_display_options(array(
|
||||
'viewmoreurl' => new moodle_url('/my/'),
|
||||
'viewmoretext' => new lang_string('mycourses')
|
||||
));
|
||||
} else {
|
||||
} else if (core_course_category::top()->is_uservisible()) {
|
||||
// All enrolled courses are displayed, display link to 'All courses' if there are more courses in system.
|
||||
$chelper->set_courses_display_options(array(
|
||||
'viewmoreurl' => new moodle_url('/course/index.php'),
|
||||
@@ -2007,8 +2009,8 @@ class core_course_renderer extends plugin_renderer_base {
|
||||
'viewmoretext' => new lang_string('fulllistofcourses')));
|
||||
|
||||
$chelper->set_attributes(array('class' => 'frontpage-course-list-all'));
|
||||
$courses = core_course_category::get(0)->get_courses($chelper->get_courses_display_options());
|
||||
$totalcount = core_course_category::get(0)->get_courses_count($chelper->get_courses_display_options());
|
||||
$courses = core_course_category::top()->get_courses($chelper->get_courses_display_options());
|
||||
$totalcount = core_course_category::top()->get_courses_count($chelper->get_courses_display_options());
|
||||
if (!$totalcount && !$this->page->user_is_editing() && has_capability('moodle/course:create', context_system::instance())) {
|
||||
// Print link to create a new course, for the 1st available category.
|
||||
return $this->add_new_course_button();
|
||||
@@ -2038,6 +2040,11 @@ class core_course_renderer extends plugin_renderer_base {
|
||||
*/
|
||||
public function frontpage_combo_list() {
|
||||
global $CFG;
|
||||
// TODO MDL-10965 improve.
|
||||
$tree = core_course_category::top();
|
||||
if (!$tree->get_children_count()) {
|
||||
return '';
|
||||
}
|
||||
$chelper = new coursecat_helper();
|
||||
$chelper->set_subcat_depth($CFG->maxcategorydepth)->
|
||||
set_categories_display_options(array(
|
||||
@@ -2051,7 +2058,7 @@ class core_course_renderer extends plugin_renderer_base {
|
||||
array('browse' => 'courses', 'page' => 1))
|
||||
))->
|
||||
set_attributes(array('class' => 'frontpage-category-combo'));
|
||||
return $this->coursecat_tree($chelper, core_course_category::get(0));
|
||||
return $this->coursecat_tree($chelper, $tree);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2061,6 +2068,11 @@ class core_course_renderer extends plugin_renderer_base {
|
||||
*/
|
||||
public function frontpage_categories_list() {
|
||||
global $CFG;
|
||||
// TODO MDL-10965 improve.
|
||||
$tree = core_course_category::top();
|
||||
if (!$tree->get_children_count()) {
|
||||
return '';
|
||||
}
|
||||
$chelper = new coursecat_helper();
|
||||
$chelper->set_subcat_depth($CFG->maxcategorydepth)->
|
||||
set_show_courses(self::COURSECAT_SHOW_COURSES_COUNT)->
|
||||
@@ -2070,7 +2082,7 @@ class core_course_renderer extends plugin_renderer_base {
|
||||
array('browse' => 'categories', 'page' => 1))
|
||||
))->
|
||||
set_attributes(array('class' => 'frontpage-category-names'));
|
||||
return $this->coursecat_tree($chelper, core_course_category::get(0));
|
||||
return $this->coursecat_tree($chelper, $tree);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2379,6 +2391,9 @@ class core_course_renderer extends plugin_renderer_base {
|
||||
* @return string
|
||||
*/
|
||||
protected function frontpage_part($skipdivid, $contentsdivid, $header, $contents) {
|
||||
if (strval($contents) === '') {
|
||||
return '';
|
||||
}
|
||||
$output = html_writer::link('#' . $skipdivid,
|
||||
get_string('skipa', 'access', core_text::strtolower(strip_tags($header))),
|
||||
array('class' => 'skip-block skip'));
|
||||
@@ -2438,10 +2453,8 @@ class core_course_renderer extends plugin_renderer_base {
|
||||
|
||||
case FRONTPAGEALLCOURSELIST:
|
||||
$availablecourseshtml = $this->frontpage_available_courses();
|
||||
if (!empty($availablecourseshtml)) {
|
||||
$output .= $this->frontpage_part('skipavailablecourses', 'frontpage-available-course-list',
|
||||
get_string('availablecourses'), $availablecourseshtml);
|
||||
}
|
||||
$output .= $this->frontpage_part('skipavailablecourses', 'frontpage-available-course-list',
|
||||
get_string('availablecourses'), $availablecourseshtml);
|
||||
break;
|
||||
|
||||
case FRONTPAGECATEGORYNAMES:
|
||||
@@ -2705,7 +2718,7 @@ class coursecat_helper {
|
||||
* @return string|null
|
||||
*/
|
||||
public function get_category_formatted_description($coursecat, $options = null) {
|
||||
if ($coursecat->id && !empty($coursecat->description)) {
|
||||
if ($coursecat->id && $coursecat->is_uservisible() && !empty($coursecat->description)) {
|
||||
if (!isset($coursecat->descriptionformat)) {
|
||||
$descriptionformat = FORMAT_MOODLE;
|
||||
} else {
|
||||
|
||||
+2
-1
@@ -71,7 +71,8 @@ $strsearch = new lang_string("search");
|
||||
$strsearchresults = new lang_string("searchresults");
|
||||
$strnovalidcourses = new lang_string('novalidcourses');
|
||||
|
||||
$PAGE->navbar->add($strcourses, new moodle_url('/course/index.php'));
|
||||
$courseurl = core_course_category::user_top() ? new moodle_url('/course/index.php') : null;
|
||||
$PAGE->navbar->add($strcourses, $courseurl);
|
||||
$PAGE->navbar->add($strsearch, new moodle_url('/course/search.php'));
|
||||
if (!empty($search)) {
|
||||
$PAGE->navbar->add(s($search));
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
@core @core_course
|
||||
Feature: Restricting access to course lists
|
||||
In order to provide more targeted content
|
||||
As a Moodle Administrator
|
||||
I need to be able to give/revoke capabilities to view list of courses
|
||||
|
||||
Background:
|
||||
Given the following "categories" exist:
|
||||
| name | category | idnumber |
|
||||
| Science category | 0 | SCI |
|
||||
| English category | 0 | ENG |
|
||||
| Other category | 0 | MISC |
|
||||
And the following "courses" exist:
|
||||
| fullname | shortname | category |
|
||||
| Biology Y1 | BIO1 | SCI |
|
||||
| Biology Y2 | BI02 | SCI |
|
||||
| English Y1 | ENG1 | ENG |
|
||||
| English Y2 | ENG2 | ENG |
|
||||
| Humanities Y1 | HUM2 | MISC |
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| user0 | User | Z | user0@example.com |
|
||||
| userb | User | B | userb@example.com |
|
||||
| usere | User | E | usere@example.com |
|
||||
Given the following "roles" exist:
|
||||
| name | shortname | description | archetype |
|
||||
| Category viewer | coursebrowse | My custom role 1 | |
|
||||
Given I log in as "admin"
|
||||
And I set the following system permissions of "Authenticated user" role:
|
||||
| capability | permission |
|
||||
| moodle/course:browse | Prevent |
|
||||
And I set the following system permissions of "Guest" role:
|
||||
| capability | permission |
|
||||
| moodle/course:browse | Prevent |
|
||||
And I set the following system permissions of "Category viewer" role:
|
||||
| capability | permission |
|
||||
| moodle/course:browse | Allow |
|
||||
And I am on site homepage
|
||||
And I turn editing mode on
|
||||
And I add the "Navigation" block if not present
|
||||
And I log out
|
||||
And the following "role assigns" exist:
|
||||
| user | role | contextlevel | reference |
|
||||
| usere | coursebrowse | Category | ENG |
|
||||
| userb | coursebrowse | Category | ENG |
|
||||
| userb | coursebrowse | Category | SCI |
|
||||
|
||||
Scenario: Browse courses as a user without any browse capability
|
||||
When I log in as "user0"
|
||||
And I am on site homepage
|
||||
Then I should not see "Available courses"
|
||||
And "Courses" "link" should not exist in the "Navigation" "block"
|
||||
And I log out
|
||||
|
||||
Scenario: Browse own courses as a user without any browse capability
|
||||
Given the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| user0 | BIO1 | student |
|
||||
When I log in as "user0"
|
||||
And I am on site homepage
|
||||
And I should see "Available courses"
|
||||
And I should see "Biology Y1"
|
||||
And "Courses" "link" should not exist in the "Navigation" "block"
|
||||
And I log out
|
||||
|
||||
Scenario: Browse courses as a user who has access to only one category
|
||||
When I log in as "usere"
|
||||
And I am on site homepage
|
||||
Then I should see "Available courses"
|
||||
And I should see "English Y1"
|
||||
And I should see "English Y2"
|
||||
And I should not see "Biology"
|
||||
And I should not see "Humanities"
|
||||
And I click on "Courses" "link" in the "Navigation" "block"
|
||||
And "English category" "text" should exist in the ".breadcrumb" "css_element"
|
||||
And I should see "English Y1"
|
||||
And I should see "English Y2"
|
||||
And I should not see "Biology"
|
||||
And I should not see "Humanities"
|
||||
And I should not see "Other category"
|
||||
And I follow "English Y2"
|
||||
And I should see "You can not enrol yourself in this course."
|
||||
And I log out
|
||||
|
||||
Scenario: Browse courses as a user who has access to several but not all categories
|
||||
When I log in as "userb"
|
||||
And I am on site homepage
|
||||
Then I should see "Available courses"
|
||||
And I should see "English Y1"
|
||||
And I should see "English Y2"
|
||||
And I should see "Biology"
|
||||
And I should not see "Humanities"
|
||||
And I click on "Courses" "link" in the "Navigation" "block"
|
||||
And "category" "text" should not exist in the ".breadcrumb" "css_element"
|
||||
And I should see "Science category"
|
||||
And I should see "English category"
|
||||
And I should not see "Other category"
|
||||
And I follow "Science category"
|
||||
And I should see "Biology Y2"
|
||||
And I should not see "English Y1"
|
||||
And the "Course categories" select box should contain "Science category"
|
||||
And the "Course categories" select box should contain "English category"
|
||||
And the "Course categories" select box should not contain "Other category"
|
||||
And I follow "Biology Y1"
|
||||
And I should see "You can not enrol yourself in this course."
|
||||
And I log out
|
||||
@@ -418,14 +418,37 @@ class core_course_category_testcase extends advanced_testcase {
|
||||
// Dont assume there is just one. An add-on might create a category as part of the install.
|
||||
$numcategories = $DB->count_records('course_categories');
|
||||
$this->assertEquals($numcategories, core_course_category::count_all());
|
||||
$this->assertDebuggingCalled('Method core_course_category::count_all() is deprecated. Please use ' .
|
||||
'core_course_category::is_simple_site()', DEBUG_DEVELOPER);
|
||||
$category1 = core_course_category::create(array('name' => 'Cat1'));
|
||||
$category2 = core_course_category::create(array('name' => 'Cat2', 'parent' => $category1->id));
|
||||
$category3 = core_course_category::create(array('name' => 'Cat3', 'parent' => $category2->id, 'visible' => 0));
|
||||
// Now we've got three more.
|
||||
$this->assertEquals($numcategories + 3, core_course_category::count_all());
|
||||
$this->assertDebuggingCalled('Method core_course_category::count_all() is deprecated. Please use ' .
|
||||
'core_course_category::is_simple_site()', DEBUG_DEVELOPER);
|
||||
cache_helper::purge_by_event('changesincoursecat');
|
||||
// We should still have 4.
|
||||
$this->assertEquals($numcategories + 3, core_course_category::count_all());
|
||||
$this->assertDebuggingCalled('Method core_course_category::count_all() is deprecated. Please use ' .
|
||||
'core_course_category::is_simple_site()', DEBUG_DEVELOPER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the is_simple_site function
|
||||
*/
|
||||
public function test_is_simple_site() {
|
||||
// By default site has one category and is considered simple.
|
||||
$this->assertEquals(true, core_course_category::is_simple_site());
|
||||
$default = core_course_category::get_default();
|
||||
// When there is only one category but it is hidden, it is not a simple site.
|
||||
$default->update(['visible' => 0]);
|
||||
$this->assertEquals(false, core_course_category::is_simple_site());
|
||||
$default->update(['visible' => 1]);
|
||||
$this->assertEquals(true, core_course_category::is_simple_site());
|
||||
// As soon as there is more than one category, site is not simple any more.
|
||||
core_course_category::create(array('name' => 'Cat1'));
|
||||
$this->assertEquals(false, core_course_category::is_simple_site());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1007,7 +1030,7 @@ class core_course_category_testcase extends advanced_testcase {
|
||||
|
||||
// Expecting to get an exception as this new user does not have the moodle/category:viewhiddencategories capability.
|
||||
$this->expectException('moodle_exception');
|
||||
$this->expectExceptionMessage('unknowncategory');
|
||||
$this->expectExceptionMessage(get_string('cannotviewcategory', 'error'));
|
||||
core_course_category::get($category2->id);
|
||||
}
|
||||
|
||||
@@ -1033,7 +1056,7 @@ class core_course_category_testcase extends advanced_testcase {
|
||||
|
||||
$this->assertEquals($category1->id, core_course_category::get($category1->id, MUST_EXIST, false, $user2)->id);
|
||||
$this->expectException('moodle_exception');
|
||||
$this->expectExceptionMessage('unknowncategory');
|
||||
$this->expectExceptionMessage(get_string('cannotviewcategory', 'error'));
|
||||
core_course_category::get($category2->id, MUST_EXIST, false, $user2);
|
||||
}
|
||||
|
||||
|
||||
@@ -876,8 +876,7 @@ class core_enrol_external extends external_api {
|
||||
self::validate_context(context_system::instance());
|
||||
|
||||
$course = $DB->get_record('course', array('id' => $params['courseid']), '*', MUST_EXIST);
|
||||
$context = context_course::instance($course->id);
|
||||
if (!$course->visible and !has_capability('moodle/course:viewhiddencourses', $context)) {
|
||||
if (!core_course_category::can_view_course_info($course) && !can_access_course($course)) {
|
||||
throw new moodle_exception('coursehidden');
|
||||
}
|
||||
|
||||
|
||||
@@ -75,8 +75,7 @@ class enrol_guest_external extends external_api {
|
||||
$enrolinstance = $DB->get_record('enrol', array('id' => $params['instanceid']), '*', MUST_EXIST);
|
||||
|
||||
$course = $DB->get_record('course', array('id' => $enrolinstance->courseid), '*', MUST_EXIST);
|
||||
$context = context_course::instance($course->id);
|
||||
if (!$course->visible and !has_capability('moodle/course:viewhiddencourses', $context)) {
|
||||
if (!core_course_category::can_view_course_info($course) && !can_access_course($course)) {
|
||||
throw new moodle_exception('coursehidden');
|
||||
}
|
||||
|
||||
|
||||
@@ -61,6 +61,11 @@ if (\core\session\manager::is_loggedinas() and $USER->loginascontext->contextlev
|
||||
print_error('loginasnoenrol', '', $CFG->wwwroot.'/course/view.php?id='.$USER->loginascontext->instanceid);
|
||||
}
|
||||
|
||||
// Check if user has access to the category where the course is located.
|
||||
if (!core_course_category::can_view_course_info($course) && !is_enrolled($context, $USER, '', true)) {
|
||||
print_error('coursehidden', '', $CFG->wwwroot . '/');
|
||||
}
|
||||
|
||||
// get all enrol forms available in this course
|
||||
$enrols = enrol_get_plugins(true);
|
||||
$enrolinstances = enrol_get_instances($course->id, true);
|
||||
|
||||
@@ -71,8 +71,7 @@ class enrol_self_external extends external_api {
|
||||
|
||||
$enrolinstance = $DB->get_record('enrol', array('id' => $params['instanceid']), '*', MUST_EXIST);
|
||||
$course = $DB->get_record('course', array('id' => $enrolinstance->courseid), '*', MUST_EXIST);
|
||||
$context = context_course::instance($course->id);
|
||||
if (!$course->visible and !has_capability('moodle/course:viewhiddencourses', $context)) {
|
||||
if (!core_course_category::can_view_course_info($course) && !can_access_course($course)) {
|
||||
throw new moodle_exception('coursehidden');
|
||||
}
|
||||
|
||||
@@ -147,7 +146,7 @@ class enrol_self_external extends external_api {
|
||||
$context = context_course::instance($course->id);
|
||||
self::validate_context(context_system::instance());
|
||||
|
||||
if (!$course->visible and !has_capability('moodle/course:viewhiddencourses', $context)) {
|
||||
if (!core_course_category::can_view_course_info($course)) {
|
||||
throw new moodle_exception('coursehidden');
|
||||
}
|
||||
|
||||
|
||||
@@ -169,6 +169,7 @@ $string['cannotuseadmin'] = 'You need to be an admin user to use this page';
|
||||
$string['cannotuseadminadminorteacher'] = 'You need to be a teacher or admin user to use this page';
|
||||
$string['cannotusepage'] = 'Only teachers and administrators can use this page';
|
||||
$string['cannotusepage2'] = 'Sorry, you may not use this page';
|
||||
$string['cannotviewcategory'] = 'You don\'t have permission to view courses here';
|
||||
$string['cannotviewprofile'] = 'You cannot view the profile of this user';
|
||||
$string['cannotviewreport'] = 'You cannot view this report';
|
||||
$string['cannotwritefile'] = 'Cannot write to file ({$a})';
|
||||
|
||||
@@ -232,6 +232,7 @@ $string['categorycontents'] = 'Subcategories and courses';
|
||||
$string['categorycurrentcontents'] = 'Contents of {$a}';
|
||||
$string['categorydeleted'] = 'The category \'{$a}\' was deleted';
|
||||
$string['categoryduplicate'] = 'A category named \'{$a}\' already exists!';
|
||||
$string['categoryhidden'] = '(hidden)';
|
||||
$string['categorymodifiedcancel'] = 'Category was modified! Please cancel and try again.';
|
||||
$string['categoryname'] = 'Category name';
|
||||
$string['categorysubcategoryof'] = '{$a->category} - subcategory of {$a->parentcategory}';
|
||||
|
||||
@@ -154,6 +154,7 @@ $string['confirmunassignyes'] = 'Remove';
|
||||
$string['confirmunassignno'] = 'Cancel';
|
||||
$string['context'] = 'Context';
|
||||
$string['course:activityvisibility'] = 'Hide/show activities';
|
||||
$string['course:browse'] = 'View list of courses where user is not enrolled';
|
||||
$string['course:bulkmessaging'] = 'Send a message to many people';
|
||||
$string['course:create'] = 'Create courses';
|
||||
$string['course:creategroupconversations'] = 'Create group conversations';
|
||||
|
||||
@@ -1948,6 +1948,11 @@ function can_access_course(stdClass $course, $user = null, $withcapability = '',
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!core_course_category::can_view_course_info($course)) {
|
||||
// No guest access if user does not have capability to browse courses.
|
||||
return false;
|
||||
}
|
||||
|
||||
// if not enrolled try to gain temporary guest access
|
||||
$instances = $DB->get_records('enrol', array('courseid'=>$course->id, 'status'=>ENROL_INSTANCE_ENABLED), 'sortorder, id ASC');
|
||||
$enrols = enrol_get_plugins(true);
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
define(["core/ajax","jquery"],function(a,b){return{processResults:function(a,c){var d=[],e=0,f=String(b(a).data("exclude")).split(",");for(e=0;e<c.courses.length;e++)f.indexOf(String(c.courses[e].id))===-1&&d.push({value:c.courses[e].id,label:c.courses[e].displayname});return d},transport:function(c,d,e,f){var g=b(c),h=g.data("requiredcapabilities");h=""!==h.trim()?h.split(","):[];var i=g.data("limittoenrolled"),j=g.data("includefrontpage"),k=null;"undefined"==typeof d&&(d="");var l={criterianame:"search",criteriavalue:d,page:0,perpage:100,requiredcapabilities:h,limittoenrolled:i},m=[{methodname:"core_course_search_courses",args:l}];j&&m.push({methodname:"core_course_get_courses",args:{options:{ids:[j]}}}),k=a.call(m),b.when.apply(b.when,k).done(function(a,b){if(b&&1==b.length){var c=b.pop(),f=""===d||c.fullname.toUpperCase().indexOf(d.toUpperCase())>-1||c.shortname.toUpperCase().indexOf(d.toUpperCase())>-1;f&&a.courses.splice(0,0,c)}e(a)}).fail(f)}}});
|
||||
define(["core/ajax","jquery"],function(a,b){return{processResults:function(a,c){var d=[],e=0,f=String(b(a).data("exclude")).split(",");for(e=0;e<c.courses.length;e++)f.indexOf(String(c.courses[e].id))===-1&&d.push({value:c.courses[e].id,label:c.courses[e].displayname});return d},transport:function(c,d,e,f){var g=b(c),h=g.data("requiredcapabilities");h=""!==h.trim()?h.split(","):[];var i=g.data("limittoenrolled"),j=g.data("includefrontpage"),k=g.data("onlywithcompletion"),l=null;"undefined"==typeof d&&(d="");var m={criterianame:"search",criteriavalue:d,page:0,perpage:100,requiredcapabilities:h,limittoenrolled:i,onlywithcompletion:k},n=[{methodname:"core_course_search_courses",args:m}];j&&n.push({methodname:"core_course_get_courses",args:{options:{ids:[j]}}}),l=a.call(n),b.when.apply(b.when,l).done(function(a,b){if(b&&1==b.length){var c=b.pop(),f=""===d||c.fullname.toUpperCase().indexOf(d.toUpperCase())>-1||c.shortname.toUpperCase().indexOf(d.toUpperCase())>-1;f&&a.courses.splice(0,0,c)}e(a)}).fail(f)}}});
|
||||
@@ -54,6 +54,7 @@ define(['core/ajax', 'jquery'], function(ajax, $) {
|
||||
|
||||
var limittoenrolled = el.data('limittoenrolled');
|
||||
var includefrontpage = el.data('includefrontpage');
|
||||
var onlywithcompletion = el.data('onlywithcompletion');
|
||||
|
||||
// Build the query.
|
||||
var promises = null;
|
||||
@@ -68,7 +69,8 @@ define(['core/ajax', 'jquery'], function(ajax, $) {
|
||||
page: 0,
|
||||
perpage: 100,
|
||||
requiredcapabilities: requiredcapabilities,
|
||||
limittoenrolled: limittoenrolled
|
||||
limittoenrolled: limittoenrolled,
|
||||
onlywithcompletion: onlywithcompletion
|
||||
};
|
||||
|
||||
var calls = [{
|
||||
|
||||
+10
-86
@@ -597,6 +597,9 @@ function get_course($courseid, $clone = true) {
|
||||
* we are using distinct. You almost _NEVER_ need all the fields
|
||||
* in such a large SELECT
|
||||
*
|
||||
* Consider using core_course_category::get_courses()
|
||||
* or core_course_category::search_courses() instead since they use caching.
|
||||
*
|
||||
* @global object
|
||||
* @global object
|
||||
* @global object
|
||||
@@ -643,12 +646,7 @@ function get_courses($categoryid="all", $sort="c.sortorder ASC", $fields="c.*")
|
||||
// loop throught them
|
||||
foreach ($courses as $course) {
|
||||
context_helper::preload_from_record($course);
|
||||
if (isset($course->visible) && $course->visible <= 0) {
|
||||
// for hidden courses, require visibility check
|
||||
if (has_capability('moodle/course:viewhiddencourses', context_course::instance($course->id))) {
|
||||
$visiblecourses [$course->id] = $course;
|
||||
}
|
||||
} else {
|
||||
if (core_course_category::can_view_course_info($course)) {
|
||||
$visiblecourses [$course->id] = $course;
|
||||
}
|
||||
}
|
||||
@@ -656,81 +654,6 @@ function get_courses($categoryid="all", $sort="c.sortorder ASC", $fields="c.*")
|
||||
return $visiblecourses;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns list of courses, for whole site, or category
|
||||
*
|
||||
* Similar to get_courses, but allows paging
|
||||
* Important: Using c.* for fields is extremely expensive because
|
||||
* we are using distinct. You almost _NEVER_ need all the fields
|
||||
* in such a large SELECT
|
||||
*
|
||||
* @global object
|
||||
* @global object
|
||||
* @global object
|
||||
* @uses CONTEXT_COURSE
|
||||
* @param string|int $categoryid Either a category id or 'all' for everything
|
||||
* @param string $sort A field and direction to sort by
|
||||
* @param string $fields The additional fields to return
|
||||
* @param int $totalcount Reference for the number of courses
|
||||
* @param string $limitfrom The course to start from
|
||||
* @param string $limitnum The number of courses to limit to
|
||||
* @return array Array of courses
|
||||
*/
|
||||
function get_courses_page($categoryid="all", $sort="c.sortorder ASC", $fields="c.*",
|
||||
&$totalcount, $limitfrom="", $limitnum="") {
|
||||
global $USER, $CFG, $DB;
|
||||
|
||||
$params = array();
|
||||
|
||||
$categoryselect = "";
|
||||
if ($categoryid !== "all" && is_numeric($categoryid)) {
|
||||
$categoryselect = "WHERE c.category = :catid";
|
||||
$params['catid'] = $categoryid;
|
||||
} else {
|
||||
$categoryselect = "";
|
||||
}
|
||||
|
||||
$ccselect = ', ' . context_helper::get_preload_record_columns_sql('ctx');
|
||||
$ccjoin = "LEFT JOIN {context} ctx ON (ctx.instanceid = c.id AND ctx.contextlevel = :contextlevel)";
|
||||
$params['contextlevel'] = CONTEXT_COURSE;
|
||||
|
||||
$totalcount = 0;
|
||||
if (!$limitfrom) {
|
||||
$limitfrom = 0;
|
||||
}
|
||||
$visiblecourses = array();
|
||||
|
||||
$sql = "SELECT $fields $ccselect
|
||||
FROM {course} c
|
||||
$ccjoin
|
||||
$categoryselect
|
||||
ORDER BY $sort";
|
||||
|
||||
// pull out all course matching the cat
|
||||
$rs = $DB->get_recordset_sql($sql, $params);
|
||||
// iteration will have to be done inside loop to keep track of the limitfrom and limitnum
|
||||
foreach($rs as $course) {
|
||||
context_helper::preload_from_record($course);
|
||||
if ($course->visible <= 0) {
|
||||
// for hidden courses, require visibility check
|
||||
if (has_capability('moodle/course:viewhiddencourses', context_course::instance($course->id))) {
|
||||
$totalcount++;
|
||||
if ($totalcount > $limitfrom && (!$limitnum or count($visiblecourses) < $limitnum)) {
|
||||
$visiblecourses [$course->id] = $course;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$totalcount++;
|
||||
if ($totalcount > $limitfrom && (!$limitnum or count($visiblecourses) < $limitnum)) {
|
||||
$visiblecourses [$course->id] = $course;
|
||||
}
|
||||
}
|
||||
}
|
||||
$rs->close();
|
||||
return $visiblecourses;
|
||||
}
|
||||
|
||||
/**
|
||||
* A list of courses that match a search
|
||||
*
|
||||
@@ -742,10 +665,12 @@ function get_courses_page($categoryid="all", $sort="c.sortorder ASC", $fields="c
|
||||
* @param int $recordsperpage The number of records per page
|
||||
* @param int $totalcount Passed in by reference.
|
||||
* @param array $requiredcapabilities Extra list of capabilities used to filter courses
|
||||
* @return object {@link $COURSE} records
|
||||
* @param array $searchcond additional search conditions, for example ['c.enablecompletion = :p1']
|
||||
* @param array $params named parameters for additional search conditions, for example ['p1' => 1]
|
||||
* @return stdClass[] {@link $COURSE} records
|
||||
*/
|
||||
function get_courses_search($searchterms, $sort, $page, $recordsperpage, &$totalcount,
|
||||
$requiredcapabilities = array()) {
|
||||
$requiredcapabilities = array(), $searchcond = [], $params = []) {
|
||||
global $CFG, $DB;
|
||||
|
||||
if ($DB->sql_regex_supported()) {
|
||||
@@ -753,8 +678,6 @@ function get_courses_search($searchterms, $sort, $page, $recordsperpage, &$total
|
||||
$NOTREGEXP = $DB->sql_regex(false);
|
||||
}
|
||||
|
||||
$searchcond = array();
|
||||
$params = array();
|
||||
$i = 0;
|
||||
|
||||
// Thanks Oracle for your non-ansi concat and type limits in coalesce. MDL-29912
|
||||
@@ -822,12 +745,13 @@ function get_courses_search($searchterms, $sort, $page, $recordsperpage, &$total
|
||||
WHERE $searchcond AND c.id <> ".SITEID."
|
||||
ORDER BY $sort";
|
||||
|
||||
$mycourses = enrol_get_my_courses();
|
||||
$rs = $DB->get_recordset_sql($sql, $params);
|
||||
foreach($rs as $course) {
|
||||
// Preload contexts only for hidden courses or courses we need to return.
|
||||
context_helper::preload_from_record($course);
|
||||
$coursecontext = context_course::instance($course->id);
|
||||
if (!$course->visible && !has_capability('moodle/course:viewhiddencourses', $coursecontext)) {
|
||||
if (!array_key_exists($course->id, $mycourses) && !core_course_category::can_view_course_info($course)) {
|
||||
continue;
|
||||
}
|
||||
if (!empty($requiredcapabilities)) {
|
||||
|
||||
@@ -733,6 +733,16 @@ $capabilities = array(
|
||||
'clonepermissionsfrom' => 'moodle/category:update'
|
||||
),
|
||||
|
||||
'moodle/course:browse' => array(
|
||||
|
||||
'captype' => 'read',
|
||||
'contextlevel' => CONTEXT_COURSE,
|
||||
'archetypes' => array(
|
||||
'guest' => CAP_ALLOW,
|
||||
'user' => CAP_ALLOW,
|
||||
)
|
||||
),
|
||||
|
||||
'moodle/category:viewhiddencategories' => array(
|
||||
|
||||
'captype' => 'read',
|
||||
|
||||
@@ -3611,3 +3611,70 @@ function message_get_contact($contactid) {
|
||||
|
||||
return \core_message\api::get_contact($USER->id, $contactid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of courses, for whole site, or category
|
||||
*
|
||||
* Similar to get_courses, but allows paging
|
||||
* Important: Using c.* for fields is extremely expensive because
|
||||
* we are using distinct. You almost _NEVER_ need all the fields
|
||||
* in such a large SELECT
|
||||
*
|
||||
* @deprecated since Moodle 3.7
|
||||
* @todo The final deprecation of this function will take place in Moodle 41 - see MDL-65319.
|
||||
*
|
||||
* @param string|int $categoryid Either a category id or 'all' for everything
|
||||
* @param string $sort A field and direction to sort by
|
||||
* @param string $fields The additional fields to return
|
||||
* @param int $totalcount Reference for the number of courses
|
||||
* @param string $limitfrom The course to start from
|
||||
* @param string $limitnum The number of courses to limit to
|
||||
* @return array Array of courses
|
||||
*/
|
||||
function get_courses_page($categoryid="all", $sort="c.sortorder ASC", $fields="c.*",
|
||||
&$totalcount, $limitfrom="", $limitnum="") {
|
||||
debugging('Function get_courses_page() is deprecated. Please use core_course_category::get_courses() ' .
|
||||
'or core_course_category::search_courses()', DEBUG_DEVELOPER);
|
||||
global $USER, $CFG, $DB;
|
||||
|
||||
$params = array();
|
||||
|
||||
$categoryselect = "";
|
||||
if ($categoryid !== "all" && is_numeric($categoryid)) {
|
||||
$categoryselect = "WHERE c.category = :catid";
|
||||
$params['catid'] = $categoryid;
|
||||
} else {
|
||||
$categoryselect = "";
|
||||
}
|
||||
|
||||
$ccselect = ', ' . context_helper::get_preload_record_columns_sql('ctx');
|
||||
$ccjoin = "LEFT JOIN {context} ctx ON (ctx.instanceid = c.id AND ctx.contextlevel = :contextlevel)";
|
||||
$params['contextlevel'] = CONTEXT_COURSE;
|
||||
|
||||
$totalcount = 0;
|
||||
if (!$limitfrom) {
|
||||
$limitfrom = 0;
|
||||
}
|
||||
$visiblecourses = array();
|
||||
|
||||
$sql = "SELECT $fields $ccselect
|
||||
FROM {course} c
|
||||
$ccjoin
|
||||
$categoryselect
|
||||
ORDER BY $sort";
|
||||
|
||||
// Pull out all course matching the cat.
|
||||
$rs = $DB->get_recordset_sql($sql, $params);
|
||||
// Iteration will have to be done inside loop to keep track of the limitfrom and limitnum.
|
||||
foreach ($rs as $course) {
|
||||
context_helper::preload_from_record($course);
|
||||
if (core_course_category::can_view_course_info($course)) {
|
||||
$totalcount++;
|
||||
if ($totalcount > $limitfrom && (!$limitnum or count($visiblecourses) < $limitnum)) {
|
||||
$visiblecourses [$course->id] = $course;
|
||||
}
|
||||
}
|
||||
}
|
||||
$rs->close();
|
||||
return $visiblecourses;
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ class file_info_context_coursecat extends file_info {
|
||||
public function get_file_info($component, $filearea, $itemid, $filepath, $filename) {
|
||||
global $DB;
|
||||
|
||||
if (!$this->category->visible and !has_capability('moodle/category:viewhiddencategories', $this->context)) {
|
||||
if (!core_course_category::can_view_category($this->category)) {
|
||||
if (empty($component)) {
|
||||
// we can not list the category contents, so try parent, or top system
|
||||
if ($this->category->parent and $pc = $DB->get_record('course_categories', array('id'=>$this->category->parent))) {
|
||||
@@ -101,7 +101,7 @@ class file_info_context_coursecat extends file_info {
|
||||
// No coursecat description area for "system".
|
||||
return null;
|
||||
}
|
||||
if (!$this->category->visible and !has_capability('moodle/category:viewhiddencategories', $this->context)) {
|
||||
if (!core_course_category::can_view_category($this->category)) {
|
||||
return null;
|
||||
}
|
||||
if (!has_capability('moodle/category:manage', $this->context)) {
|
||||
@@ -228,8 +228,7 @@ class file_info_context_coursecat extends file_info {
|
||||
|
||||
foreach ($coursecats as $id => &$category) {
|
||||
context_helper::preload_from_record($category);
|
||||
$context = context_coursecat::instance($category->id);
|
||||
if (!$category->visible && !has_capability('moodle/category:viewhiddencategories', $context)) {
|
||||
if (!core_course_category::can_view_category($category)) {
|
||||
$hiddencats[$id] = $coursecats[$id];
|
||||
unset($coursecats[$id]);
|
||||
}
|
||||
|
||||
+2
-5
@@ -4626,11 +4626,8 @@ function file_pluginfile($relativepath, $forcedownload, $preview = null, $offlin
|
||||
}
|
||||
|
||||
// Check if user can view this category.
|
||||
if (!has_capability('moodle/category:viewhiddencategories', $context)) {
|
||||
$coursecatvisible = $DB->get_field('course_categories', 'visible', array('id' => $context->instanceid));
|
||||
if (!$coursecatvisible) {
|
||||
send_file_not_found();
|
||||
}
|
||||
if (!core_course_category::get($context->instanceid, IGNORE_MISSING)) {
|
||||
send_file_not_found();
|
||||
}
|
||||
|
||||
$filename = array_pop($args);
|
||||
|
||||
+6
-1
@@ -71,6 +71,7 @@ class MoodleQuickForm_course extends MoodleQuickForm_autocomplete {
|
||||
* 'requiredcapabilities' - array of capabilities. Uses ANY to combine them.
|
||||
* 'limittoenrolled' - boolean Limits to enrolled courses.
|
||||
* 'includefrontpage' - boolean Enables the frontpage to be selected.
|
||||
* 'onlywithcompletion' - only courses where completion is enabled
|
||||
*/
|
||||
public function __construct($elementname = null, $elementlabel = null, $options = array()) {
|
||||
if (isset($options['multiple'])) {
|
||||
@@ -107,6 +108,9 @@ class MoodleQuickForm_course extends MoodleQuickForm_autocomplete {
|
||||
if (!empty($options['includefrontpage'])) {
|
||||
$validattributes['data-includefrontpage'] = SITEID;
|
||||
}
|
||||
if (!empty($options['onlywithcompletion'])) {
|
||||
$validattributes['data-onlywithcompletion'] = 1;
|
||||
}
|
||||
|
||||
parent::__construct($elementname, $elementlabel, array(), $validattributes);
|
||||
}
|
||||
@@ -146,12 +150,13 @@ class MoodleQuickForm_course extends MoodleQuickForm_autocomplete {
|
||||
WHERE c.id ". $whereclause." ORDER BY c.sortorder";
|
||||
$list = $DB->get_records_sql($sql, array('contextcourse' => CONTEXT_COURSE) + $params);
|
||||
|
||||
$mycourses = enrol_get_my_courses(null, null, 0, array_keys($list));
|
||||
$coursestoselect = array();
|
||||
foreach ($list as $course) {
|
||||
context_helper::preload_from_record($course);
|
||||
$context = context_course::instance($course->id);
|
||||
// Make sure we can see the course.
|
||||
if (!$course->visible && !has_capability('moodle/course:viewhiddencourses', $context)) {
|
||||
if (!array_key_exists($course->id, $mycourses) && !core_course_category::can_view_course_info($course)) {
|
||||
continue;
|
||||
}
|
||||
$label = format_string(get_course_display_name_for_list($course), true, ['context' => $context]);
|
||||
|
||||
+11
-1
@@ -2906,7 +2906,7 @@ function require_login($courseorid = null, $autologinguest = true, $cm = null, $
|
||||
$USER->enrol['enrolled'][$course->id] = $until;
|
||||
$access = true;
|
||||
|
||||
} else {
|
||||
} else if (core_course_category::can_view_course_info($course)) {
|
||||
$params = array('courseid' => $course->id, 'status' => ENROL_INSTANCE_ENABLED);
|
||||
$instances = $DB->get_records('enrol', $params, 'sortorder, id ASC');
|
||||
$enrols = enrol_get_plugins(true);
|
||||
@@ -2941,6 +2941,16 @@ function require_login($courseorid = null, $autologinguest = true, $cm = null, $
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// User is not enrolled and is not allowed to browse courses here.
|
||||
if ($preventredirect) {
|
||||
throw new require_login_exception('Course is not available');
|
||||
}
|
||||
$PAGE->set_context(null);
|
||||
// We need to override the navigation URL as the course won't have been added to the navigation and thus
|
||||
// the navigation will mess up when trying to find it.
|
||||
navigation_node::override_active_url(new moodle_url('/'));
|
||||
notice(get_string('coursehidden'), $CFG->wwwroot .'/');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+27
-17
@@ -1298,6 +1298,9 @@ class global_navigation extends navigation_node {
|
||||
$this->rootnodes['currentcourse'] = $this->add(get_string('currentcourse'), null, self::TYPE_ROOTNODE, null, 'currentcourse');
|
||||
$this->rootnodes['mycourses'] = $this->add(get_string('mycourses'), null, self::TYPE_ROOTNODE, null, 'mycourses', new pix_icon('i/course', ''));
|
||||
$this->rootnodes['courses'] = $this->add(get_string('courses'), new moodle_url('/course/index.php'), self::TYPE_ROOTNODE, null, 'courses');
|
||||
if (!core_course_category::user_top()) {
|
||||
$this->rootnodes['courses']->hide();
|
||||
}
|
||||
$this->rootnodes['users'] = $this->add(get_string('users'), null, self::TYPE_ROOTNODE, null, 'users');
|
||||
|
||||
// We always load the frontpage course to ensure it is available without
|
||||
@@ -1568,7 +1571,7 @@ class global_navigation extends navigation_node {
|
||||
protected function show_my_categories() {
|
||||
global $CFG;
|
||||
if ($this->showmycategories === null) {
|
||||
$this->showmycategories = !empty($CFG->navshowmycoursecategories) && core_course_category::count_all() > 1;
|
||||
$this->showmycategories = !empty($CFG->navshowmycoursecategories) && !core_course_category::is_simple_site();
|
||||
}
|
||||
return $this->showmycategories;
|
||||
}
|
||||
@@ -1951,16 +1954,18 @@ class global_navigation extends navigation_node {
|
||||
if (array_key_exists($category->id, $this->addedcategories)) {
|
||||
return;
|
||||
}
|
||||
$url = new moodle_url('/course/index.php', array('categoryid' => $category->id));
|
||||
$canview = core_course_category::can_view_category($category);
|
||||
$url = $canview ? new moodle_url('/course/index.php', array('categoryid' => $category->id)) : null;
|
||||
$context = context_coursecat::instance($category->id);
|
||||
$categoryname = format_string($category->name, true, array('context' => $context));
|
||||
$categoryname = $canview ? format_string($category->name, true, array('context' => $context)) :
|
||||
get_string('categoryhidden');
|
||||
$categorynode = $parent->add($categoryname, $url, $nodetype, $categoryname, $category->id);
|
||||
if (empty($category->visible)) {
|
||||
if (has_capability('moodle/category:viewhiddencategories', context_system::instance())) {
|
||||
$categorynode->hidden = true;
|
||||
} else {
|
||||
$categorynode->display = false;
|
||||
}
|
||||
if (!$canview) {
|
||||
// User does not have required capabilities to view category.
|
||||
$categorynode->display = false;
|
||||
} else if (!$category->visible) {
|
||||
// Category is hidden but user has capability to view hidden categories.
|
||||
$categorynode->hidden = true;
|
||||
}
|
||||
$this->addedcategories[$category->id] = $categorynode;
|
||||
}
|
||||
@@ -2606,10 +2611,10 @@ class global_navigation extends navigation_node {
|
||||
|
||||
$coursecontext = context_course::instance($course->id);
|
||||
|
||||
if ($course->id != $SITE->id && !$course->visible) {
|
||||
if ($coursetype != self::COURSE_MY && $coursetype != self::COURSE_CURRENT && $course->id != $SITE->id) {
|
||||
if (is_role_switched($course->id)) {
|
||||
// user has to be able to access course in order to switch, let's skip the visibility test here
|
||||
} else if (!has_capability('moodle/course:viewhiddencourses', $coursecontext)) {
|
||||
} else if (!core_course_category::can_view_course_info($course)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -3057,6 +3062,10 @@ class global_navigation extends navigation_node {
|
||||
if (isset($this->addedcategories[$coursecat->id])) {
|
||||
continue;
|
||||
}
|
||||
// Skip categories that are not visible.
|
||||
if (!$coursecat->is_uservisible()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get this course category's parent node.
|
||||
$parent = null;
|
||||
@@ -3209,7 +3218,7 @@ class global_navigation_for_ajax extends global_navigation {
|
||||
}
|
||||
require_course_login($course, true, null, false, true);
|
||||
$this->page->set_context(context_course::instance($course->id));
|
||||
$coursenode = $this->add_course($course);
|
||||
$coursenode = $this->add_course($course, false, self::COURSE_CURRENT);
|
||||
$this->add_course_essentials($coursenode, $course);
|
||||
$this->load_course_sections($course, $coursenode);
|
||||
break;
|
||||
@@ -3221,7 +3230,7 @@ class global_navigation_for_ajax extends global_navigation {
|
||||
$course = $DB->get_record_sql($sql, array($this->instanceid), MUST_EXIST);
|
||||
require_course_login($course, true, null, false, true);
|
||||
$this->page->set_context(context_course::instance($course->id));
|
||||
$coursenode = $this->add_course($course);
|
||||
$coursenode = $this->add_course($course, false, self::COURSE_CURRENT);
|
||||
$this->add_course_essentials($coursenode, $course);
|
||||
$this->load_course_sections($course, $coursenode, $course->sectionnumber);
|
||||
break;
|
||||
@@ -3236,7 +3245,7 @@ class global_navigation_for_ajax extends global_navigation {
|
||||
$cm = $modinfo->get_cm($this->instanceid);
|
||||
require_course_login($course, true, $cm, false, true);
|
||||
$this->page->set_context(context_module::instance($cm->id));
|
||||
$coursenode = $this->load_course($course);
|
||||
$coursenode = $this->add_course($course, false, self::COURSE_CURRENT);
|
||||
$this->load_course_sections($course, $coursenode, null, $cm);
|
||||
$activitynode = $coursenode->find($cm->id, self::TYPE_ACTIVITY);
|
||||
if ($activitynode) {
|
||||
@@ -3597,15 +3606,16 @@ class navbar extends navigation_node {
|
||||
|
||||
$categories = array();
|
||||
$cap = 'moodle/category:viewhiddencategories';
|
||||
$showcategories = core_course_category::count_all() > 1;
|
||||
$showcategories = !core_course_category::is_simple_site();
|
||||
|
||||
if ($showcategories) {
|
||||
foreach ($this->page->categories as $category) {
|
||||
if (!$category->visible && !has_capability($cap, get_category_or_system_context($category->parent))) {
|
||||
$context = context_coursecat::instance($category->id);
|
||||
if (!core_course_category::can_view_category($category)) {
|
||||
continue;
|
||||
}
|
||||
$url = new moodle_url('/course/index.php', array('categoryid' => $category->id));
|
||||
$name = format_string($category->name, true, array('context' => context_coursecat::instance($category->id)));
|
||||
$name = format_string($category->name, true, array('context' => $context));
|
||||
$categorynode = breadcrumb_navigation_node::create($name, $url, self::TYPE_CATEGORY, null, $category->id);
|
||||
if (!$category->visible) {
|
||||
$categorynode->hidden = true;
|
||||
|
||||
@@ -26,6 +26,10 @@ attribute on forms to avoid collisions in forms loaded in AJAX requests.
|
||||
When the parameter is set to that constant, the function won't process file merging, keeping the original state of the file area.
|
||||
* Introduced new callback for plugin developers '<component>_pre_processor_message_send($procname, $proceventdata)':
|
||||
This will allow any plugin to manipulate messages or notifications before they are sent by a processor (email, mobile...)
|
||||
* New capability 'moodle/course:browse' in category context that controls whether user is able to browse list of courses
|
||||
in this category. To work with list of courses use API methods in core_course_category and also 'course' form element.
|
||||
* It is possible to pass additional conditions to get_courses_search();
|
||||
core_course_category::search_courses() now allows to search only among courses with completion enabled.
|
||||
|
||||
=== 3.6 ===
|
||||
|
||||
|
||||
@@ -720,7 +720,7 @@ class manager {
|
||||
(!$limitcontextids || in_array($coursecontext->id, $limitcontextids))) {
|
||||
// Add the course contexts the user can view.
|
||||
foreach ($areasbylevel[CONTEXT_COURSE] as $areaid => $searchclass) {
|
||||
if ($course->visible || has_capability('moodle/course:viewhiddencourses', $coursecontext)) {
|
||||
if (!empty($mycourses[$course->id]) || \core_course_category::can_view_course_info($course)) {
|
||||
$areascontexts[$areaid][$coursecontext->id] = $coursecontext->id;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$version = 2019041000.00; // YYYYMMDD = weekly release date of this DEV branch.
|
||||
$version = 2019041000.01; // YYYYMMDD = weekly release date of this DEV branch.
|
||||
// RR = release increments - 00 in DEV branches.
|
||||
// .XX = incremental changes.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user