From 0cbc248dd35f28728d7b092728793052f0df2ae9 Mon Sep 17 00:00:00 2001 From: Marina Glancy Date: Tue, 14 Mar 2017 10:24:06 +0800 Subject: [PATCH] MDL-58140 completion: access control refinements Part of MDL-58138 epic --- completion/classes/manager.php | 76 +++++++++++++++---- course/bulkcompletion.php | 7 +- .../bulk_activity_completion_renderer.php | 44 +++++++---- course/completion.php | 9 ++- course/defaultcompletion.php | 1 + course/lib.php | 1 + course/templates/activityinstance.mustache | 2 + course/tests/externallib_test.php | 6 +- lib/navigationlib.php | 6 +- 9 files changed, 114 insertions(+), 38 deletions(-) diff --git a/completion/classes/manager.php b/completion/classes/manager.php index 97f8d7d4cd1..5af13cff199 100644 --- a/completion/classes/manager.php +++ b/completion/classes/manager.php @@ -65,26 +65,47 @@ class manager { $sectionobject = new stdClass(); $sectionobject->sectionnumber = $sectionnumber; $sectionobject->name = get_section_name($this->courseid, $sectioninfo); - $sectionobject->activities = []; - - foreach ($section as $cmid) { - $mod = $moduleinfo->get_cm($cmid); - $moduleobject = new stdClass(); - $moduleobject->cmid = $cmid; - $moduleobject->modname = $mod->get_formatted_name(); - $moduleobject->icon = $mod->get_icon_url()->out(); - $moduleobject->url = $mod->url; - - // Get activity completion information. - $moduleobject->completionstatus = $this->get_completion_detail($mod); - - $sectionobject->activities[] = $moduleobject; - } - $data->sections[] = $sectionobject; + $activitiesdata = $this->get_activities($section, true); + $sectionobject->activities = $activitiesdata->activities; + $data->sections[] = $sectionobject; } return $data; } + /** + * Gets the data (context) to be used with the activityinstance template + * + * @param array $cmids list of course module ids + * @param bool $withcompletiondetails include completion details + * @return \stdClass + */ + public function get_activities($cmids, $withcompletiondetails = false) { + $moduleinfo = get_fast_modinfo($this->courseid); + $activities = []; + foreach ($cmids as $cmid) { + $mod = $moduleinfo->get_cm($cmid); + if (!$mod->uservisible) { + continue; + } + $moduleobject = new stdClass(); + $moduleobject->cmid = $cmid; + $moduleobject->modname = $mod->get_formatted_name(); + $moduleobject->icon = $mod->get_icon_url()->out(); + $moduleobject->url = $mod->url; + $moduleobject->canmanage = $withcompletiondetails && self::can_edit_bulk_completion($this->courseid, $mod); + + // Get activity completion information. + if ($moduleobject->canmanage) { + $moduleobject->completionstatus = $this->get_completion_detail($mod); // This is a placeholder only. Must be replaced later. + } else { + $moduleobject->completionstatus = ['icon' => null, 'string' => null]; + } + + $activities[] = $moduleobject; + } + return (object)['activities' => $activities]; + } + private function get_completion_detail(\cm_info $mod) { global $OUTPUT; $strings = []; @@ -137,4 +158,27 @@ class manager { return $data; } + /** + * Checks if current user can edit activity completion + * + * @param int|stdClass $courseorid + * @param \cm_info|null $cm if specified capability for a given coursemodule will be check, + * if not specified capability to edit at least one activity is checked. + */ + public static function can_edit_bulk_completion($courseorid, $cm = null) { + if ($cm) { + return $cm->uservisible && has_capability('moodle/course:manageactivities', $cm->context); + } + $coursecontext = context_course::instance(is_object($courseorid) ? $courseorid->id : $courseorid); + if (has_capability('moodle/course:manageactivities', $coursecontext)) { + return true; + } + $modinfo = get_fast_modinfo($courseorid); + foreach ($modinfo->cms as $mod) { + if ($mod->uservisible && has_capability('moodle/course:manageactivities', $mod->context)) { + return true; + } + } + return false; + } } \ No newline at end of file diff --git a/course/bulkcompletion.php b/course/bulkcompletion.php index 8da4d7939c6..2096b6f1c29 100644 --- a/course/bulkcompletion.php +++ b/course/bulkcompletion.php @@ -43,7 +43,6 @@ if ($id) { print_error('invalidcourseid'); } require_login($course); - require_capability('moodle/course:update', context_course::instance($course->id)); } else { require_login(); @@ -51,6 +50,7 @@ if ($id) { } // Set up the page. +navigation_node::override_active_url(new moodle_url('/course/completion.php', array('id' => $course->id))); $PAGE->set_course($course); $PAGE->set_url('/course/bulkcompletion.php', array('id' => $course->id)); $PAGE->set_title($course->shortname); @@ -58,6 +58,11 @@ $PAGE->set_heading($course->fullname); $PAGE->set_pagelayout('admin'); // Get all that stuff I need for the renderer. +if (!core_completion\manager::can_edit_bulk_completion($id)) { + throw new required_capability_exception(context_course::instance($course->id), + 'moodle/course:manageactivities', 'nopermission'); +} + $manager = new \core_completion\manager($id); $bulkcompletiondata = $manager->get_activities_and_headings(); diff --git a/course/classes/output/bulk_activity_completion_renderer.php b/course/classes/output/bulk_activity_completion_renderer.php index 4f18b8aad2b..779e6292ebe 100644 --- a/course/classes/output/bulk_activity_completion_renderer.php +++ b/course/classes/output/bulk_activity_completion_renderer.php @@ -38,25 +38,33 @@ class core_course_bulk_activity_completion_renderer extends plugin_renderer_base public function navigation($courseid, $page) { $tabs = []; - $tabs[] = new tabobject( - 'completion', - new moodle_url('/course/completion.php', ['id' => $courseid]), - get_string('coursecompletion', 'completion') - ); + if (has_capability('moodle/course:update', context_course::instance($courseid))) { + $tabs[] = new tabobject( + 'completion', + new moodle_url('/course/completion.php', ['id' => $courseid]), + get_string('coursecompletion', 'completion') + ); - $tabs[] = new tabobject( - 'defaultcompletion', - new moodle_url('/course/defaultcompletion.php', ['id' => $courseid]), - get_string('defaultcompletion', 'completion') - ); + $tabs[] = new tabobject( + 'defaultcompletion', + new moodle_url('/course/defaultcompletion.php', ['id' => $courseid]), + get_string('defaultcompletion', 'completion') + ); + } - $tabs[] = new tabobject( - 'bulkcompletion', - new moodle_url('/course/bulkcompletion.php', ['id' => $courseid]), - get_string('bulkactivitycompletion', 'completion') - ); + if (core_completion\manager::can_edit_bulk_completion($courseid)) { + $tabs[] = new tabobject( + 'bulkcompletion', + new moodle_url('/course/bulkcompletion.php', ['id' => $courseid]), + get_string('bulkactivitycompletion', 'completion') + ); + } - return $this->tabtree($tabs, $page); + if (count($tabs) > 1) { + return $this->tabtree($tabs, $page); + } else { + return ''; + } } @@ -68,4 +76,8 @@ class core_course_bulk_activity_completion_renderer extends plugin_renderer_base return parent::render_from_template('core_course/defaultactivitycompletion', $data); } + public function activities_list($data) { + return parent::render_from_template('core_course/activityinstance', $data); + } + } diff --git a/course/completion.php b/course/completion.php index fdd1bc1f56b..c1967a8af21 100644 --- a/course/completion.php +++ b/course/completion.php @@ -53,7 +53,14 @@ if ($id) { print_error('invalidcourseid'); } require_login($course); - require_capability('moodle/course:update', context_course::instance($course->id)); + $context = context_course::instance($course->id); + if (!has_capability('moodle/course:update', $context)) { + if (core_completion\manager::can_edit_bulk_completion($course)) { + redirect(new moodle_url('/course/bulkcompletion.php', ['id' => $course->id])); + } else { + require_capability('moodle/course:update', $context); + } + } } else { require_login(); diff --git a/course/defaultcompletion.php b/course/defaultcompletion.php index 200911c22db..9f595e0bd2b 100644 --- a/course/defaultcompletion.php +++ b/course/defaultcompletion.php @@ -52,6 +52,7 @@ if ($id) { } // Set up the page. +navigation_node::override_active_url(new moodle_url('/course/completion.php', array('id' => $course->id))); $PAGE->set_course($course); $PAGE->set_url('/course/bulkcompletion.php', array('id' => $course->id)); $PAGE->set_title($course->shortname); diff --git a/course/lib.php b/course/lib.php index 3e99f0bcfab..88996b89fec 100644 --- a/course/lib.php +++ b/course/lib.php @@ -3870,6 +3870,7 @@ function course_get_user_administration_options($course, $context) { $options = new stdClass; $options->update = has_capability('moodle/course:update', $context); + $options->editcompletion = core_completion\manager::can_edit_bulk_completion($course); $options->filters = has_capability('moodle/filter:manage', $context) && count(filter_get_available_in_context($context)) > 0; $options->reports = has_capability('moodle/site:viewreports', $context); diff --git a/course/templates/activityinstance.mustache b/course/templates/activityinstance.mustache index e786d48db61..326e9245c10 100644 --- a/course/templates/activityinstance.mustache +++ b/course/templates/activityinstance.mustache @@ -47,7 +47,9 @@
+ {{#canmanage}} + {{/canmanage}}  {{modname}} diff --git a/course/tests/externallib_test.php b/course/tests/externallib_test.php index 9d0cb22fc25..054f2ce1526 100644 --- a/course/tests/externallib_test.php +++ b/course/tests/externallib_test.php @@ -1942,7 +1942,7 @@ class core_course_externallib_testcase extends externallib_advanced_testcase { $adminoptions->{$option['name']} = $option['available']; } if ($course['id'] == SITEID) { - $this->assertCount(15, $course['options']); + $this->assertCount(16, $course['options']); $this->assertFalse($adminoptions->update); $this->assertFalse($adminoptions->filters); $this->assertFalse($adminoptions->reports); @@ -1957,8 +1957,9 @@ class core_course_externallib_testcase extends externallib_advanced_testcase { $this->assertFalse($adminoptions->publish); $this->assertFalse($adminoptions->reset); $this->assertFalse($adminoptions->roles); + $this->assertFalse($adminoptions->editcompletion); } else { - $this->assertCount(14, $course['options']); + $this->assertCount(15, $course['options']); $this->assertFalse($adminoptions->update); $this->assertFalse($adminoptions->filters); $this->assertFalse($adminoptions->reports); @@ -1973,6 +1974,7 @@ class core_course_externallib_testcase extends externallib_advanced_testcase { $this->assertFalse($adminoptions->publish); $this->assertFalse($adminoptions->reset); $this->assertFalse($adminoptions->roles); + $this->assertFalse($adminoptions->editcompletion); } } } diff --git a/lib/navigationlib.php b/lib/navigationlib.php index 3467ecc29a4..f607bf921ea 100644 --- a/lib/navigationlib.php +++ b/lib/navigationlib.php @@ -4207,14 +4207,16 @@ class settings_navigation extends navigation_node { $coursenode->add($editstring, $editurl, self::TYPE_SETTING, null, 'turneditingonoff', new pix_icon('i/edit', '')); } - if ($adminoptions->update) { + if ($adminoptions->update || $adminoptions->editcompletion) { // Add the course completion settings link if ($CFG->enablecompletion && $course->enablecompletion) { $url = new moodle_url('/course/completion.php', array('id'=>$course->id)); $coursenode->add(get_string('coursecompletion', 'completion'), $url, self::TYPE_SETTING, null, null, new pix_icon('i/settings', '')); } - } else if ($adminoptions->tags) { + } + + if (!$adminoptions->update && $adminoptions->tags) { $url = new moodle_url('/course/tags.php', array('id' => $course->id)); $coursenode->add(get_string('coursetags', 'tag'), $url, self::TYPE_SETTING, null, 'coursetags', new pix_icon('i/settings', '')); }