Skip to content

Commit

Permalink
Merge branch 'MDL-30545_23' of git://github.com/timhunt/moodle into M…
Browse files Browse the repository at this point in the history
…OODLE_23_STABLE
  • Loading branch information
danpoltawski committed Nov 13, 2012
2 parents dc99b42 + 06661a5 commit 3517201
Show file tree
Hide file tree
Showing 4 changed files with 347 additions and 17 deletions.
99 changes: 82 additions & 17 deletions lib/messagelib.php
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -387,32 +387,97 @@ function message_get_my_providers() {
function message_get_providers_for_user($userid) { function message_get_providers_for_user($userid) {
global $DB, $CFG; global $DB, $CFG;


$systemcontext = get_context_instance(CONTEXT_SYSTEM);

$providers = get_message_providers(); $providers = get_message_providers();


// Remove all the providers we aren't allowed to see now // Ensure user is not allowed to configure instantmessage if it is globally disabled.
foreach ($providers as $providerid => $provider) { if (!$CFG->messaging) {
if (!empty($provider->capability)) { foreach ($providers as $providerid => $provider) {
if (!has_capability($provider->capability, $systemcontext, $userid)) { if ($provider->name == 'instantmessage') {
unset($providers[$providerid]); // Not allowed to see this unset($providers[$providerid]);
continue; break;
} }
} }
}


// Ensure user is not allowed to configure instantmessage if it is globally disabled. // If the component is an enrolment plugin, check it is enabled
if (!$CFG->messaging && $provider->name == 'instantmessage') { foreach ($providers as $providerid => $provider) {
list($type, $name) = normalize_component($provider->component);
if ($type == 'enrol' && !enrol_is_enabled($name)) {
unset($providers[$providerid]); 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; continue;
} }


// If the component is an enrolment plugin, check it is enabled $unsureproviders[$providerid] = $provider;
list($type, $name) = normalize_component($provider->component); $unsurecapabilities[$provider->capability] = 1;
if ($type == 'enrol') { unset($providers[$providerid]);
if (!enrol_is_enabled($name)) { }
unset($providers[$providerid]);
continue; 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;
} }
} }


Expand Down
157 changes: 157 additions & 0 deletions lib/tests/messagelib_test.php
Original file line number Original file line Diff line number Diff line change
@@ -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;
}
}
2 changes: 2 additions & 0 deletions mod/quiz/lib.php
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -1024,6 +1024,8 @@ function quiz_process_options($quiz) {
$quiz->feedbackboundaries[-1] = $quiz->grade + 1; $quiz->feedbackboundaries[-1] = $quiz->grade + 1;
$quiz->feedbackboundaries[$numboundaries] = 0; $quiz->feedbackboundaries[$numboundaries] = 0;
$quiz->feedbackboundarycount = $numboundaries; $quiz->feedbackboundarycount = $numboundaries;
} else {
$quiz->feedbackboundarycount = -1;
} }


// Combing the individual settings into the review columns. // Combing the individual settings into the review columns.
Expand Down
106 changes: 106 additions & 0 deletions mod/quiz/tests/generator/lib.php
Original file line number Original file line Diff line number Diff line change
@@ -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.