Skip to content
Browse files

Merge branch 'MDL-30545_23' of git://github.com/timhunt/moodle into M…

…OODLE_23_STABLE
  • Loading branch information...
2 parents dc99b42 + 06661a5 commit 3517201801dcab62e0ea56b0cdbbb9a4d51b5219 @danpoltawski danpoltawski committed
Showing with 347 additions and 17 deletions.
  1. +82 −17 lib/messagelib.php
  2. +157 −0 lib/tests/messagelib_test.php
  3. +2 −0 mod/quiz/lib.php
  4. +106 −0 mod/quiz/tests/generator/lib.php
View
99 lib/messagelib.php
@@ -387,32 +387,97 @@ function message_get_my_providers() {
function message_get_providers_for_user($userid) {
global $DB, $CFG;
- $systemcontext = get_context_instance(CONTEXT_SYSTEM);
-
$providers = get_message_providers();
- // Remove all the providers we aren't allowed to see now
- foreach ($providers as $providerid => $provider) {
- if (!empty($provider->capability)) {
- if (!has_capability($provider->capability, $systemcontext, $userid)) {
- unset($providers[$providerid]); // Not allowed to see this
- continue;
+ // Ensure user is not allowed to configure instantmessage if it is globally disabled.
+ if (!$CFG->messaging) {
+ foreach ($providers as $providerid => $provider) {
+ if ($provider->name == 'instantmessage') {
+ unset($providers[$providerid]);
+ break;
}
}
+ }
- // Ensure user is not allowed to configure instantmessage if it is globally disabled.
- if (!$CFG->messaging && $provider->name == 'instantmessage') {
+ // If the component is an enrolment plugin, check it is enabled
+ foreach ($providers as $providerid => $provider) {
+ list($type, $name) = normalize_component($provider->component);
+ if ($type == 'enrol' && !enrol_is_enabled($name)) {
unset($providers[$providerid]);
+ }
+ }
+
+ // Now we need to check capabilities. We need to eliminate the providers
+ // where the user does not have the corresponding capability anywhere.
+ // Here we deal with the common simple case of the user having the
+ // capability in the system context. That handles $CFG->defaultuserroleid.
+ // For the remaining providers/capabilities, we need to do a more complex
+ // query involving all overrides everywhere.
+ $unsureproviders = array();
+ $unsurecapabilities = array();
+ $systemcontext = context_system::instance();
+ foreach ($providers as $providerid => $provider) {
+ if (empty($provider->capability) || has_capability($provider->capability, $systemcontext, $userid)) {
+ // The provider is relevant to this user.
continue;
}
- // If the component is an enrolment plugin, check it is enabled
- list($type, $name) = normalize_component($provider->component);
- if ($type == 'enrol') {
- if (!enrol_is_enabled($name)) {
- unset($providers[$providerid]);
- continue;
- }
+ $unsureproviders[$providerid] = $provider;
+ $unsurecapabilities[$provider->capability] = 1;
+ unset($providers[$providerid]);
+ }
+
+ if (empty($unsureproviders)) {
+ // More complex checks are not required.
+ return $providers;
+ }
+
+ // Now check the unsure capabilities.
+ list($capcondition, $params) = $DB->get_in_or_equal(
+ array_keys($unsurecapabilities), SQL_PARAMS_NAMED);
+ $params['userid'] = $userid;
+
+ $sql = "SELECT DISTINCT rc.capability, 1
+
+ FROM {role_assignments} ra
+ JOIN {context} actx ON actx.id = ra.contextid
+ JOIN {role_capabilities} rc ON rc.roleid = ra.roleid
+ JOIN {context} cctx ON cctx.id = rc.contextid
+
+ WHERE ra.userid = :userid
+ AND rc.capability $capcondition
+ AND rc.permission > 0
+ AND (CONCAT(actx.path, '/') LIKE CONCAT(cctx.path, '/%') OR CONCAT(cctx.path, '/') LIKE CONCAT(actx.path, '/%'))";
+
+ if (!empty($CFG->defaultfrontpageroleid)) {
+ $frontpagecontext = context_course::instance(SITEID);
+
+ list($capcondition2, $params2) = $DB->get_in_or_equal(
+ array_keys($unsurecapabilities), SQL_PARAMS_NAMED);
+ $params = array_merge($params, $params2);
+ $params['frontpageroleid'] = $CFG->defaultfrontpageroleid;
+ $params['frontpagepathpattern'] = $frontpagecontext->path . '/';
+
+ $sql .= "
+ UNION DISTINCT
+
+ SELECT DISTINCT rc.capability, 1
+
+ FROM {role_capabilities} rc
+ JOIN {context} cctx ON cctx.id = rc.contextid
+
+ WHERE rc.roleid = :frontpageroleid
+ AND rc.capability $capcondition2
+ AND rc.permission > 0
+ AND CONCAT(cctx.path, '/') LIKE :frontpagepathpattern";
+ }
+
+ $relevantcapabilities = $DB->get_records_sql_menu($sql, $params);
+
+ // Add back any providers based on the detailed capability check.
+ foreach ($unsureproviders as $providerid => $provider) {
+ if (array_key_exists($provider->capability, $relevantcapabilities)) {
+ $providers[$providerid] = $provider;
}
}
View
157 lib/tests/messagelib_test.php
@@ -0,0 +1,157 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Tests for messagelib.php.
+ *
+ * @package core_message
+ * @copyright 2012 The Open Universtiy
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+class messagelib_testcase extends advanced_testcase {
+
+ public function test_message_get_providers_for_user() {
+ global $CFG, $DB;
+
+ $this->resetAfterTest(true);
+
+ $generator = $this->getDataGenerator();
+
+ // Create a course category and course
+ $cat = $generator->create_category(array('parent' => 0));
+ $course = $generator->create_course(array('category' => $cat->id));
+ $quiz = $generator->create_module('quiz', array('course' => $course->id));
+ $user = $generator->create_user();
+
+ $coursecontext = context_course::instance($course->id);
+ $quizcontext = context_module::instance($quiz->cmid);
+ $frontpagecontext = context_course::instance(SITEID);
+
+ $studentrole = $DB->get_record('role', array('shortname' => 'student'));
+
+ // The user is a student in a course, and has the capability for quiz
+ // confirmation emails in one quiz in that course.
+ role_assign($studentrole->id, $user->id, $coursecontext->id);
+ assign_capability('mod/quiz:emailconfirmsubmission', CAP_ALLOW, $studentrole->id, $quizcontext->id);
+
+ // Give this message type to the front page role.
+ assign_capability('mod/quiz:emailwarnoverdue', CAP_ALLOW, $CFG->defaultfrontpageroleid, $frontpagecontext->id);
+
+ $providers = message_get_providers_for_user($user->id);
+ $this->assertTrue($this->message_type_present('mod_forum', 'posts', $providers));
+ $this->assertTrue($this->message_type_present('mod_quiz', 'confirmation', $providers));
+ $this->assertTrue($this->message_type_present('mod_quiz', 'attempt_overdue', $providers));
+ $this->assertFalse($this->message_type_present('mod_quiz', 'submission', $providers));
+
+ // A user is a student in a different course, they should not get confirmation.
+ $course2 = $generator->create_course(array('category' => $cat->id));
+ $user2 = $generator->create_user();
+ $coursecontext2 = context_course::instance($course2->id);
+ role_assign($studentrole->id, $user2->id, $coursecontext2->id);
+ accesslib_clear_all_caches_for_unit_testing();
+ $providers = message_get_providers_for_user($user2->id);
+ $this->assertTrue($this->message_type_present('mod_forum', 'posts', $providers));
+ $this->assertFalse($this->message_type_present('mod_quiz', 'confirmation', $providers));
+
+ // Now remove the frontpage role id, and attempt_overdue message should go away.
+ unset_config('defaultfrontpageroleid');
+ accesslib_clear_all_caches_for_unit_testing();
+
+ $providers = message_get_providers_for_user($user->id);
+ $this->assertTrue($this->message_type_present('mod_quiz', 'confirmation', $providers));
+ $this->assertFalse($this->message_type_present('mod_quiz', 'attempt_overdue', $providers));
+ $this->assertFalse($this->message_type_present('mod_quiz', 'submission', $providers));
+ }
+
+ public function test_message_get_providers_for_user_more() {
+ global $DB;
+
+ $this->resetAfterTest(true);
+
+ // Create a course
+ $course = $this->getDataGenerator()->create_course();
+ $coursecontext = context_course::instance($course->id);
+
+ // It would probably be better to use a quiz instance as it has capability controlled messages
+ // however mod_quiz doesn't have a data generator
+ // Instead we're going to use backup notifications and give and take away the capability at various levels
+ $assign = $this->getDataGenerator()->create_module('assign', array('course'=>$course->id));
+ $modulecontext = context_module::instance($assign->id);
+
+ // Create and enrol a teacher
+ $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
+ $teacher = $this->getDataGenerator()->create_user();
+ role_assign($teacherrole->id, $teacher->id, $coursecontext);
+ $enrolplugin = enrol_get_plugin('manual');
+ $enrolplugin->add_instance($course);
+ $enrolinstances = enrol_get_instances($course->id, false);
+ foreach ($enrolinstances as $enrolinstance) {
+ if ($enrolinstance->enrol === 'manual') {
+ break;
+ }
+ }
+ $enrolplugin->enrol_user($enrolinstance, $teacher->id);
+
+ // Make the teacher the current user
+ $this->setUser($teacher);
+
+ // Teacher shouldn't have the required capability so they shouldn't be able to see the backup message
+ $this->assertFalse(has_capability('moodle/site:config', $modulecontext));
+ $providers = message_get_providers_for_user($teacher->id);
+ $this->assertFalse($this->message_type_present('moodle', 'backup', $providers));
+
+ // Give the user the required capability in an activity module
+ // They should now be able to see the backup message
+ assign_capability('moodle/site:config', CAP_ALLOW, $teacherrole->id, $modulecontext->id, true);
+ accesslib_clear_all_caches_for_unit_testing();
+ $modulecontext = context_module::instance($assign->id);
+ $this->assertTrue(has_capability('moodle/site:config', $modulecontext));
+
+ $providers = message_get_providers_for_user($teacher->id);
+ $this->assertTrue($this->message_type_present('moodle', 'backup', $providers));
+
+ // Prohibit the capability for the user at the course level
+ // This overrules the CAP_ALLOW at the module level
+ // They should not be able to see the backup message
+ assign_capability('moodle/site:config', CAP_PROHIBIT, $teacherrole->id, $coursecontext->id, true);
+ accesslib_clear_all_caches_for_unit_testing();
+ $modulecontext = context_module::instance($assign->id);
+ $this->assertFalse(has_capability('moodle/site:config', $modulecontext));
+
+ $providers = message_get_providers_for_user($teacher->id);
+ // Actually, handling PROHIBITs would be too expensive. We do not
+ // care if users with PROHIBITs see a few more preferences than they should.
+ // $this->assertFalse($this->message_type_present('moodle', 'backup', $providers));
+ }
+
+ /**
+ * Is a particular message type in the list of message types.
+ * @param string $name a message name.
+ * @param array $providers as returned by message_get_providers_for_user.
+ * @return bool whether the message type is present.
+ */
+ protected function message_type_present($component, $name, $providers) {
+ foreach ($providers as $provider) {
+ if ($provider->component == $component && $provider->name == $name) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
View
2 mod/quiz/lib.php
@@ -1024,6 +1024,8 @@ function quiz_process_options($quiz) {
$quiz->feedbackboundaries[-1] = $quiz->grade + 1;
$quiz->feedbackboundaries[$numboundaries] = 0;
$quiz->feedbackboundarycount = $numboundaries;
+ } else {
+ $quiz->feedbackboundarycount = -1;
}
// Combing the individual settings into the review columns.
View
106 mod/quiz/tests/generator/lib.php
@@ -0,0 +1,106 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Quiz module test data generator class
+ *
+ * @package mod_quiz
+ * @copyright 2012 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class mod_quiz_generator extends phpunit_module_generator {
+
+ /**
+ * Create new quiz module instance.
+ * @param array|stdClass $record
+ * @param array $options (mostly course_module properties)
+ * @return stdClass activity record with extra cmid field
+ */
+ public function create_instance($record = null, array $options = null) {
+ global $CFG;
+ require_once("$CFG->dirroot/mod/quiz/locallib.php");
+
+ $this->instancecount++;
+ $i = $this->instancecount;
+
+ $record = (object)(array)$record;
+ $options = (array)$options;
+
+ if (empty($record->course)) {
+ throw new coding_exception('module generator requires $record->course');
+ }
+ if (isset($options['idnumber'])) {
+ $record->cmidnumber = $options['idnumber'];
+ } else {
+ $record->cmidnumber = '';
+ }
+
+ $alwaysvisible = mod_quiz_display_options::DURING | mod_quiz_display_options::IMMEDIATELY_AFTER |
+ mod_quiz_display_options::LATER_WHILE_OPEN | mod_quiz_display_options::AFTER_CLOSE;
+
+ $defaultquizsettings = array(
+ 'name' => get_string('pluginname', 'data').' '.$i,
+ 'intro' => 'Test quiz ' . $i,
+ 'introformat' => FORMAT_MOODLE,
+ 'timeopen' => 0,
+ 'timeclose' => 0,
+ 'preferredbehaviour' => 'deferredfeedback',
+ 'attempts' => 0,
+ 'attemptonlast' => 0,
+ 'grademethod' => QUIZ_GRADEHIGHEST,
+ 'decimalpoints' => 2,
+ 'questiondecimalpoints' => -1,
+ 'reviewattempt' => $alwaysvisible,
+ 'reviewcorrectness' => $alwaysvisible,
+ 'reviewmarks' => $alwaysvisible,
+ 'reviewspecificfeedback' => $alwaysvisible,
+ 'reviewgeneralfeedback' => $alwaysvisible,
+ 'reviewrightanswer' => $alwaysvisible,
+ 'reviewoverallfeedback' => $alwaysvisible,
+ 'questionsperpage' => 1,
+ 'shufflequestions' => 0,
+ 'shuffleanswers' => 1,
+ 'questions' => '',
+ 'sumgrades' => 0,
+ 'grade' => 0,
+ 'timecreated' => time(),
+ 'timemodified' => time(),
+ 'timelimit' => 0,
+ 'overduehandling' => 'autoabandon',
+ 'graceperiod' => 86400,
+ 'quizpassword' => '',
+ 'subnet' => '',
+ 'browsersecurity' => '',
+ 'delay1' => 0,
+ 'delay2' => 0,
+ 'showuserpicture' => 0,
+ 'showblocks' => 0,
+ 'navmethod' => QUIZ_NAVMETHOD_FREE,
+ );
+
+ foreach ($defaultquizsettings as $name => $value) {
+ if (!isset($record->{$name})) {
+ $record->{$name} = $value;
+ }
+ }
+
+ $record->coursemodule = $this->precreate_course_module($record->course, $options);
+ $id = quiz_add_instance($record);
+ return $this->post_add_instance($id, $record->coursemodule);
+ }
+}

0 comments on commit 3517201

Please sign in to comment.
Something went wrong with that request. Please try again.