Permalink
Browse files

MDL-32240 quiz editing: check permissions questions are added.

We also check permissions when removing questions from the quiz, so that
we do not let users make mistakes they cannot immediately undo.

Conflicts:

	mod/quiz/editlib.php
  • Loading branch information...
1 parent 0f83dd1 commit 016660ca422928ec8b4145119ca2dc94014261f0 @timhunt timhunt committed Mar 28, 2012
View
26 lib/questionlib.php
@@ -1058,10 +1058,13 @@ function question_make_default_categories($contexts) {
} else {
$category = question_get_default_category($context->id);
}
- if ($preferredlevels[$context->contextlevel] > $preferredness && has_any_capability(
- array('moodle/question:usemine', 'moodle/question:useall'), $context)) {
+ $thispreferredness = $preferredlevels[$context->contextlevel];
+ if (has_any_capability(array('moodle/question:usemine', 'moodle/question:useall'), $context)) {
+ $thispreferredness += 10;
+ }
+ if ($thispreferredness > $preferredness) {
$toreturn = $category;
- $preferredness = $preferredlevels[$context->contextlevel];
+ $preferredness = $thispreferredness;
}
}
@@ -1583,6 +1586,23 @@ public function having_one_edit_tab_cap($tabname) {
return $this->having_one_cap(self::$caps[$tabname]);
}
/**
+ * @return those contexts where a user can add a question and then use it.
+ */
+ public function having_add_and_use() {
+ $contextswithcap = array();
+ foreach ($this->allcontexts as $context) {
+ if (!has_capability('moodle/question:add', $context)) {
+ continue;
+ }
+ if (!has_any_capability(array('moodle/question:useall', 'moodle/question:usemine'), $context)) {
+ continue;
+ }
+ $contextswithcap[] = $context;
+ }
+ return $contextswithcap;
+ }
+
+ /**
* Has at least one parent context got the cap $cap?
*
* @param string $cap capability
View
6 mod/quiz/addrandom.php
@@ -44,8 +44,12 @@
if (!$course = $DB->get_record('course', array('id' => $quiz->course))) {
print_error('invalidcourseid');
}
-//you need mod/quiz:manage in addition to question capabilities to access this page.
+// You need mod/quiz:manage in addition to question capabilities to access this page.
+// You also need the moodle/question:useall capability somewhere.
require_capability('mod/quiz:manage', $contexts->lowest());
+if (!$contexts->having_cap('moodle/question:useall')) {
+ print_error('nopermissions', '', '', 'use');
+}
$PAGE->set_url($thispageurl);
View
5 mod/quiz/addrandomform.php
@@ -42,13 +42,14 @@ protected function definition() {
$mform =& $this->_form;
$contexts = $this->_customdata;
+ $usablecontexts = $contexts->having_cap('moodle/question:useall');
//--------------------------------------------------------------------------------
$mform->addElement('header', 'categoryheader',
get_string('randomfromexistingcategory', 'quiz'));
$mform->addElement('questioncategory', 'category', get_string('category'),
- array('contexts' => $contexts->all(), 'top' => false));
+ array('contexts' => $usablecontexts, 'top' => false));
$mform->addElement('checkbox', 'includesubcategories', '', get_string('recurse', 'quiz'));
@@ -62,7 +63,7 @@ protected function definition() {
$mform->setType('name', PARAM_MULTILANG);
$mform->addElement('questioncategory', 'parent', get_string('parentcategory', 'question'),
- array('contexts' => $contexts->all(), 'top' => true));
+ array('contexts' => $usablecontexts, 'top' => true));
$mform->addHelpButton('parent', 'parentcategory', 'question');
$mform->addElement('submit', 'newcategory',
View
35 mod/quiz/edit.php
@@ -55,14 +55,15 @@
*/
function module_specific_buttons($cmid, $cmoptions) {
global $OUTPUT;
+ $params = array(
+ 'type' => 'submit',
+ 'name' => 'add',
+ 'value' => $OUTPUT->larrow() . ' ' . get_string('addtoquiz', 'quiz'),
+ );
if ($cmoptions->hasattempts) {
- $disabled = 'disabled="disabled" ';
- } else {
- $disabled = '';
+ $params['disabled'] = 'disabled';
}
- $out = '<input type="submit" name="add" value="' . $OUTPUT->larrow() . ' ' .
- get_string('addtoquiz', 'quiz') . '" ' . $disabled . "/>\n";
- return $out;
+ return html_writer::empty_tag('input', $params);
}
/**
@@ -137,7 +138,9 @@ function module_specific_controls($totalnumber, $recurse, $category, $cmid, $cmo
$quiz_reordertool = get_user_preferences('quiz_reordertab', 0);
}
-//will be set further down in the code
+$canaddrandom = $contexts->have_cap('moodle/question:useall');
+$canaddquestion = (bool) $contexts->having_add_and_use();
+
$quizhasattempts = quiz_has_attempts($quiz->id);
$PAGE->set_url($thispageurl);
@@ -211,6 +214,7 @@ function module_specific_controls($totalnumber, $recurse, $category, $cmid, $cmo
if (($addquestion = optional_param('addquestion', 0, PARAM_INT)) && confirm_sesskey()) {
// Add a single question to the current quiz
+ quiz_require_question_use($addquestion);
$addonpage = optional_param('addonpage', 0, PARAM_INT);
quiz_add_quiz_question($addquestion, $quiz, $addonpage);
quiz_delete_previews($quiz);
@@ -225,6 +229,7 @@ function module_specific_controls($totalnumber, $recurse, $category, $cmid, $cmo
foreach ($rawdata as $key => $value) { // Parse input for question ids
if (preg_match('!^q([0-9]+)$!', $key, $matches)) {
$key = $matches[1];
+ quiz_require_question_use($key);
quiz_add_quiz_question($key, $quiz);
}
}
@@ -273,7 +278,11 @@ function module_specific_controls($totalnumber, $recurse, $category, $cmid, $cmo
}
$remove = optional_param('remove', false, PARAM_INT);
-if (($remove = optional_param('remove', false, PARAM_INT)) && confirm_sesskey()) {
+if ($remove && confirm_sesskey()) {
+ // Remove a question from the quiz.
+ // We require the user to have the 'use' capability on the question,
+ // so that then can add it back if they remove the wrong one by mistake.
+ quiz_require_question_use($remove);
quiz_remove_question($quiz, $remove);
quiz_delete_previews($quiz);
quiz_update_sumgrades($quiz);
@@ -283,7 +292,9 @@ function module_specific_controls($totalnumber, $recurse, $category, $cmid, $cmo
if (optional_param('quizdeleteselected', false, PARAM_BOOL) &&
!empty($selectedquestionids) && confirm_sesskey()) {
foreach ($selectedquestionids as $questionid) {
- quiz_remove_question($quiz, $questionid);
+ if (quiz_has_question_use($questionid)) {
+ quiz_remove_question($quiz, $questionid);
+ }
}
quiz_delete_previews($quiz);
quiz_update_sumgrades($quiz);
@@ -548,14 +559,14 @@ function module_specific_controls($totalnumber, $recurse, $category, $cmid, $cmo
echo '<div class="editq">';
}
-quiz_print_question_list($quiz, $thispageurl, true,
- $quiz_reordertool, $quiz_qbanktool, $quizhasattempts, $defaultcategoryobj);
+quiz_print_question_list($quiz, $thispageurl, true, $quiz_reordertool, $quiz_qbanktool,
+ $quizhasattempts, $defaultcategoryobj, $canaddquestion, $canaddrandom);
echo '</div>';
// Close <div class="quizcontents">:
echo '</div>';
-if (!$quiz_reordertool) {
+if (!$quiz_reordertool && $canaddrandom) {
$randomform = new quiz_add_random_form(new moodle_url('/mod/quiz/addrandom.php'), $contexts);
$randomform->set_data(array(
'category' => $pagevars['cat'],
View
79 mod/quiz/editlib.php
@@ -36,6 +36,28 @@
define('NUM_QS_TO_SHOW_IN_RANDOM', 3);
/**
+ * Verify that the question exists, and the user has permission to use it.
+ * Does not return. Throws an exception if the question cannot be used.
+ * @param int $questionid The id of the question.
+ */
+function quiz_require_question_use($questionid) {
+ global $DB;
+ $question = $DB->get_record('question', array('id' => $questionid), '*', MUST_EXIST);
+ question_require_capability_on($question, 'use');
+}
+
+/**
+ * Verify that the question exists, and the user has permission to use it.
+ * @param int $questionid The id of the question.
+ * @return bool whether the user can use this question.
+ */
+function quiz_has_question_use($questionid) {
+ global $DB;
+ $question = $DB->get_record('question', array('id' => $questionid), '*', MUST_EXIST);
+ return question_has_capability_on($question, 'use');
+}
+
+/**
* Remove a question from a quiz
* @param object $quiz the quiz object.
* @param int $questionid The id of the question to be deleted.
@@ -333,19 +355,21 @@ function quiz_move_question_down($layout, $questionid) {
* Prints a list of quiz questions for the edit.php main view for edit
* ($reordertool = false) and order and paging ($reordertool = true) tabs
*
- * @return int sum of maximum grades
* @param object $quiz This is not the standard quiz object used elsewhere but
* it contains the quiz layout in $quiz->questions and the grades in
* $quiz->grades
- * @param object $pageurl The url of the current page with the parameters required
+ * @param moodle_url $pageurl The url of the current page with the parameters required
* for links returning to the current page, as a moodle_url object
* @param bool $allowdelete Indicates whether the delete icons should be displayed
* @param bool $reordertool Indicates whether the reorder tool should be displayed
* @param bool $quiz_qbanktool Indicates whether the question bank should be displayed
* @param bool $hasattempts Indicates whether the quiz has attempts
+ * @param object $defaultcategoryobj
+ * @param bool $canaddquestion is the user able to add and use questions anywere?
+ * @param bool $canaddrandom is the user able to add random questions anywere?
*/
function quiz_print_question_list($quiz, $pageurl, $allowdelete, $reordertool,
- $quiz_qbanktool, $hasattempts, $defaultcategoryobj) {
+ $quiz_qbanktool, $hasattempts, $defaultcategoryobj, $canaddquestion, $canaddrandom) {
global $CFG, $DB, $OUTPUT;
$strorder = get_string('order');
$strquestionname = get_string('questionname', 'quiz');
@@ -667,7 +691,7 @@ function quiz_print_question_list($quiz, $pageurl, $allowdelete, $reordertool,
if (!$reordertool && !($quiz->shufflequestions &&
$count < $questiontotalcount - 1)) {
quiz_print_pagecontrols($quiz, $pageurl, $pagecount,
- $hasattempts, $defaultcategoryobj);
+ $hasattempts, $defaultcategoryobj, $canaddquestion, $canaddrandom);
} else if ($count < $questiontotalcount - 1) {
//do not include the last page break for reordering
//to avoid creating a new extra page in the end
@@ -703,12 +727,19 @@ function quiz_print_question_list($quiz, $pageurl, $allowdelete, $reordertool,
* Print all the controls for adding questions directly into the
* specific page in the edit tab of edit.php
*
- * @param unknown_type $quiz
- * @param unknown_type $pageurl
- * @param unknown_type $page
- * @param unknown_type $hasattempts
+ * @param object $quiz This is not the standard quiz object used elsewhere but
+ * it contains the quiz layout in $quiz->questions and the grades in
+ * $quiz->grades
+ * @param moodle_url $pageurl The url of the current page with the parameters required
+ * for links returning to the current page, as a moodle_url object
+ * @param int $page the current page number.
+ * @param bool $hasattempts Indicates whether the quiz has attempts
+ * @param object $defaultcategoryobj
+ * @param bool $canaddquestion is the user able to add and use questions anywere?
+ * @param bool $canaddrandom is the user able to add random questions anywere?
*/
-function quiz_print_pagecontrols($quiz, $pageurl, $page, $hasattempts, $defaultcategoryobj) {
+function quiz_print_pagecontrols($quiz, $pageurl, $page, $hasattempts,
+ $defaultcategoryobj, $canaddquestion, $canaddrandom) {
global $CFG, $OUTPUT;
static $randombuttoncount = 0;
$randombuttoncount++;
@@ -724,22 +755,25 @@ function quiz_print_pagecontrols($quiz, $pageurl, $page, $hasattempts, $defaultc
$defaultcategoryid = $defaultcategoryobj->id;
}
- // Create the url the question page will return to
- $returnurladdtoquiz = new moodle_url($pageurl, array('addonpage' => $page));
-
- // Print a button linking to the choose question type page.
- $returnurladdtoquiz = str_replace($CFG->wwwroot, '', $returnurladdtoquiz->out(false));
- $newquestionparams = array('returnurl' => $returnurladdtoquiz,
- 'cmid' => $quiz->cmid, 'appendqnumstring' => 'addquestion');
- create_new_question_button($defaultcategoryid, $newquestionparams,
- get_string('addaquestion', 'quiz'),
- get_string('createquestionandadd', 'quiz'), $hasattempts);
+ if ($canaddquestion) {
+ // Create the url the question page will return to
+ $returnurladdtoquiz = new moodle_url($pageurl, array('addonpage' => $page));
+
+ // Print a button linking to the choose question type page.
+ $returnurladdtoquiz = str_replace($CFG->wwwroot, '', $returnurladdtoquiz->out(false));
+ $newquestionparams = array('returnurl' => $returnurladdtoquiz,
+ 'cmid' => $quiz->cmid, 'appendqnumstring' => 'addquestion');
+ create_new_question_button($defaultcategoryid, $newquestionparams,
+ get_string('addaquestion', 'quiz'),
+ get_string('createquestionandadd', 'quiz'), $hasattempts);
+ }
if ($hasattempts) {
$disabled = 'disabled="disabled"';
} else {
$disabled = '';
}
+ if ($canaddrandom) {
?>
<div class="singlebutton">
<form class="randomquestionform" action="<?php echo $CFG->wwwroot;
@@ -760,8 +794,8 @@ function quiz_print_pagecontrols($quiz, $pageurl, $page, $hasattempts, $defaultc
</div>
</form>
</div>
- <?php echo $OUTPUT->help_icon('addarandomquestion', 'quiz'); ?>
- <?php
+ <?php echo $OUTPUT->help_icon('addarandomquestion', 'quiz');
+ }
echo "\n</div>";
}
@@ -1021,6 +1055,9 @@ public function get_name() {
}
protected function display_content($question, $rowclasses) {
+ if (!question_has_capability_on($question, 'use')) {
+ return;
+ }
// for RTL languages: switch right and left arrows
if (right_to_left()) {
$movearrow = 't/removeright';
View
1 question/editlib.php
@@ -406,6 +406,7 @@ protected function display_content($question, $rowclasses) {
echo '<input title="' . $this->strselect . '" type="checkbox" name="q' .
$question->id . '" id="checkq' . $question->id . '" value="1"/>';
if ($this->firstrow) {
+ $PAGE->requires->js('/question/qbank.js');
$PAGE->requires->js_function_call('question_bank.init_checkbox_column', array(get_string('selectall'),
get_string('deselectall'), 'checkq' . $question->id));
$this->firstrow = false;
View
1 question/question.php
@@ -188,6 +188,7 @@
$formeditable = true;
require_capability('moodle/question:add', $categorycontext);
}
+$question->formoptions->mustbeusable = (bool) $appendqnumstring;
// Validate the question type.
$PAGE->set_pagetype('question-type-' . $question->qtype);
View
8 question/type/edit_question_form.php
@@ -129,9 +129,15 @@ protected function definition() {
$mform->addElement('header', 'generalheader', get_string("general", 'form'));
if (!isset($this->question->id)) {
+ if (!empty($this->question->formoptions->mustbeusable)) {
+ $contexts = $this->contexts->having_add_and_use();
+ } else {
+ $contexts = $this->contexts->having_cap('moodle/question:add');
+ }
+
// Adding question
$mform->addElement('questioncategory', 'category', get_string('category', 'question'),
- array('contexts' => $this->contexts->having_cap('moodle/question:add')));
+ array('contexts' => $contexts));
} else if (!($this->question->formoptions->canmove ||
$this->question->formoptions->cansaveasnew)) {
// Editing question with no permission to move from category.

0 comments on commit 016660c

Please sign in to comment.