Merge branch 'MDL-42592_master' of https://github.com/markn86/moodle
[moodle.git] / course / classes / management / helper.php
blobce02da90f8e2ca6361c64d298251bddaa954684b
1 <?php
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.
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/>.
17 /**
18 * Course and category management helper class.
20 * @package core_course
21 * @copyright 2013 Sam Hemelryk
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 namespace core_course\management;
27 defined('MOODLE_INTERNAL') || die;
29 /**
30 * Course and category management interface helper class.
32 * This class provides methods useful to the course and category management interfaces.
33 * Many of the methods on this class are static and serve one of two purposes.
34 * 1. encapsulate functionality in an effort to ensure minimal changes between the different
35 * methods of interaction. Specifically browser, AJAX and webservice.
36 * 2. abstract logic for acquiring actions away from output so that renderers may use them without
37 * having to include any logic or capability checks.
39 * @package core_course
40 * @copyright 2013 Sam Hemelryk
41 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
43 class helper {
45 /**
46 * The expanded category structure if its already being loaded from the cache.
47 * @var null|array
49 protected static $expandedcategories = null;
51 /**
52 * Returns course details in an array ready to be printed.
54 * @global \moodle_database $DB
55 * @param \course_in_list $course
56 * @return array
58 public static function get_course_detail_array(\course_in_list $course) {
59 global $DB;
61 $canaccess = $course->can_access();
63 $format = \course_get_format($course->id);
64 $modinfo = \get_fast_modinfo($course->id);
65 $modules = $modinfo->get_used_module_names();
66 $sections = array();
67 if ($format->uses_sections()) {
68 foreach ($modinfo->get_section_info_all() as $section) {
69 if ($section->uservisible) {
70 $sections[] = $format->get_section_name($section);
75 $category = \coursecat::get($course->category);
76 $categoryurl = new \moodle_url('/course/management.php', array('categoryid' => $course->category));
77 $categoryname = $category->get_formatted_name();
79 $details = array(
80 'fullname' => array(
81 'key' => \get_string('fullname'),
82 'value' => $course->get_formatted_fullname()
84 'shortname' => array(
85 'key' => \get_string('shortname'),
86 'value' => $course->get_formatted_shortname()
88 'idnumber' => array(
89 'key' => \get_string('idnumber'),
90 'value' => s($course->idnumber)
92 'category' => array(
93 'key' => \get_string('category'),
94 'value' => \html_writer::link($categoryurl, $categoryname)
97 if (has_capability('moodle/site:accessallgroups', $course->get_context())) {
98 $groups = \groups_get_course_data($course->id);
99 $details += array(
100 'groupings' => array(
101 'key' => \get_string('groupings', 'group'),
102 'value' => count($groups->groupings)
104 'groups' => array(
105 'key' => \get_string('groups'),
106 'value' => count($groups->groups)
110 if ($canaccess) {
111 $names = \role_get_names($course->get_context());
112 $sql = 'SELECT ra.roleid, COUNT(ra.id) AS rolecount
113 FROM {role_assignments} ra
114 WHERE ra.contextid = :contextid
115 GROUP BY ra.roleid';
116 $rolecounts = $DB->get_records_sql($sql, array('contextid' => $course->get_context()->id));
117 $roledetails = array();
118 foreach ($rolecounts as $result) {
119 $a = new \stdClass;
120 $a->role = $names[$result->roleid]->localname;
121 $a->count = $result->rolecount;
122 $roledetails[] = \get_string('assignedrolecount', 'moodle', $a);
125 $details['roleassignments'] = array(
126 'key' => \get_string('roleassignments'),
127 'value' => join('<br />', $roledetails)
130 if ($course->can_review_enrolments()) {
131 $enrolmentlines = array();
132 $instances = \enrol_get_instances($course->id, true);
133 $plugins = \enrol_get_plugins(true);
134 foreach ($instances as $instance) {
135 if (!isset($plugins[$instance->enrol])) {
136 // Weird.
137 continue;
139 $plugin = $plugins[$instance->enrol];
140 $enrolmentlines[] = $plugin->get_instance_name($instance);
142 $details['enrolmentmethods'] = array(
143 'key' => \get_string('enrolmentmethods'),
144 'value' => join('<br />', $enrolmentlines)
147 if ($canaccess) {
148 $details['format'] = array(
149 'key' => \get_string('format'),
150 'value' => \course_get_format($course)->get_format_name()
152 $details['sections'] = array(
153 'key' => \get_string('sections'),
154 'value' => join('<br />', $sections)
156 $details['modulesused'] = array(
157 'key' => \get_string('modulesused'),
158 'value' => join('<br />', $modules)
161 return $details;
165 * Returns an array of actions that can be performed upon a category being shown in a list.
167 * @param \coursecat $category
168 * @return array
170 public static function get_category_listitem_actions(\coursecat $category) {
171 $baseurl = new \moodle_url('/course/management.php', array('categoryid' => $category->id, 'sesskey' => \sesskey()));
172 $actions = array();
173 // Edit.
174 if ($category->can_edit()) {
175 $actions['edit'] = array(
176 'url' => new \moodle_url('/course/editcategory.php', array('id' => $category->id)),
177 'icon' => new \pix_icon('t/edit', new \lang_string('edit')),
178 'string' => new \lang_string('edit')
182 // Show/Hide.
183 if ($category->can_change_visibility()) {
184 // We always show both icons and then just toggle the display of the invalid option with CSS.
185 $actions['hide'] = array(
186 'url' => new \moodle_url($baseurl, array('action' => 'hidecategory')),
187 'icon' => new \pix_icon('t/hide', new \lang_string('hide')),
188 'string' => new \lang_string('hide')
190 $actions['show'] = array(
191 'url' => new \moodle_url($baseurl, array('action' => 'showcategory')),
192 'icon' => new \pix_icon('t/show', new \lang_string('show')),
193 'string' => new \lang_string('show')
197 // Move up/down.
198 if ($category->can_change_sortorder()) {
199 $actions['moveup'] = array(
200 'url' => new \moodle_url($baseurl, array('action' => 'movecategoryup')),
201 'icon' => new \pix_icon('t/up', new \lang_string('up')),
202 'string' => new \lang_string('up')
204 $actions['movedown'] = array(
205 'url' => new \moodle_url($baseurl, array('action' => 'movecategorydown')),
206 'icon' => new \pix_icon('t/down', new \lang_string('down')),
207 'string' => new \lang_string('down')
211 // Resort.
212 if ($category->can_resort_subcategories() && $category->has_children()) {
213 $actions['resortbyname'] = array(
214 'url' => new \moodle_url($baseurl, array('action' => 'resortcategories', 'resort' => 'name')),
215 'icon' => new \pix_icon('t/sort', new \lang_string('sort')),
216 'string' => new \lang_string('resortsubcategoriesbyname', 'moodle')
218 $actions['resortbyidnumber'] = array(
219 'url' => new \moodle_url($baseurl, array('action' => 'resortcategories', 'resort' => 'idnumber')),
220 'icon' => new \pix_icon('t/sort', new \lang_string('sort')),
221 'string' => new \lang_string('resortsubcategoriesbyidnumber', 'moodle')
225 // Delete.
226 if ($category->can_delete_full()) {
227 $actions['delete'] = array(
228 'url' => new \moodle_url($baseurl, array('action' => 'deletecategory')),
229 'icon' => new \pix_icon('t/delete', new \lang_string('delete')),
230 'string' => new \lang_string('delete')
234 // Roles.
235 if ($category->can_review_roles()) {
236 $actions['assignroles'] = array(
237 'url' => new \moodle_url('/admin/roles/assign.php', array('contextid' => $category->get_context()->id,
238 'return' => 'management')),
239 'icon' => new \pix_icon('i/assignroles', new \lang_string('assignroles', 'role')),
240 'string' => new \lang_string('assignroles', 'role')
244 // Permissions.
245 if ($category->can_review_permissions()) {
246 $actions['permissions'] = array(
247 'url' => new \moodle_url('/admin/roles/permissions.php', array('contextid' => $category->get_context()->id,
248 'return' => 'management')),
249 'icon' => new \pix_icon('i/permissions', new \lang_string('permissions', 'role')),
250 'string' => new \lang_string('permissions', 'role')
254 // Cohorts.
255 if ($category->can_review_cohorts()) {
256 $actions['cohorts'] = array(
257 'url' => new \moodle_url('/cohort/index.php', array('contextid' => $category->get_context()->id)),
258 'icon' => new \pix_icon('i/cohort', new \lang_string('cohorts', 'cohort')),
259 'string' => new \lang_string('cohorts', 'cohort')
263 // Filters.
264 if ($category->can_review_filters()) {
265 $actions['filters'] = array(
266 'url' => new \moodle_url('/filter/manage.php', array('contextid' => $category->get_context()->id,
267 'return' => 'management')),
268 'icon' => new \pix_icon('i/filter', new \lang_string('filters', 'admin')),
269 'string' => new \lang_string('filters', 'admin')
273 return $actions;
277 * Returns an array of actions for a course listitem.
279 * @param \coursecat $category
280 * @param \course_in_list $course
281 * @return string
283 public static function get_course_listitem_actions(\coursecat $category, \course_in_list $course) {
284 $baseurl = new \moodle_url(
285 '/course/management.php',
286 array('courseid' => $course->id, 'categoryid' => $course->category, 'sesskey' => \sesskey())
288 $actions = array();
289 // Edit.
290 if ($course->can_edit()) {
291 $actions[] = array(
292 'url' => new \moodle_url('/course/edit.php', array('id' => $course->id)),
293 'icon' => new \pix_icon('t/edit', \get_string('edit')),
294 'attributes' => array('class' => 'action-edit')
297 // Delete.
298 if ($course->can_delete()) {
299 $actions[] = array(
300 'url' => new \moodle_url('/course/delete.php', array('id' => $course->id)),
301 'icon' => new \pix_icon('t/delete', \get_string('delete')),
302 'attributes' => array('class' => 'action-delete')
305 // Show/Hide.
306 if ($course->can_change_visibility()) {
307 $actions[] = array(
308 'url' => new \moodle_url($baseurl, array('action' => 'hidecourse')),
309 'icon' => new \pix_icon('t/hide', \get_string('hide')),
310 'attributes' => array('data-action' => 'hide', 'class' => 'action-hide')
312 $actions[] = array(
313 'url' => new \moodle_url($baseurl, array('action' => 'showcourse')),
314 'icon' => new \pix_icon('t/show', \get_string('show')),
315 'attributes' => array('data-action' => 'show', 'class' => 'action-show')
318 // Move up/down.
319 if ($category->can_resort_courses()) {
320 $actions[] = array(
321 'url' => new \moodle_url($baseurl, array('action' => 'movecourseup')),
322 'icon' => new \pix_icon('t/up', \get_string('up')),
323 'attributes' => array('data-action' => 'moveup', 'class' => 'action-moveup')
325 $actions[] = array(
326 'url' => new \moodle_url($baseurl, array('action' => 'movecoursedown')),
327 'icon' => new \pix_icon('t/down', \get_string('down')),
328 'attributes' => array('data-action' => 'movedown', 'class' => 'action-movedown')
331 return $actions;
335 * Returns an array of actions that can be performed on the course being displayed.
337 * @param \course_in_list $course
338 * @return array
340 public static function get_course_detail_actions(\course_in_list $course) {
341 $params = array('courseid' => $course->id, 'categoryid' => $course->category, 'sesskey' => \sesskey());
342 $baseurl = new \moodle_url('/course/management.php', $params);
343 $actions = array();
344 // View.
345 if ($course->is_uservisible()) {
346 $actions['view'] = array(
347 'url' => new \moodle_url('/course/view.php', array('id' => $course->id)),
348 'string' => \get_string('view')
351 // Edit.
352 if ($course->can_edit()) {
353 $actions['edit'] = array(
354 'url' => new \moodle_url('/course/edit.php', array('id' => $course->id)),
355 'string' => \get_string('edit')
358 // Permissions.
359 if ($course->can_review_enrolments()) {
360 $actions['enrolledusers'] = array(
361 'url' => new \moodle_url('/enrol/users.php', array('id' => $course->id)),
362 'string' => \get_string('enrolledusers', 'enrol')
365 // Delete.
366 if ($course->can_delete()) {
367 $actions['delete'] = array(
368 'url' => new \moodle_url('/course/delete.php', array('id' => $course->id)),
369 'string' => \get_string('delete')
372 // Show/Hide.
373 if ($course->can_change_visibility()) {
374 if ($course->visible) {
375 $actions['hide'] = array(
376 'url' => new \moodle_url($baseurl, array('action' => 'hidecourse')),
377 'string' => \get_string('hide')
379 } else {
380 $actions['show'] = array(
381 'url' => new \moodle_url($baseurl, array('action' => 'showcourse')),
382 'string' => \get_string('show')
386 // Backup.
387 if ($course->can_backup()) {
388 $actions['backup'] = array(
389 'url' => new \moodle_url('/backup/backup.php', array('id' => $course->id)),
390 'string' => \get_string('backup')
393 // Restore.
394 if ($course->can_restore()) {
395 $actions['restore'] = array(
396 'url' => new \moodle_url('/backup/restorefile.php', array('contextid' => $course->get_context()->id)),
397 'string' => \get_string('restore')
400 return $actions;
404 * Resorts the courses within a category moving the given course up by one.
406 * @param \course_in_list $course
407 * @param \coursecat $category
408 * @return bool
409 * @throws \moodle_exception
411 public static function action_course_change_sortorder_up_one(\course_in_list $course, \coursecat $category) {
412 if (!$category->can_resort_courses()) {
413 throw new \moodle_exception('permissiondenied', 'error', '', null, 'coursecat::can_resort');
415 return \course_change_sortorder_by_one($course, true);
419 * Resorts the courses within a category moving the given course down by one.
421 * @param \course_in_list $course
422 * @param \coursecat $category
423 * @return bool
424 * @throws \moodle_exception
426 public static function action_course_change_sortorder_down_one(\course_in_list $course, \coursecat $category) {
427 if (!$category->can_resort_courses()) {
428 throw new \moodle_exception('permissiondenied', 'error', '', null, 'coursecat::can_resort');
430 return \course_change_sortorder_by_one($course, false);
434 * Resorts the courses within a category moving the given course up by one.
436 * @global \moodle_database $DB
437 * @param int|\stdClass $courserecordorid
438 * @return bool
440 public static function action_course_change_sortorder_up_one_by_record($courserecordorid) {
441 if (is_int($courserecordorid)) {
442 $courserecordorid = get_course($courserecordorid);
444 $course = new \course_in_list($courserecordorid);
445 $category = \coursecat::get($course->category);
446 return self::action_course_change_sortorder_up_one($course, $category);
450 * Resorts the courses within a category moving the given course down by one.
452 * @global \moodle_database $DB
453 * @param int|\stdClass $courserecordorid
454 * @return bool
456 public static function action_course_change_sortorder_down_one_by_record($courserecordorid) {
457 if (is_int($courserecordorid)) {
458 $courserecordorid = get_course($courserecordorid);
460 $course = new \course_in_list($courserecordorid);
461 $category = \coursecat::get($course->category);
462 return self::action_course_change_sortorder_down_one($course, $category);
466 * Changes the sort order so that the first course appears after the second course.
468 * @param int|\stdClass $courserecordorid
469 * @param int $moveaftercourseid
470 * @return bool
471 * @throws \moodle_exception
473 public static function action_course_change_sortorder_after_course($courserecordorid, $moveaftercourseid) {
474 $course = \get_course($courserecordorid);
475 $category = \coursecat::get($course->category);
476 if (!$category->can_resort_courses()) {
477 $url = '/course/management.php?categoryid='.$course->category;
478 throw new \moodle_exception('nopermissions', 'error', $url, \get_string('resortcourses', 'moodle'));
480 return \course_change_sortorder_after_course($course, $moveaftercourseid);
484 * Makes a course visible given a \course_in_list object.
486 * @param \course_in_list $course
487 * @return bool
488 * @throws \moodle_exception
490 public static function action_course_show(\course_in_list $course) {
491 if (!$course->can_change_visibility()) {
492 throw new \moodle_exception('permissiondenied', 'error', '', null, 'course_in_list::can_change_visbility');
494 return course_change_visibility($course->id, true);
498 * Makes a course hidden given a \course_in_list object.
500 * @param \course_in_list $course
501 * @return bool
502 * @throws \moodle_exception
504 public static function action_course_hide(\course_in_list $course) {
505 if (!$course->can_change_visibility()) {
506 throw new \moodle_exception('permissiondenied', 'error', '', null, 'course_in_list::can_change_visbility');
508 return course_change_visibility($course->id, false);
512 * Makes a course visible given a course id or a database record.
514 * @global \moodle_database $DB
515 * @param int|\stdClass $courserecordorid
516 * @return bool
518 public static function action_course_show_by_record($courserecordorid) {
519 if (is_int($courserecordorid)) {
520 $courserecordorid = get_course($courserecordorid);
522 $course = new \course_in_list($courserecordorid);
523 return self::action_course_show($course);
527 * Makes a course hidden given a course id or a database record.
529 * @global \moodle_database $DB
530 * @param int|\stdClass $courserecordorid
531 * @return bool
533 public static function action_course_hide_by_record($courserecordorid) {
534 if (is_int($courserecordorid)) {
535 $courserecordorid = get_course($courserecordorid);
537 $course = new \course_in_list($courserecordorid);
538 return self::action_course_hide($course);
542 * Resort a categories subcategories shifting the given category up one.
544 * @param \coursecat $category
545 * @return bool
546 * @throws \moodle_exception
548 public static function action_category_change_sortorder_up_one(\coursecat $category) {
549 if (!$category->can_change_sortorder()) {
550 throw new \moodle_exception('permissiondenied', 'error', '', null, 'coursecat::can_change_sortorder');
552 return $category->change_sortorder_by_one(true);
556 * Resort a categories subcategories shifting the given category down one.
558 * @param \coursecat $category
559 * @return bool
560 * @throws \moodle_exception
562 public static function action_category_change_sortorder_down_one(\coursecat $category) {
563 if (!$category->can_change_sortorder()) {
564 throw new \moodle_exception('permissiondenied', 'error', '', null, 'coursecat::can_change_sortorder');
566 return $category->change_sortorder_by_one(false);
570 * Resort a categories subcategories shifting the given category up one.
572 * @param int $categoryid
573 * @return bool
575 public static function action_category_change_sortorder_up_one_by_id($categoryid) {
576 $category = \coursecat::get($categoryid);
577 return self::action_category_change_sortorder_up_one($category);
581 * Resort a categories subcategories shifting the given category down one.
583 * @param int $categoryid
584 * @return bool
586 public static function action_category_change_sortorder_down_one_by_id($categoryid) {
587 $category = \coursecat::get($categoryid);
588 return self::action_category_change_sortorder_down_one($category);
592 * Makes a category hidden given a \coursecat record.
594 * @param \coursecat $category
595 * @return bool
596 * @throws \moodle_exception
598 public static function action_category_hide(\coursecat $category) {
599 if (!$category->can_change_visibility()) {
600 throw new \moodle_exception('permissiondenied', 'error', '', null, 'coursecat::can_change_visbility');
602 $category->hide();
603 return true;
607 * Makes a category visible given a \coursecat record.
609 * @param \coursecat $category
610 * @return bool
611 * @throws \moodle_exception
613 public static function action_category_show(\coursecat $category) {
614 if (!$category->can_change_visibility()) {
615 throw new \moodle_exception('permissiondenied', 'error', '', null, 'coursecat::can_change_visbility');
617 if ((int)$category->get_parent_coursecat()->visible === 0) {
618 // You cannot mark a category visible if its parent is hidden.
619 return false;
621 $category->show();
622 return true;
626 * Makes a category visible given a \coursecat id or database record.
628 * @param int|\stdClass $categoryid
629 * @return bool
631 public static function action_category_show_by_id($categoryid) {
632 return self::action_category_show(\coursecat::get($categoryid));
636 * Makes a category hidden given a \coursecat id or database record.
638 * @param int|\stdClass $categoryid
639 * @return bool
641 public static function action_category_hide_by_id($categoryid) {
642 return self::action_category_hide(\coursecat::get($categoryid));
646 * Resorts the sub categories of the given category.
648 * @param \coursecat $category
649 * @param string $sort One of idnumber or name.
650 * @param bool $cleanup If true cleanup will be done, if false you will need to do it manually later.
651 * @return bool
652 * @throws \moodle_exception
654 public static function action_category_resort_subcategories(\coursecat $category, $sort, $cleanup = true) {
655 if (!$category->can_resort_subcategories()) {
656 throw new \moodle_exception('permissiondenied', 'error', '', null, 'coursecat::can_resort');
658 return $category->resort_subcategories($sort, $cleanup);
662 * Resorts the courses within the given category.
664 * @param \coursecat $category
665 * @param string $sort One of fullname, shortname or idnumber
666 * @return bool
667 * @throws \moodle_exception
669 public static function action_category_resort_courses(\coursecat $category, $sort) {
670 if (!$category->can_resort_courses()) {
671 throw new \moodle_exception('permissiondenied', 'error', '', null, 'coursecat::can_resort');
673 return $category->resort_courses($sort);
677 * Moves courses out of one category and into a new category.
679 * @param \coursecat $oldcategory The category we are moving courses out of.
680 * @param \coursecat $newcategory The category we are moving courses into.
681 * @param array $courseids The ID's of the courses we want to move.
682 * @return bool True on success.
683 * @throws \moodle_exception
685 public static function action_category_move_courses_into(\coursecat $oldcategory, \coursecat $newcategory, array $courseids) {
686 global $DB;
688 list($where, $params) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED);
689 $params['categoryid'] = $oldcategory->id;
690 $sql = "SELECT c.id FROM {course} c WHERE c.id {$where} AND c.category <> :categoryid";
691 if ($DB->record_exists_sql($sql, $params)) {
692 // Likely being tinkered with.
693 throw new \moodle_exception('coursedoesnotbelongtocategory');
696 // All courses are currently within the old category.
697 return self::move_courses_into_category($newcategory, $courseids);
701 * Returns the view modes for the management interface.
702 * @return array
704 public static function get_management_viewmodes() {
705 return array(
706 'combined' => new \lang_string('categoriesandcoures'),
707 'categories' => new \lang_string('categories'),
708 'courses' => new \lang_string('courses')
713 * Search for courses with matching params.
715 * Please note that only one of search, blocklist, or modulelist can be specified at a time.
716 * Specifying more than one will result in only the first being used.
718 * @param string $search Words to search for. We search fullname, shortname, idnumber and summary.
719 * @param int $blocklist The ID of a block, courses will only be returned if they use this block.
720 * @param string $modulelist The name of a module (relates to database table name). Only courses containing this module
721 * will be returned.
722 * @param int $page The page number to display, starting at 0.
723 * @param int $perpage The number of courses to display per page.
724 * @return array
726 public static function search_courses($search, $blocklist, $modulelist, $page = 0, $perpage = null) {
727 global $CFG;
729 if ($perpage === null) {
730 $perpage = $CFG->coursesperpage;
733 $searchcriteria = array();
734 if (!empty($search)) {
735 $searchcriteria = array('search' => $search);
736 } else if (!empty($blocklist)) {
737 $searchcriteria = array('blocklist' => $blocklist);
738 } else if (!empty($modulelist)) {
739 $searchcriteria = array('modulelist' => $modulelist);
742 $courses = \coursecat::get(0)->search_courses($searchcriteria, array(
743 'recursive' => true,
744 'offset' => $page * $perpage,
745 'limit' => $perpage,
746 'sort' => array('fullname' => 1)
748 $totalcount = \coursecat::get(0)->search_courses_count($searchcriteria, array('recursive' => true));
750 return array($courses, \count($courses), $totalcount);
754 * Moves one or more courses out of the category they are currently in and into a new category.
756 * This function works much the same way as action_category_move_courses_into however it allows courses from multiple
757 * categories to be moved into a single category.
759 * @param int|\coursecat $categoryorid The category to move them into.
760 * @param array|int $courseids An array of course id's or optionally just a single course id.
761 * @return bool True on success or false on failure.
762 * @throws \moodle_exception
764 public static function move_courses_into_category($categoryorid, $courseids = array()) {
765 global $DB;
766 if (!is_array($courseids)) {
767 // Just a single course ID.
768 $courseids = array($courseids);
770 // Bulk move courses from one category to another.
771 if (count($courseids) === 0) {
772 return false;
774 if ($categoryorid instanceof \coursecat) {
775 $moveto = $categoryorid;
776 } else {
777 $moveto = \coursecat::get($categoryorid);
779 if (!$moveto->can_move_courses_out_of() || !$moveto->can_move_courses_into()) {
780 throw new \moodle_exception('cannotmovecourses');
783 list($where, $params) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED);
784 $params['categoryid'] = $moveto->id;
785 $sql = "SELECT c.id, c.category FROM {course} c WHERE c.id {$where} AND c.category <> :categoryid";
786 $courses = $DB->get_records_sql($sql, $params);
787 $checks = array();
788 foreach ($courseids as $id) {
789 if (!isset($courses[$id])) {
790 throw new \moodle_exception('invalidcourseid');
792 $catid = $courses[$id]->category;
793 if (!isset($checks[$catid])) {
794 $coursecat = \coursecat::get($catid);
795 $checks[$catid] = $coursecat->can_move_courses_out_of() && $coursecat->can_move_courses_into();
797 if (!$checks[$catid]) {
798 throw new \moodle_exception('cannotmovecourses');
801 return \move_courses($courseids, $moveto->id);
805 * Returns an array of courseids and visiblity for all courses within the given category.
806 * @param int $categoryid
807 * @return array
809 public static function get_category_courses_visibility($categoryid) {
810 global $DB;
811 $sql = "SELECT c.id, c.visible
812 FROM {course} c
813 WHERE c.category = :category";
814 $params = array('category' => (int)$categoryid);
815 return $DB->get_records_sql($sql, $params);
819 * Returns an array of all categoryids that have the given category as a parent and their visible value.
820 * @param int $categoryid
821 * @return array
823 public static function get_category_children_visibility($categoryid) {
824 global $DB;
825 $category = \coursecat::get($categoryid);
826 $select = $DB->sql_like('path', ':path');
827 $path = $category->path . '/%';
829 $sql = "SELECT c.id, c.visible
830 FROM {course_categories} c
831 WHERE ".$select;
832 $params = array('path' => $path);
833 return $DB->get_records_sql($sql, $params);
837 * Records when a category is expanded or collapsed so that when the user
839 * @param \coursecat $coursecat The category we're working with.
840 * @param bool $expanded True if the category is expanded now.
842 public static function record_expanded_category(\coursecat $coursecat, $expanded = true) {
843 // If this ever changes we are going to reset it and reload the categories as required.
844 self::$expandedcategories = null;
845 $categoryid = $coursecat->id;
846 $path = $coursecat->get_parents();
847 /* @var \cache_session $cache */
848 $cache = \cache::make('core', 'userselections');
849 $categories = $cache->get('categorymanagementexpanded');
850 if (!is_array($categories)) {
851 if (!$expanded) {
852 // No categories recorded, nothing to remove.
853 return;
855 $categories = array();
857 if ($expanded) {
858 $ref =& $categories;
859 foreach ($coursecat->get_parents() as $path) {
860 if (!isset($ref[$path]) || !is_array($ref[$path])) {
861 $ref[$path] = array();
863 $ref =& $ref[$path];
865 if (!isset($ref[$categoryid])) {
866 $ref[$categoryid] = true;
868 } else {
869 $found = true;
870 $ref =& $categories;
871 foreach ($coursecat->get_parents() as $path) {
872 if (!isset($ref[$path])) {
873 $found = false;
874 break;
876 $ref =& $ref[$path];
878 if ($found) {
879 $ref[$categoryid] = null;
880 unset($ref[$categoryid]);
883 $cache->set('categorymanagementexpanded', $categories);
887 * Returns the categories that should be expanded when displaying the interface.
889 * @param int|null $withpath If specified a path to require as the parent.
890 * @return \coursecat[] An array of Category ID's to expand.
892 public static function get_expanded_categories($withpath = null) {
893 if (self::$expandedcategories === null) {
894 /* @var \cache_session $cache */
895 $cache = \cache::make('core', 'userselections');
896 self::$expandedcategories = $cache->get('categorymanagementexpanded');
897 if (self::$expandedcategories === false) {
898 self::$expandedcategories = array();
901 if (empty($withpath)) {
902 return array_keys(self::$expandedcategories);
904 $parents = explode('/', trim($withpath, '/'));
905 $ref =& self::$expandedcategories;
906 foreach ($parents as $parent) {
907 if (!isset($ref[$parent])) {
908 return array();
910 $ref =& $ref[$parent];
912 if (is_array($ref)) {
913 return array_keys($ref);
914 } else {
915 return array($parent);