Commit | Line | Data |
---|---|---|
aeb15530 | 1 | <?php |
d3603157 TH |
2 | // This file is part of Moodle - http://moodle.org/ |
3 | // | |
4 | // Moodle is free software: you can redistribute it and/or modify | |
5 | // it under the terms of the GNU General Public License as published by | |
6 | // the Free Software Foundation, either version 3 of the License, or | |
7 | // (at your option) any later version. | |
8 | // | |
9 | // Moodle is distributed in the hope that it will be useful, | |
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | // GNU General Public License for more details. | |
13 | // | |
14 | // You should have received a copy of the GNU General Public License | |
15 | // along with Moodle. If not, see <http://www.gnu.org/licenses/>. | |
16 | ||
17 | /** | |
18 | * A class for representing question categories. | |
19 | * | |
20 | * @package moodlecore | |
21 | * @subpackage questionbank | |
22 | * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} | |
23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
24 | */ | |
25 | ||
516cf3eb | 26 | |
a17b297d TH |
27 | defined('MOODLE_INTERNAL') || die(); |
28 | ||
516cf3eb | 29 | // number of categories to display on page |
d3603157 | 30 | define('QUESTION_PAGE_LENGTH', 25); |
dac786f3 | 31 | |
94dbfb3a TH |
32 | require_once($CFG->libdir . '/listlib.php'); |
33 | require_once($CFG->dirroot . '/question/category_form.php'); | |
017bc1d9 TH |
34 | require_once($CFG->dirroot . '/question/move_form.php'); |
35 | ||
dac786f3 | 36 | |
017bc1d9 TH |
37 | /** |
38 | * Class representing a list of question categories | |
39 | * | |
40 | * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} | |
41 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
42 | */ | |
dac786f3 | 43 | class question_category_list extends moodle_list { |
f34488b2 | 44 | public $table = "question_categories"; |
45 | public $listitemclassname = 'question_category_list_item'; | |
271e6dec | 46 | /** |
47 | * @var reference to list displayed below this one. | |
48 | */ | |
f34488b2 | 49 | public $nextlist = null; |
271e6dec | 50 | /** |
51 | * @var reference to list displayed above this one. | |
52 | */ | |
f34488b2 | 53 | public $lastlist = null; |
271e6dec | 54 | |
f34488b2 | 55 | public $context = null; |
c65921d4 | 56 | public $sortby = 'parent, sortorder, name'; |
271e6dec | 57 | |
20d89122 TH |
58 | public function __construct($type='ul', $attributes='', $editable = false, $pageurl=null, $page = 0, $pageparamname = 'page', $itemsperpage = 20, $context = null){ |
59 | parent::__construct('ul', '', $editable, $pageurl, $page, 'cpage', $itemsperpage); | |
271e6dec | 60 | $this->context = $context; |
61 | } | |
986effb6 | 62 | |
f34488b2 | 63 | public function get_records() { |
271e6dec | 64 | $this->records = get_categories_for_contexts($this->context->id, $this->sortby); |
65 | } | |
18d6ff78 SR |
66 | |
67 | /** | |
68 | * Returns the highest category id that the $item can have as its parent. | |
69 | * Note: question categories cannot go higher than the TOP category. | |
70 | * | |
71 | * @param list_item $item The item which its top level parent is going to be returned. | |
72 | * @return int | |
73 | */ | |
74 | public function get_top_level_parent_id($item) { | |
75 | // Put the item at the highest level it can go. | |
76 | $topcategory = question_get_top_category($item->item->contextid, true); | |
77 | return $topcategory->id; | |
78 | } | |
93e435b9 | 79 | |
4ca60a56 V |
80 | /** |
81 | * process any actions. | |
82 | * | |
83 | * @param integer $left id of item to move left | |
84 | * @param integer $right id of item to move right | |
85 | * @param integer $moveup id of item to move up | |
86 | * @param integer $movedown id of item to move down | |
87 | * @return void | |
88 | * @throws coding_exception | |
89 | */ | |
93e435b9 | 90 | public function process_actions($left, $right, $moveup, $movedown) { |
4ca60a56 | 91 | $category = new stdClass(); |
93e435b9 SB |
92 | if (!empty($left)) { |
93 | // Moved Left (In to another category). | |
4ca60a56 V |
94 | $category->id = $left; |
95 | $category->contextid = $this->context->id; | |
96 | $event = \core\event\question_category_moved::create_from_question_category_instance($category); | |
93e435b9 SB |
97 | $event->trigger(); |
98 | } else if (!empty($right)) { | |
99 | // Moved Right (Out of the current category). | |
4ca60a56 V |
100 | $category->id = $right; |
101 | $category->contextid = $this->context->id; | |
102 | $event = \core\event\question_category_moved::create_from_question_category_instance($category); | |
93e435b9 | 103 | $event->trigger(); |
4ca60a56 | 104 | } |
93e435b9 SB |
105 | parent::process_actions($left, $right, $moveup, $movedown); |
106 | } | |
dac786f3 | 107 | } |
108 | ||
017bc1d9 TH |
109 | /** |
110 | * An item in a list of question categories. | |
111 | * | |
112 | * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} | |
113 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
114 | */ | |
dac786f3 | 115 | class question_category_list_item extends list_item { |
e2bb3c92 | 116 | public function set_icon_html($first, $last, $lastitem){ |
271e6dec | 117 | global $CFG; |
118 | $category = $this->item; | |
8afba50b PS |
119 | $url = new moodle_url('/question/category.php', ($this->parentlist->pageurl->params() + array('edit'=>$category->id))); |
120 | $this->icons['edit']= $this->image_icon(get_string('editthiscategory', 'question'), $url, 'edit'); | |
271e6dec | 121 | parent::set_icon_html($first, $last, $lastitem); |
122 | $toplevel = ($this->parentlist->parentitem === null);//this is a top level item | |
123 | if (($this->parentlist->nextlist !== null) && $last && $toplevel && (count($this->parentlist->items)>1)){ | |
8afba50b | 124 | $url = new moodle_url($this->parentlist->pageurl, array('movedowncontext'=>$this->id, 'tocontext'=>$this->parentlist->nextlist->context->id, 'sesskey'=>sesskey())); |
f34488b2 | 125 | $this->icons['down'] = $this->image_icon( |
329846f1 | 126 | get_string('shareincontext', 'question', $this->parentlist->nextlist->context->get_context_name()), $url, 'down'); |
271e6dec | 127 | } |
128 | if (($this->parentlist->lastlist !== null) && $first && $toplevel && (count($this->parentlist->items)>1)){ | |
8afba50b | 129 | $url = new moodle_url($this->parentlist->pageurl, array('moveupcontext'=>$this->id, 'tocontext'=>$this->parentlist->lastlist->context->id, 'sesskey'=>sesskey())); |
f34488b2 | 130 | $this->icons['up'] = $this->image_icon( |
329846f1 | 131 | get_string('shareincontext', 'question', $this->parentlist->lastlist->context->get_context_name()), $url, 'up'); |
271e6dec | 132 | } |
133 | } | |
20d89122 | 134 | |
f34488b2 | 135 | public function item_html($extraargs = array()){ |
42d5737a | 136 | global $CFG, $OUTPUT; |
dac786f3 | 137 | $str = $extraargs['str']; |
138 | $category = $this->item; | |
139 | ||
5e8a85aa | 140 | $editqestions = get_string('editquestions', 'question'); |
dac786f3 | 141 | |
8009810e TH |
142 | // Each section adds html to be displayed as part of this list item. |
143 | $questionbankurl = new moodle_url('/question/edit.php', $this->parentlist->pageurl->params()); | |
144 | $questionbankurl->param('cat', $category->id . ',' . $category->contextid); | |
8009810e | 145 | $item = ''; |
d4b47c54 TH |
146 | $text = format_string($category->name, true, ['context' => $this->parentlist->context]); |
147 | if ($category->idnumber !== null && $category->idnumber !== '') { | |
148 | $text .= ' ' . html_writer::span( | |
149 | html_writer::span(get_string('idnumber', 'question'), 'accesshide') . | |
150 | ' ' . $category->idnumber, 'badge badge-primary'); | |
151 | } | |
152 | $text .= ' (' . $category->questioncount . ')'; | |
2ea8fbb0 V |
153 | $item .= html_writer::tag('b', html_writer::link($questionbankurl, $text, |
154 | ['title' => $editqestions]) . ' '); | |
8009810e | 155 | $item .= format_text($category->info, $category->infoformat, |
fc51c2bc | 156 | array('context' => $this->parentlist->context, 'noclean' => true)); |
dac786f3 | 157 | |
9275220d SR |
158 | // Don't allow delete if this is the top category, or the last editable category in this context. |
159 | if ($category->parent && !question_is_only_child_of_top_category_in_context($category->id)) { | |
8009810e TH |
160 | $deleteurl = new moodle_url($this->parentlist->pageurl, array('delete' => $this->id, 'sesskey' => sesskey())); |
161 | $item .= html_writer::link($deleteurl, | |
663640f5 | 162 | $OUTPUT->pix_icon('t/delete', $str->delete), |
8009810e | 163 | array('title' => $str->delete)); |
dac786f3 | 164 | } |
165 | ||
166 | return $item; | |
dac786f3 | 167 | } |
dac786f3 | 168 | } |
169 | ||
516cf3eb | 170 | |
4323d029 | 171 | /** |
468d7ead | 172 | * Class for performing operations on question categories. |
dac786f3 | 173 | * |
017bc1d9 TH |
174 | * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} |
175 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
4323d029 | 176 | */ |
dc1f00de | 177 | class question_category_object { |
516cf3eb | 178 | |
dac786f3 | 179 | /** |
fa5c608c | 180 | * @var array common language strings. |
dac786f3 | 181 | */ |
fa5c608c TH |
182 | public $str; |
183 | ||
184 | /** | |
185 | * @var array nested lists to display categories. | |
186 | */ | |
187 | public $editlists = array(); | |
fa5c608c TH |
188 | public $tab; |
189 | public $tabsize = 3; | |
017bc1d9 | 190 | |
986effb6 | 191 | /** |
192 | * @var moodle_url Object representing url for this page | |
193 | */ | |
fa5c608c TH |
194 | public $pageurl; |
195 | ||
271e6dec | 196 | /** |
197 | * @var question_category_edit_form Object representing form for adding / editing categories. | |
198 | */ | |
fa5c608c | 199 | public $catform; |
516cf3eb | 200 | |
bc649d80 | 201 | /** |
468d7ead | 202 | * Constructor. |
bc649d80 | 203 | * |
468d7ead TH |
204 | * @param int $page page number |
205 | * @param moodle_url $pageurl base URL of the display categories page. Used for redirects. | |
206 | * @param context[] $contexts contexts where the current user can edit categories. | |
207 | * @param int $currentcat id of the category to be edited. 0 if none. | |
208 | * @param int|null $defaultcategory id of the current category. null if none. | |
209 | * @param int $todelete id of the category to delete. 0 if none. | |
210 | * @param context[] $addcontexts contexts where the current user can add questions. | |
bc649d80 | 211 | */ |
c4d0b752 | 212 | public function __construct($page, $pageurl, $contexts, $currentcat, $defaultcategory, $todelete, $addcontexts) { |
516cf3eb | 213 | |
214 | $this->tab = str_repeat(' ', $this->tabsize); | |
215 | ||
e2bb3c92 | 216 | $this->str = new stdClass(); |
516cf3eb | 217 | $this->str->course = get_string('course'); |
5e8a85aa TH |
218 | $this->str->category = get_string('category', 'question'); |
219 | $this->str->categoryinfo = get_string('categoryinfo', 'question'); | |
220 | $this->str->questions = get_string('questions', 'question'); | |
516cf3eb | 221 | $this->str->add = get_string('add'); |
222 | $this->str->delete = get_string('delete'); | |
223 | $this->str->moveup = get_string('moveup'); | |
224 | $this->str->movedown = get_string('movedown'); | |
85edde5c | 225 | $this->str->edit = get_string('editthiscategory', 'question'); |
516cf3eb | 226 | $this->str->hide = get_string('hide'); |
516cf3eb | 227 | $this->str->order = get_string('order'); |
5e8a85aa | 228 | $this->str->parent = get_string('parent', 'question'); |
516cf3eb | 229 | $this->str->add = get_string('add'); |
230 | $this->str->action = get_string('action'); | |
5e8a85aa TH |
231 | $this->str->top = get_string('top'); |
232 | $this->str->addcategory = get_string('addcategory', 'question'); | |
233 | $this->str->editcategory = get_string('editcategory', 'question'); | |
516cf3eb | 234 | $this->str->cancel = get_string('cancel'); |
5e8a85aa | 235 | $this->str->editcategories = get_string('editcategories', 'question'); |
2befe778 | 236 | $this->str->page = get_string('page'); |
516cf3eb | 237 | |
986effb6 | 238 | $this->pageurl = $pageurl; |
271e6dec | 239 | |
240 | $this->initialize($page, $contexts, $currentcat, $defaultcategory, $todelete, $addcontexts); | |
516cf3eb | 241 | } |
271e6dec | 242 | |
9e313e79 DM |
243 | /** |
244 | * Old syntax of class constructor. Deprecated in PHP7. | |
245 | * | |
246 | * @deprecated since Moodle 3.1 | |
247 | */ | |
248 | public function question_category_object($page, $pageurl, $contexts, $currentcat, $defaultcategory, $todelete, $addcontexts) { | |
249 | debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER); | |
250 | self::__construct($page, $pageurl, $contexts, $currentcat, $defaultcategory, $todelete, $addcontexts); | |
251 | } | |
252 | ||
271e6dec | 253 | /** |
254 | * Initializes this classes general category-related variables | |
255 | */ | |
f34488b2 | 256 | public function initialize($page, $contexts, $currentcat, $defaultcategory, $todelete, $addcontexts) { |
271e6dec | 257 | $lastlist = null; |
258 | foreach ($contexts as $context){ | |
259 | $this->editlists[$context->id] = new question_category_list('ul', '', true, $this->pageurl, $page, 'cpage', QUESTION_PAGE_LENGTH, $context); | |
260 | $this->editlists[$context->id]->lastlist =& $lastlist; | |
261 | if ($lastlist!== null){ | |
262 | $lastlist->nextlist =& $this->editlists[$context->id]; | |
263 | } | |
264 | $lastlist =& $this->editlists[$context->id]; | |
265 | } | |
266 | ||
267 | $count = 1; | |
268 | $paged = false; | |
269 | foreach ($this->editlists as $key => $list){ | |
20f500dc | 270 | list($paged, $count) = $this->editlists[$key]->list_from_records($paged, $count); |
271e6dec | 271 | } |
272 | $this->catform = new question_category_edit_form($this->pageurl, compact('contexts', 'currentcat')); | |
273 | if (!$currentcat){ | |
274 | $this->catform->set_data(array('parent'=>$defaultcategory)); | |
275 | } | |
276 | } | |
017bc1d9 | 277 | |
bc649d80 | 278 | /** |
279 | * Displays the user interface | |
280 | * | |
bc649d80 | 281 | */ |
f34488b2 | 282 | public function display_user_interface() { |
dac786f3 | 283 | |
284 | /// Interface for editing existing categories | |
271e6dec | 285 | $this->output_edit_lists(); |
dac786f3 | 286 | |
516cf3eb | 287 | |
dac786f3 | 288 | echo '<br />'; |
516cf3eb | 289 | /// Interface for adding a new category: |
516cf3eb | 290 | $this->output_new_table(); |
291 | echo '<br />'; | |
292 | ||
516cf3eb | 293 | } |
294 | ||
bc649d80 | 295 | /** |
296 | * Outputs a table to allow entry of a new category | |
297 | */ | |
f34488b2 | 298 | public function output_new_table() { |
271e6dec | 299 | $this->catform->display(); |
516cf3eb | 300 | } |
301 | ||
bc649d80 | 302 | /** |
dac786f3 | 303 | * Outputs a list to allow editing/rearranging of existing categories |
bc649d80 | 304 | * |
305 | * $this->initialize() must have already been called | |
306 | * | |
bc649d80 | 307 | */ |
f34488b2 | 308 | public function output_edit_lists() { |
723d610c | 309 | global $OUTPUT; |
1d58b567 | 310 | |
5fcefc97 | 311 | echo $OUTPUT->heading_with_help(get_string('editcategories', 'question'), 'editcategories', 'question'); |
1d58b567 | 312 | |
271e6dec | 313 | foreach ($this->editlists as $context => $list){ |
314 | $listhtml = $list->to_html(0, array('str'=>$this->str)); | |
315 | if ($listhtml){ | |
beb677cd | 316 | echo $OUTPUT->box_start('boxwidthwide boxaligncenter generalbox questioncategories contextlevel' . $list->context->contextlevel); |
329846f1 AG |
317 | $fullcontext = context::instance_by_id($context); |
318 | echo $OUTPUT->heading(get_string('questioncatsfor', 'question', $fullcontext->get_context_name()), 3); | |
271e6dec | 319 | echo $listhtml; |
beb677cd | 320 | echo $OUTPUT->box_end(); |
271e6dec | 321 | } |
322 | } | |
323 | echo $list->display_page_numbers(); | |
324 | } | |
516cf3eb | 325 | |
bc649d80 | 326 | /** |
327 | * gets all the courseids for the given categories | |
328 | * | |
329 | * @param array categories contains category objects in a tree representation | |
330 | * @return array courseids flat array in form categoryid=>courseid | |
331 | */ | |
f34488b2 | 332 | public function get_course_ids($categories) { |
516cf3eb | 333 | $courseids = array(); |
334 | foreach ($categories as $key=>$cat) { | |
335 | $courseids[$key] = $cat->course; | |
336 | if (!empty($cat->children)) { | |
337 | $courseids = array_merge($courseids, $this->get_course_ids($cat->children)); | |
338 | } | |
339 | } | |
340 | return $courseids; | |
341 | } | |
342 | ||
f34488b2 | 343 | public function edit_single_category($categoryid) { |
516cf3eb | 344 | /// Interface for adding a new category |
9275220d | 345 | global $DB; |
516cf3eb | 346 | /// Interface for editing existing categories |
9275220d SR |
347 | $category = $DB->get_record("question_categories", array("id" => $categoryid)); |
348 | if (empty($category)) { | |
349 | print_error('invalidcategory', '', '', $categoryid); | |
350 | } else if ($category->parent == 0) { | |
351 | print_error('cannotedittopcat', 'question', '', $categoryid); | |
352 | } else { | |
f4fe3968 | 353 | $category->parent = "{$category->parent},{$category->contextid}"; |
271e6dec | 354 | $category->submitbutton = get_string('savechanges'); |
355 | $category->categoryheader = $this->str->edit; | |
356 | $this->catform->set_data($category); | |
357 | $this->catform->display(); | |
516cf3eb | 358 | } |
516cf3eb | 359 | } |
360 | ||
bc649d80 | 361 | /** |
362 | * Sets the viable parents | |
363 | * | |
364 | * Viable parents are any except for the category itself, or any of it's descendants | |
365 | * The parentstrings parameter is passed by reference and changed by this function. | |
366 | * | |
367 | * @param array parentstrings a list of parentstrings | |
368 | * @param object category | |
369 | */ | |
f34488b2 | 370 | public function set_viable_parents(&$parentstrings, $category) { |
516cf3eb | 371 | |
372 | unset($parentstrings[$category->id]); | |
373 | if (isset($category->children)) { | |
374 | foreach ($category->children as $child) { | |
375 | $this->set_viable_parents($parentstrings, $child); | |
376 | } | |
377 | } | |
378 | } | |
379 | ||
bc649d80 | 380 | /** |
381 | * Gets question categories | |
382 | * | |
383 | * @param int parent - if given, restrict records to those with this parent id. | |
384 | * @param string sort - [[sortfield [,sortfield]] {ASC|DESC}] | |
385 | * @return array categories | |
386 | */ | |
f34488b2 | 387 | public function get_question_categories($parent=null, $sort="sortorder ASC") { |
388 | global $COURSE, $DB; | |
516cf3eb | 389 | if (is_null($parent)) { |
f34488b2 | 390 | $categories = $DB->get_records('question_categories', array('course' => $COURSE->id), $sort); |
516cf3eb | 391 | } else { |
f34488b2 | 392 | $select = "parent = ? AND course = ?"; |
393 | $categories = $DB->get_records_select('question_categories', $select, array($parent, $COURSE->id), $sort); | |
516cf3eb | 394 | } |
395 | return $categories; | |
396 | } | |
397 | ||
bc649d80 | 398 | /** |
399 | * Deletes an existing question category | |
400 | * | |
21cbf9da | 401 | * @param int deletecat id of category to delete |
bc649d80 | 402 | */ |
f34488b2 | 403 | public function delete_category($categoryid) { |
404 | global $CFG, $DB; | |
271e6dec | 405 | question_can_delete_cat($categoryid); |
f34488b2 | 406 | if (!$category = $DB->get_record("question_categories", array("id" => $categoryid))) { // security |
64e71d27 | 407 | print_error('unknowcategory'); |
516cf3eb | 408 | } |
516cf3eb | 409 | /// Send the children categories to live with their grandparent |
bb4b6010 | 410 | $DB->set_field("question_categories", "parent", $category->parent, array("parent" => $category->id)); |
516cf3eb | 411 | |
412 | /// Finally delete the category itself | |
21cbf9da | 413 | $DB->delete_records("question_categories", array("id" => $category->id)); |
93e435b9 SB |
414 | |
415 | // Log the deletion of this category. | |
4ca60a56 V |
416 | $event = \core\event\question_category_deleted::create_from_question_category_instance($category); |
417 | $event->add_record_snapshot('question_categories', $category); | |
93e435b9 SB |
418 | $event->trigger(); |
419 | ||
516cf3eb | 420 | } |
21cbf9da | 421 | |
f34488b2 | 422 | public function move_questions_and_delete_category($oldcat, $newcat){ |
271e6dec | 423 | question_can_delete_cat($oldcat); |
424 | $this->move_questions($oldcat, $newcat); | |
425 | $this->delete_category($oldcat); | |
426 | } | |
516cf3eb | 427 | |
f34488b2 | 428 | public function display_move_form($questionsincategory, $category){ |
beb677cd | 429 | global $OUTPUT; |
0ff4bd08 | 430 | $vars = new stdClass(); |
271e6dec | 431 | $vars->name = $category->name; |
432 | $vars->count = $questionsincategory; | |
5e8a85aa | 433 | echo $OUTPUT->box(get_string('categorymove', 'question', $vars), 'generalbox boxaligncenter'); |
271e6dec | 434 | $this->moveform->display(); |
435 | } | |
516cf3eb | 436 | |
f34488b2 | 437 | public function move_questions($oldcat, $newcat){ |
534792cd | 438 | global $DB; |
5d548d3e TH |
439 | $questionids = $DB->get_records_select_menu('question', |
440 | 'category = ? AND (parent = 0 OR parent = id)', array($oldcat), '', 'id,1'); | |
9d0ac0ff | 441 | question_move_questions_to_category(array_keys($questionids), $newcat); |
516cf3eb | 442 | } |
443 | ||
bc649d80 | 444 | /** |
468d7ead TH |
445 | * Create a new category. |
446 | * | |
447 | * Data is expected to come from question_category_edit_form. | |
448 | * | |
449 | * By default redirects on success, unless $return is true. | |
450 | * | |
451 | * @param string $newparent 'categoryid,contextid' of the parent category. | |
452 | * @param string $newcategory the name. | |
453 | * @param string $newinfo the description. | |
454 | * @param bool $return if true, return rather than redirecting. | |
455 | * @param int|string $newinfoformat description format. One of the FORMAT_ constants. | |
456 | * @param null $idnumber the idnumber. '' is converted to null. | |
457 | * @return bool|int New category id if successful, else false. | |
bc649d80 | 458 | */ |
6189fda4 JB |
459 | public function add_category($newparent, $newcategory, $newinfo, $return = false, $newinfoformat = FORMAT_HTML, |
460 | $idnumber = null) { | |
f34488b2 | 461 | global $DB; |
bc649d80 | 462 | if (empty($newcategory)) { |
5e8a85aa | 463 | print_error('categorynamecantbeblank', 'question'); |
bc649d80 | 464 | } |
271e6dec | 465 | list($parentid, $contextid) = explode(',', $newparent); |
466 | //moodle_form makes sure select element output is legal no need for further cleaning | |
d197ea43 | 467 | require_capability('moodle/question:managecategory', context::instance_by_id($contextid)); |
516cf3eb | 468 | |
271e6dec | 469 | if ($parentid) { |
f34488b2 | 470 | if(!($DB->get_field('question_categories', 'contextid', array('id' => $parentid)) == $contextid)) { |
3e0e25ef | 471 | print_error('cannotinsertquestioncatecontext', 'question', '', array('cat'=>$newcategory, 'ctx'=>$contextid)); |
516cf3eb | 472 | } |
473 | } | |
474 | ||
2d696f8f TH |
475 | if ((string) $idnumber === '') { |
476 | $idnumber = null; | |
477 | } else if (!empty($contextid)) { | |
6189fda4 JB |
478 | // While this check already exists in the form validation, this is a backstop preventing unnecessary errors. |
479 | if ($DB->record_exists('question_categories', | |
480 | ['idnumber' => $idnumber, 'contextid' => $contextid])) { | |
481 | $idnumber = null; | |
482 | } | |
483 | } | |
484 | ||
7f389342 | 485 | $cat = new stdClass(); |
271e6dec | 486 | $cat->parent = $parentid; |
487 | $cat->contextid = $contextid; | |
516cf3eb | 488 | $cat->name = $newcategory; |
489 | $cat->info = $newinfo; | |
fa5c608c | 490 | $cat->infoformat = $newinfoformat; |
516cf3eb | 491 | $cat->sortorder = 999; |
492 | $cat->stamp = make_unique_id_code(); | |
e7400851 | 493 | $cat->idnumber = $idnumber; |
bb4b6010 | 494 | $categoryid = $DB->insert_record("question_categories", $cat); |
58940b19 MN |
495 | |
496 | // Log the creation of this category. | |
4ca60a56 V |
497 | $category = new stdClass(); |
498 | $category->id = $categoryid; | |
499 | $category->contextid = $contextid; | |
500 | $event = \core\event\question_category_created::create_from_question_category_instance($category); | |
58940b19 MN |
501 | $event->trigger(); |
502 | ||
bb4b6010 | 503 | if ($return) { |
504 | return $categoryid; | |
516cf3eb | 505 | } else { |
e42f153c | 506 | redirect($this->pageurl);//always redirect after successful action |
516cf3eb | 507 | } |
516cf3eb | 508 | } |
509 | ||
bc649d80 | 510 | /** |
468d7ead | 511 | * Updates an existing category with given params. |
93e435b9 | 512 | * |
468d7ead TH |
513 | * Warning! parameter order and meaning confusingly different from add_category in some ways! |
514 | * | |
515 | * @param int $updateid id of the category to update. | |
516 | * @param int $newparent 'categoryid,contextid' of the parent category to set. | |
517 | * @param string $newname category name. | |
518 | * @param string $newinfo category description. | |
519 | * @param int|string $newinfoformat description format. One of the FORMAT_ constants. | |
520 | * @param int $idnumber the idnumber. '' is converted to null. | |
521 | * @param bool $redirect if true, will redirect once the DB is updated (default). | |
bc649d80 | 522 | */ |
6189fda4 | 523 | public function update_category($updateid, $newparent, $newname, $newinfo, $newinfoformat = FORMAT_HTML, |
93e435b9 | 524 | $idnumber = null, $redirect = true) { |
f9b0500f | 525 | global $CFG, $DB; |
271e6dec | 526 | if (empty($newname)) { |
5e8a85aa | 527 | print_error('categorynamecantbeblank', 'question'); |
bc649d80 | 528 | } |
516cf3eb | 529 | |
41c96651 | 530 | // Get the record we are updating. |
f34488b2 | 531 | $oldcat = $DB->get_record('question_categories', array('id' => $updateid)); |
9275220d | 532 | $lastcategoryinthiscontext = question_is_only_child_of_top_category_in_context($updateid); |
41c96651 | 533 | |
534 | if (!empty($newparent) && !$lastcategoryinthiscontext) { | |
535 | list($parentid, $tocontextid) = explode(',', $newparent); | |
536 | } else { | |
537 | $parentid = $oldcat->parent; | |
538 | $tocontextid = $oldcat->contextid; | |
539 | } | |
540 | ||
541 | // Check permissions. | |
d197ea43 | 542 | $fromcontext = context::instance_by_id($oldcat->contextid); |
271e6dec | 543 | require_capability('moodle/question:managecategory', $fromcontext); |
41c96651 | 544 | |
d7d4a097 JD |
545 | // If moving to another context, check permissions some more, and confirm contextid,stamp uniqueness. |
546 | $newstamprequired = false; | |
5d548d3e | 547 | if ($oldcat->contextid != $tocontextid) { |
d197ea43 | 548 | $tocontext = context::instance_by_id($tocontextid); |
271e6dec | 549 | require_capability('moodle/question:managecategory', $tocontext); |
d7d4a097 JD |
550 | |
551 | // Confirm stamp uniqueness in the new context. If the stamp already exists, generate a new one. | |
552 | if ($DB->record_exists('question_categories', array('contextid' => $tocontextid, 'stamp' => $oldcat->stamp))) { | |
553 | $newstamprequired = true; | |
554 | } | |
271e6dec | 555 | } |
41c96651 | 556 | |
2d696f8f TH |
557 | if ((string) $idnumber === '') { |
558 | $idnumber = null; | |
559 | } else if (!empty($tocontextid)) { | |
6189fda4 | 560 | // While this check already exists in the form validation, this is a backstop preventing unnecessary errors. |
468d7ead TH |
561 | if ($DB->record_exists_select('question_categories', |
562 | 'idnumber = ? AND contextid = ? AND id <> ?', | |
563 | [$idnumber, $tocontextid, $updateid])) { | |
6189fda4 JB |
564 | $idnumber = null; |
565 | } | |
566 | } | |
567 | ||
41c96651 | 568 | // Update the category record. |
9aa09d8f | 569 | $cat = new stdClass(); |
516cf3eb | 570 | $cat->id = $updateid; |
271e6dec | 571 | $cat->name = $newname; |
572 | $cat->info = $newinfo; | |
fa5c608c | 573 | $cat->infoformat = $newinfoformat; |
41c96651 | 574 | $cat->parent = $parentid; |
5d548d3e | 575 | $cat->contextid = $tocontextid; |
9c4f0fa7 | 576 | $cat->idnumber = $idnumber; |
d7d4a097 JD |
577 | if ($newstamprequired) { |
578 | $cat->stamp = make_unique_id_code(); | |
579 | } | |
bb4b6010 | 580 | $DB->update_record('question_categories', $cat); |
f59dba84 | 581 | |
93e435b9 | 582 | // Log the update of this category. |
4ca60a56 | 583 | $event = \core\event\question_category_updated::create_from_question_category_instance($cat); |
93e435b9 SB |
584 | $event->trigger(); |
585 | ||
df4e2244 | 586 | // If the category name has changed, rename any random questions in that category. |
41c96651 | 587 | if ($oldcat->name != $cat->name) { |
3cac440b | 588 | $where = "qtype = 'random' AND category = ? AND " . $DB->sql_compare_text('questiontext') . " = ?"; |
589 | ||
f9b0500f TH |
590 | $randomqtype = question_bank::get_qtype('random'); |
591 | $randomqname = $randomqtype->question_name($cat, false); | |
3cac440b | 592 | $DB->set_field_select('question', 'name', $randomqname, $where, array($cat->id, '0')); |
593 | ||
f9b0500f | 594 | $randomqname = $randomqtype->question_name($cat, true); |
3cac440b | 595 | $DB->set_field_select('question', 'name', $randomqname, $where, array($cat->id, '1')); |
41c96651 | 596 | } |
597 | ||
5d548d3e TH |
598 | if ($oldcat->contextid != $tocontextid) { |
599 | // Moving to a new context. Must move files belonging to questions. | |
600 | question_move_category_to_context($cat->id, $oldcat->contextid, $tocontextid); | |
516cf3eb | 601 | } |
271e6dec | 602 | |
9645fe83 TH |
603 | // Cat param depends on the context id, so update it. |
604 | $this->pageurl->param('cat', $updateid . ',' . $tocontextid); | |
93e435b9 SB |
605 | if ($redirect) { |
606 | redirect($this->pageurl); // Always redirect after successful action. | |
607 | } | |
271e6dec | 608 | } |
516cf3eb | 609 | } |