See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 401 and 402] [Versions 401 and 403]
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. 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 * Course and category management helper class. 19 * 20 * @package core_course 21 * @copyright 2013 Sam Hemelryk 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 namespace core_course\management; 26 27 defined('MOODLE_INTERNAL') || die; 28 29 require_once($CFG->dirroot . '/course/lib.php'); 30 31 /** 32 * Course and category management interface helper class. 33 * 34 * This class provides methods useful to the course and category management interfaces. 35 * Many of the methods on this class are static and serve one of two purposes. 36 * 1. encapsulate functionality in an effort to ensure minimal changes between the different 37 * methods of interaction. Specifically browser, AJAX and webservice. 38 * 2. abstract logic for acquiring actions away from output so that renderers may use them without 39 * having to include any logic or capability checks. 40 * 41 * @package core_course 42 * @copyright 2013 Sam Hemelryk 43 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 44 */ 45 class helper { 46 47 /** 48 * The expanded category structure if its already being loaded from the cache. 49 * @var null|array 50 */ 51 protected static $expandedcategories = null; 52 53 /** 54 * Returns course details in an array ready to be printed. 55 * 56 * @global \moodle_database $DB 57 * @param \core_course_list_element $course 58 * @return array 59 */ 60 public static function get_course_detail_array(\core_course_list_element $course) { 61 global $DB; 62 63 $canaccess = $course->can_access(); 64 65 $format = \course_get_format($course->id); 66 $modinfo = \get_fast_modinfo($course->id); 67 $modules = $modinfo->get_used_module_names(); 68 $sections = array(); 69 if ($format->uses_sections()) { 70 foreach ($modinfo->get_section_info_all() as $section) { 71 if ($section->uservisible) { 72 $sections[] = $format->get_section_name($section); 73 } 74 } 75 } 76 77 $category = \core_course_category::get($course->category); 78 $categoryurl = new \moodle_url('/course/management.php', array('categoryid' => $course->category)); 79 $categoryname = $category->get_formatted_name(); 80 81 $details = array( 82 'fullname' => array( 83 'key' => \get_string('fullname'), 84 'value' => $course->get_formatted_fullname() 85 ), 86 'shortname' => array( 87 'key' => \get_string('shortname'), 88 'value' => $course->get_formatted_shortname() 89 ), 90 'idnumber' => array( 91 'key' => \get_string('idnumber'), 92 'value' => s($course->idnumber) 93 ), 94 'category' => array( 95 'key' => \get_string('category'), 96 'value' => \html_writer::link($categoryurl, $categoryname) 97 ) 98 ); 99 if (has_capability('moodle/site:accessallgroups', $course->get_context())) { 100 $groups = \groups_get_course_data($course->id); 101 $details += array( 102 'groupings' => array( 103 'key' => \get_string('groupings', 'group'), 104 'value' => count($groups->groupings) 105 ), 106 'groups' => array( 107 'key' => \get_string('groups'), 108 'value' => count($groups->groups) 109 ) 110 ); 111 } 112 if ($canaccess) { 113 $names = \role_get_names($course->get_context()); 114 $sql = 'SELECT ra.roleid, COUNT(ra.id) AS rolecount 115 FROM {role_assignments} ra 116 WHERE ra.contextid = :contextid 117 GROUP BY ra.roleid'; 118 $rolecounts = $DB->get_records_sql($sql, array('contextid' => $course->get_context()->id)); 119 $roledetails = array(); 120 foreach ($rolecounts as $result) { 121 $a = new \stdClass; 122 $a->role = $names[$result->roleid]->localname; 123 $a->count = $result->rolecount; 124 $roledetails[] = \get_string('assignedrolecount', 'moodle', $a); 125 } 126 127 $details['roleassignments'] = array( 128 'key' => \get_string('roleassignments'), 129 'value' => join('<br />', $roledetails) 130 ); 131 } 132 if ($course->can_review_enrolments()) { 133 $enrolmentlines = array(); 134 $instances = \enrol_get_instances($course->id, true); 135 $plugins = \enrol_get_plugins(true); 136 foreach ($instances as $instance) { 137 if (!isset($plugins[$instance->enrol])) { 138 // Weird. 139 continue; 140 } 141 $plugin = $plugins[$instance->enrol]; 142 $enrolmentlines[] = $plugin->get_instance_name($instance); 143 } 144 $details['enrolmentmethods'] = array( 145 'key' => \get_string('enrolmentmethods'), 146 'value' => join('<br />', $enrolmentlines) 147 ); 148 } 149 if ($canaccess) { 150 $details['format'] = array( 151 'key' => \get_string('format'), 152 'value' => \course_get_format($course)->get_format_name() 153 ); 154 $details['sections'] = array( 155 'key' => \get_string('sections'), 156 'value' => join('<br />', $sections) 157 ); 158 $details['modulesused'] = array( 159 'key' => \get_string('modulesused'), 160 'value' => join('<br />', $modules) 161 ); 162 } 163 return $details; 164 } 165 166 /** 167 * Returns an array of actions that can be performed upon a category being shown in a list. 168 * 169 * @param \core_course_category $category 170 * @return array 171 */ 172 public static function get_category_listitem_actions(\core_course_category $category) { 173 global $CFG; 174 175 $manageurl = new \moodle_url('/course/management.php', array('categoryid' => $category->id)); 176 $baseurl = new \moodle_url($manageurl, array('sesskey' => \sesskey())); 177 $actions = array(); 178 179 // View link. 180 $actions['view'] = [ 181 'url' => new \moodle_url('/course/index.php', ['categoryid' => $category->id]), 182 'icon' => null, 183 'string' => get_string('view') 184 ]; 185 186 // Edit. 187 if ($category->can_edit()) { 188 $actions['edit'] = array( 189 'url' => new \moodle_url('/course/editcategory.php', array('id' => $category->id)), 190 'icon' => new \pix_icon('t/edit', new \lang_string('edit')), 191 'string' => new \lang_string('edit') 192 ); 193 } 194 195 // Show/Hide. 196 if ($category->can_change_visibility()) { 197 // We always show both icons and then just toggle the display of the invalid option with CSS. 198 $actions['hide'] = array( 199 'url' => new \moodle_url($baseurl, array('action' => 'hidecategory')), 200 'icon' => new \pix_icon('t/hide', new \lang_string('hide')), 201 'string' => new \lang_string('hide') 202 ); 203 $actions['show'] = array( 204 'url' => new \moodle_url($baseurl, array('action' => 'showcategory')), 205 'icon' => new \pix_icon('t/show', new \lang_string('show')), 206 'string' => new \lang_string('show') 207 ); 208 } 209 210 // Move up/down. 211 if ($category->can_change_sortorder()) { 212 $actions['moveup'] = array( 213 'url' => new \moodle_url($baseurl, array('action' => 'movecategoryup')), 214 'icon' => new \pix_icon('t/up', new \lang_string('moveup')), 215 'string' => new \lang_string('moveup') 216 ); 217 $actions['movedown'] = array( 218 'url' => new \moodle_url($baseurl, array('action' => 'movecategorydown')), 219 'icon' => new \pix_icon('t/down', new \lang_string('movedown')), 220 'string' => new \lang_string('movedown') 221 ); 222 } 223 224 if ($category->can_create_subcategory()) { 225 $actions['createnewsubcategory'] = array( 226 'url' => new \moodle_url('/course/editcategory.php', array('parent' => $category->id)), 227 'icon' => new \pix_icon('i/withsubcat', new \lang_string('createnewsubcategory')), 228 'string' => new \lang_string('createnewsubcategory') 229 ); 230 } 231 232 // Resort. 233 if ($category->can_resort_subcategories() && $category->has_children()) { 234 $actions['resortbyname'] = array( 235 'url' => new \moodle_url($baseurl, array('action' => 'resortcategories', 'resort' => 'name')), 236 'icon' => new \pix_icon('t/sort', new \lang_string('sort')), 237 'string' => new \lang_string('resortsubcategoriesby', 'moodle' , get_string('categoryname')) 238 ); 239 $actions['resortbynamedesc'] = array( 240 'url' => new \moodle_url($baseurl, array('action' => 'resortcategories', 'resort' => 'namedesc')), 241 'icon' => new \pix_icon('t/sort', new \lang_string('sort')), 242 'string' => new \lang_string('resortsubcategoriesbyreverse', 'moodle', get_string('categoryname')) 243 ); 244 $actions['resortbyidnumber'] = array( 245 'url' => new \moodle_url($baseurl, array('action' => 'resortcategories', 'resort' => 'idnumber')), 246 'icon' => new \pix_icon('t/sort', new \lang_string('sort')), 247 'string' => new \lang_string('resortsubcategoriesby', 'moodle', get_string('idnumbercoursecategory')) 248 ); 249 $actions['resortbyidnumberdesc'] = array( 250 'url' => new \moodle_url($baseurl, array('action' => 'resortcategories', 'resort' => 'idnumberdesc')), 251 'icon' => new \pix_icon('t/sort', new \lang_string('sort')), 252 'string' => new \lang_string('resortsubcategoriesbyreverse', 'moodle', get_string('idnumbercoursecategory')) 253 ); 254 } 255 256 // Delete. 257 if (!empty($category->move_content_targets_list()) || $category->can_delete_full()) { 258 $actions['delete'] = array( 259 'url' => new \moodle_url($baseurl, array('action' => 'deletecategory')), 260 'icon' => new \pix_icon('t/delete', new \lang_string('delete')), 261 'string' => new \lang_string('delete') 262 ); 263 } 264 265 // Permissions. 266 if ($category->can_review_permissions()) { 267 $actions['permissions'] = array( 268 'url' => new \moodle_url('/admin/roles/permissions.php', ['contextid' => $category->get_context()->id]), 269 'icon' => new \pix_icon('i/permissions', new \lang_string('permissions', 'role')), 270 'string' => new \lang_string('permissions', 'role') 271 ); 272 } 273 274 // Context locking. 275 if (!empty($CFG->contextlocking) && has_capability('moodle/site:managecontextlocks', $category->get_context())) { 276 $parentcontext = $category->get_context()->get_parent_context(); 277 if (empty($parentcontext) || !$parentcontext->locked) { 278 if ($category->get_context()->locked) { 279 $lockicon = 'i/unlock'; 280 $lockstring = get_string('managecontextunlock', 'admin'); 281 } else { 282 $lockicon = 'i/lock'; 283 $lockstring = get_string('managecontextlock', 'admin'); 284 } 285 $actions['managecontextlock'] = [ 286 'url' => new \moodle_url('/admin/lock.php', [ 287 'id' => $category->get_context()->id, 288 'returnurl' => $manageurl->out_as_local_url(false), 289 ]), 290 'icon' => new \pix_icon($lockicon, $lockstring), 291 'string' => $lockstring, 292 ]; 293 } 294 } 295 296 // Cohorts. 297 if ($category->can_review_cohorts()) { 298 $actions['cohorts'] = array( 299 'url' => new \moodle_url('/cohort/index.php', array('contextid' => $category->get_context()->id)), 300 'icon' => new \pix_icon('t/cohort', new \lang_string('cohorts', 'cohort')), 301 'string' => new \lang_string('cohorts', 'cohort') 302 ); 303 } 304 305 // Filters. 306 if ($category->can_review_filters()) { 307 $actions['filters'] = array( 308 'url' => new \moodle_url('/filter/manage.php', array('contextid' => $category->get_context()->id, 309 'return' => 'management')), 310 'icon' => new \pix_icon('i/filter', new \lang_string('filters', 'admin')), 311 'string' => new \lang_string('filters', 'admin') 312 ); 313 } 314 315 if ($category->can_restore_courses_into()) { 316 $actions['restore'] = array( 317 'url' => new \moodle_url('/backup/restorefile.php', array('contextid' => $category->get_context()->id)), 318 'icon' => new \pix_icon('i/restore', new \lang_string('restorecourse', 'admin')), 319 'string' => new \lang_string('restorecourse', 'admin') 320 ); 321 } 322 // Recyclebyn. 323 if (\tool_recyclebin\category_bin::is_enabled()) { 324 $categorybin = new \tool_recyclebin\category_bin($category->id); 325 if ($categorybin->can_view()) { 326 $autohide = get_config('tool_recyclebin', 'autohide'); 327 if ($autohide) { 328 $items = $categorybin->get_items(); 329 } else { 330 $items = []; 331 } 332 if (!$autohide || !empty($items)) { 333 $pluginname = get_string('pluginname', 'tool_recyclebin'); 334 $actions['recyclebin'] = [ 335 'url' => new \moodle_url('/admin/tool/recyclebin/index.php', ['contextid' => $category->get_context()->id]), 336 'icon' => new \pix_icon('trash', $pluginname, 'tool_recyclebin'), 337 'string' => $pluginname 338 ]; 339 } 340 } 341 } 342 343 // Content bank. 344 if ($category->has_contentbank()) { 345 $url = new \moodle_url('/contentbank/index.php', ['contextid' => $category->get_context()->id]); 346 $actions['contentbank'] = [ 347 'url' => $url, 348 'icon' => new \pix_icon('i/contentbank', ''), 349 'string' => get_string('contentbank') 350 ]; 351 } 352 353 return $actions; 354 } 355 356 /** 357 * Returns an array of actions for a course listitem. 358 * 359 * @param \core_course_category $category 360 * @param \core_course_list_element $course 361 * @return array 362 */ 363 public static function get_course_listitem_actions(\core_course_category $category, \core_course_list_element $course) { 364 $baseurl = new \moodle_url( 365 '/course/management.php', 366 array('courseid' => $course->id, 'categoryid' => $course->category, 'sesskey' => \sesskey()) 367 ); 368 $actions = array(); 369 // Edit. 370 if ($course->can_edit()) { 371 $actions[] = array( 372 'url' => new \moodle_url('/course/edit.php', array('id' => $course->id, 'returnto' => 'catmanage')), 373 'icon' => new \pix_icon('t/edit', \get_string('edit')), 374 'attributes' => array('class' => 'action-edit') 375 ); 376 } 377 // Copy. 378 if (self::can_copy_course($course->id)) { 379 $actions[] = array( 380 'url' => new \moodle_url('/backup/copy.php', array('id' => $course->id, 'returnto' => 'catmanage')), 381 'icon' => new \pix_icon('t/copy', \get_string('copycourse')), 382 'attributes' => array('class' => 'action-copy') 383 ); 384 } 385 // Delete. 386 if ($course->can_delete()) { 387 $actions[] = array( 388 'url' => new \moodle_url('/course/delete.php', array('id' => $course->id)), 389 'icon' => new \pix_icon('t/delete', \get_string('delete')), 390 'attributes' => array('class' => 'action-delete') 391 ); 392 } 393 // Show/Hide. 394 if ($course->can_change_visibility()) { 395 $actions[] = array( 396 'url' => new \moodle_url($baseurl, array('action' => 'hidecourse')), 397 'icon' => new \pix_icon('t/hide', \get_string('hide')), 398 'attributes' => array('data-action' => 'hide', 'class' => 'action-hide') 399 ); 400 $actions[] = array( 401 'url' => new \moodle_url($baseurl, array('action' => 'showcourse')), 402 'icon' => new \pix_icon('t/show', \get_string('show')), 403 'attributes' => array('data-action' => 'show', 'class' => 'action-show') 404 ); 405 } 406 // Move up/down. 407 if ($category->can_resort_courses()) { 408 $actions[] = array( 409 'url' => new \moodle_url($baseurl, array('action' => 'movecourseup')), 410 'icon' => new \pix_icon('t/up', \get_string('moveup')), 411 'attributes' => array('data-action' => 'moveup', 'class' => 'action-moveup') 412 ); 413 $actions[] = array( 414 'url' => new \moodle_url($baseurl, array('action' => 'movecoursedown')), 415 'icon' => new \pix_icon('t/down', \get_string('movedown')), 416 'attributes' => array('data-action' => 'movedown', 'class' => 'action-movedown') 417 ); 418 } 419 return $actions; 420 } 421 422 /** 423 * Returns an array of actions that can be performed on the course being displayed. 424 * 425 * @param \core_course_list_element $course 426 * @return array 427 */ 428 public static function get_course_detail_actions(\core_course_list_element $course) { 429 $params = array('courseid' => $course->id, 'categoryid' => $course->category, 'sesskey' => \sesskey()); 430 $baseurl = new \moodle_url('/course/management.php', $params); 431 $actions = array(); 432 // View. 433 $actions['view'] = array( 434 'url' => new \moodle_url('/course/view.php', array('id' => $course->id)), 435 'string' => \get_string('view') 436 ); 437 // Edit. 438 if ($course->can_edit()) { 439 $actions['edit'] = array( 440 'url' => new \moodle_url('/course/edit.php', array('id' => $course->id)), 441 'string' => \get_string('edit') 442 ); 443 } 444 // Permissions. 445 if ($course->can_review_enrolments()) { 446 $actions['enrolledusers'] = array( 447 'url' => new \moodle_url('/user/index.php', array('id' => $course->id)), 448 'string' => \get_string('enrolledusers', 'enrol') 449 ); 450 } 451 // Delete. 452 if ($course->can_delete()) { 453 $actions['delete'] = array( 454 'url' => new \moodle_url('/course/delete.php', array('id' => $course->id)), 455 'string' => \get_string('delete') 456 ); 457 } 458 // Show/Hide. 459 if ($course->can_change_visibility()) { 460 if ($course->visible) { 461 $actions['hide'] = array( 462 'url' => new \moodle_url($baseurl, array('action' => 'hidecourse')), 463 'string' => \get_string('hide') 464 ); 465 } else { 466 $actions['show'] = array( 467 'url' => new \moodle_url($baseurl, array('action' => 'showcourse')), 468 'string' => \get_string('show') 469 ); 470 } 471 } 472 // Backup. 473 if ($course->can_backup()) { 474 $actions['backup'] = array( 475 'url' => new \moodle_url('/backup/backup.php', array('id' => $course->id)), 476 'string' => \get_string('backup') 477 ); 478 } 479 // Restore. 480 if ($course->can_restore()) { 481 $actions['restore'] = array( 482 'url' => new \moodle_url('/backup/restorefile.php', array('contextid' => $course->get_context()->id)), 483 'string' => \get_string('restore') 484 ); 485 } 486 return $actions; 487 } 488 489 /** 490 * Resorts the courses within a category moving the given course up by one. 491 * 492 * @param \core_course_list_element $course 493 * @param \core_course_category $category 494 * @return bool 495 * @throws \moodle_exception 496 */ 497 public static function action_course_change_sortorder_up_one(\core_course_list_element $course, 498 \core_course_category $category) { 499 if (!$category->can_resort_courses()) { 500 throw new \moodle_exception('permissiondenied', 'error', '', null, 'core_course_category::can_resort'); 501 } 502 return \course_change_sortorder_by_one($course, true); 503 } 504 505 /** 506 * Resorts the courses within a category moving the given course down by one. 507 * 508 * @param \core_course_list_element $course 509 * @param \core_course_category $category 510 * @return bool 511 * @throws \moodle_exception 512 */ 513 public static function action_course_change_sortorder_down_one(\core_course_list_element $course, 514 \core_course_category $category) { 515 if (!$category->can_resort_courses()) { 516 throw new \moodle_exception('permissiondenied', 'error', '', null, 'core_course_category::can_resort'); 517 } 518 return \course_change_sortorder_by_one($course, false); 519 } 520 521 /** 522 * Resorts the courses within a category moving the given course up by one. 523 * 524 * @global \moodle_database $DB 525 * @param int|\stdClass $courserecordorid 526 * @return bool 527 */ 528 public static function action_course_change_sortorder_up_one_by_record($courserecordorid) { 529 if (is_int($courserecordorid)) { 530 $courserecordorid = get_course($courserecordorid); 531 } 532 $course = new \core_course_list_element($courserecordorid); 533 $category = \core_course_category::get($course->category); 534 return self::action_course_change_sortorder_up_one($course, $category); 535 } 536 537 /** 538 * Resorts the courses within a category moving the given course down by one. 539 * 540 * @global \moodle_database $DB 541 * @param int|\stdClass $courserecordorid 542 * @return bool 543 */ 544 public static function action_course_change_sortorder_down_one_by_record($courserecordorid) { 545 if (is_int($courserecordorid)) { 546 $courserecordorid = get_course($courserecordorid); 547 } 548 $course = new \core_course_list_element($courserecordorid); 549 $category = \core_course_category::get($course->category); 550 return self::action_course_change_sortorder_down_one($course, $category); 551 } 552 553 /** 554 * Changes the sort order so that the first course appears after the second course. 555 * 556 * @param int|\stdClass $courserecordorid 557 * @param int $moveaftercourseid 558 * @return bool 559 * @throws \moodle_exception 560 */ 561 public static function action_course_change_sortorder_after_course($courserecordorid, $moveaftercourseid) { 562 $course = \get_course($courserecordorid); 563 $category = \core_course_category::get($course->category); 564 if (!$category->can_resort_courses()) { 565 $url = '/course/management.php?categoryid='.$course->category; 566 throw new \moodle_exception('nopermissions', 'error', $url, \get_string('resortcourses', 'moodle')); 567 } 568 return \course_change_sortorder_after_course($course, $moveaftercourseid); 569 } 570 571 /** 572 * Makes a course visible given a \core_course_list_element object. 573 * 574 * @param \core_course_list_element $course 575 * @return bool 576 * @throws \moodle_exception 577 */ 578 public static function action_course_show(\core_course_list_element $course) { 579 if (!$course->can_change_visibility()) { 580 throw new \moodle_exception('permissiondenied', 'error', '', null, 581 'core_course_list_element::can_change_visbility'); 582 } 583 return course_change_visibility($course->id, true); 584 } 585 586 /** 587 * Makes a course hidden given a \core_course_list_element object. 588 * 589 * @param \core_course_list_element $course 590 * @return bool 591 * @throws \moodle_exception 592 */ 593 public static function action_course_hide(\core_course_list_element $course) { 594 if (!$course->can_change_visibility()) { 595 throw new \moodle_exception('permissiondenied', 'error', '', null, 596 'core_course_list_element::can_change_visbility'); 597 } 598 return course_change_visibility($course->id, false); 599 } 600 601 /** 602 * Makes a course visible given a course id or a database record. 603 * 604 * @global \moodle_database $DB 605 * @param int|\stdClass $courserecordorid 606 * @return bool 607 */ 608 public static function action_course_show_by_record($courserecordorid) { 609 if (is_int($courserecordorid)) { 610 $courserecordorid = get_course($courserecordorid); 611 } 612 $course = new \core_course_list_element($courserecordorid); 613 return self::action_course_show($course); 614 } 615 616 /** 617 * Makes a course hidden given a course id or a database record. 618 * 619 * @global \moodle_database $DB 620 * @param int|\stdClass $courserecordorid 621 * @return bool 622 */ 623 public static function action_course_hide_by_record($courserecordorid) { 624 if (is_int($courserecordorid)) { 625 $courserecordorid = get_course($courserecordorid); 626 } 627 $course = new \core_course_list_element($courserecordorid); 628 return self::action_course_hide($course); 629 } 630 631 /** 632 * Resort a categories subcategories shifting the given category up one. 633 * 634 * @param \core_course_category $category 635 * @return bool 636 * @throws \moodle_exception 637 */ 638 public static function action_category_change_sortorder_up_one(\core_course_category $category) { 639 if (!$category->can_change_sortorder()) { 640 throw new \moodle_exception('permissiondenied', 'error', '', null, 'core_course_category::can_change_sortorder'); 641 } 642 return $category->change_sortorder_by_one(true); 643 } 644 645 /** 646 * Resort a categories subcategories shifting the given category down one. 647 * 648 * @param \core_course_category $category 649 * @return bool 650 * @throws \moodle_exception 651 */ 652 public static function action_category_change_sortorder_down_one(\core_course_category $category) { 653 if (!$category->can_change_sortorder()) { 654 throw new \moodle_exception('permissiondenied', 'error', '', null, 'core_course_category::can_change_sortorder'); 655 } 656 return $category->change_sortorder_by_one(false); 657 } 658 659 /** 660 * Resort a categories subcategories shifting the given category up one. 661 * 662 * @param int $categoryid 663 * @return bool 664 */ 665 public static function action_category_change_sortorder_up_one_by_id($categoryid) { 666 $category = \core_course_category::get($categoryid); 667 return self::action_category_change_sortorder_up_one($category); 668 } 669 670 /** 671 * Resort a categories subcategories shifting the given category down one. 672 * 673 * @param int $categoryid 674 * @return bool 675 */ 676 public static function action_category_change_sortorder_down_one_by_id($categoryid) { 677 $category = \core_course_category::get($categoryid); 678 return self::action_category_change_sortorder_down_one($category); 679 } 680 681 /** 682 * Makes a category hidden given a core_course_category object. 683 * 684 * @param \core_course_category $category 685 * @return bool 686 * @throws \moodle_exception 687 */ 688 public static function action_category_hide(\core_course_category $category) { 689 if (!$category->can_change_visibility()) { 690 throw new \moodle_exception('permissiondenied', 'error', '', null, 'core_course_category::can_change_visbility'); 691 } 692 $category->hide(); 693 return true; 694 } 695 696 /** 697 * Makes a category visible given a core_course_category object. 698 * 699 * @param \core_course_category $category 700 * @return bool 701 * @throws \moodle_exception 702 */ 703 public static function action_category_show(\core_course_category $category) { 704 if (!$category->can_change_visibility()) { 705 throw new \moodle_exception('permissiondenied', 'error', '', null, 'core_course_category::can_change_visbility'); 706 } 707 $category->show(); 708 return true; 709 } 710 711 /** 712 * Makes a category visible given a course category id or database record. 713 * 714 * @param int|\stdClass $categoryid 715 * @return bool 716 */ 717 public static function action_category_show_by_id($categoryid) { 718 return self::action_category_show(\core_course_category::get($categoryid)); 719 } 720 721 /** 722 * Makes a category hidden given a course category id or database record. 723 * 724 * @param int|\stdClass $categoryid 725 * @return bool 726 */ 727 public static function action_category_hide_by_id($categoryid) { 728 return self::action_category_hide(\core_course_category::get($categoryid)); 729 } 730 731 /** 732 * Resorts the sub categories of the given category. 733 * 734 * @param \core_course_category $category 735 * @param string $sort One of idnumber or name. 736 * @param bool $cleanup If true cleanup will be done, if false you will need to do it manually later. 737 * @return bool 738 * @throws \moodle_exception 739 */ 740 public static function action_category_resort_subcategories(\core_course_category $category, $sort, $cleanup = true) { 741 if (!$category->can_resort_subcategories()) { 742 throw new \moodle_exception('permissiondenied', 'error', '', null, 'core_course_category::can_resort'); 743 } 744 return $category->resort_subcategories($sort, $cleanup); 745 } 746 747 /** 748 * Resorts the courses within the given category. 749 * 750 * @param \core_course_category $category 751 * @param string $sort One of fullname, shortname or idnumber 752 * @param bool $cleanup If true cleanup will be done, if false you will need to do it manually later. 753 * @return bool 754 * @throws \moodle_exception 755 */ 756 public static function action_category_resort_courses(\core_course_category $category, $sort, $cleanup = true) { 757 if (!$category->can_resort_courses()) { 758 throw new \moodle_exception('permissiondenied', 'error', '', null, 'core_course_category::can_resort'); 759 } 760 return $category->resort_courses($sort, $cleanup); 761 } 762 763 /** 764 * Moves courses out of one category and into a new category. 765 * 766 * @param \core_course_category $oldcategory The category we are moving courses out of. 767 * @param \core_course_category $newcategory The category we are moving courses into. 768 * @param array $courseids The ID's of the courses we want to move. 769 * @return bool True on success. 770 * @throws \moodle_exception 771 */ 772 public static function action_category_move_courses_into(\core_course_category $oldcategory, 773 \core_course_category $newcategory, array $courseids) { 774 global $DB; 775 776 list($where, $params) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED); 777 $params['categoryid'] = $oldcategory->id; 778 $sql = "SELECT c.id FROM {course} c WHERE c.id {$where} AND c.category <> :categoryid"; 779 if ($DB->record_exists_sql($sql, $params)) { 780 // Likely being tinkered with. 781 throw new \moodle_exception('coursedoesnotbelongtocategory'); 782 } 783 784 // All courses are currently within the old category. 785 return self::move_courses_into_category($newcategory, $courseids); 786 } 787 788 /** 789 * Returns the view modes for the management interface. 790 * @return array 791 */ 792 public static function get_management_viewmodes() { 793 return array( 794 'combined' => new \lang_string('categoriesandcourses'), 795 'categories' => new \lang_string('categories'), 796 'courses' => new \lang_string('courses') 797 ); 798 } 799 800 /** 801 * Search for courses with matching params. 802 * 803 * Please note that only one of search, blocklist, or modulelist can be specified at a time. 804 * Specifying more than one will result in only the first being used. 805 * 806 * @param string $search Words to search for. We search fullname, shortname, idnumber and summary. 807 * @param int $blocklist The ID of a block, courses will only be returned if they use this block. 808 * @param string $modulelist The name of a module (relates to database table name). Only courses containing this module 809 * will be returned. 810 * @param int $page The page number to display, starting at 0. 811 * @param int $perpage The number of courses to display per page. 812 * @return array 813 */ 814 public static function search_courses($search, $blocklist, $modulelist, $page = 0, $perpage = null) { 815 global $CFG; 816 817 if ($perpage === null) { 818 $perpage = $CFG->coursesperpage; 819 } 820 821 $searchcriteria = array(); 822 if (!empty($search)) { 823 $searchcriteria = array('search' => $search); 824 } else if (!empty($blocklist)) { 825 $searchcriteria = array('blocklist' => $blocklist); 826 } else if (!empty($modulelist)) { 827 $searchcriteria = array('modulelist' => $modulelist); 828 } 829 830 $topcat = \core_course_category::top(); 831 $courses = $topcat->search_courses($searchcriteria, array( 832 'recursive' => true, 833 'offset' => $page * $perpage, 834 'limit' => $perpage, 835 'sort' => array('fullname' => 1) 836 )); 837 $totalcount = $topcat->search_courses_count($searchcriteria, array('recursive' => true)); 838 839 return array($courses, \count($courses), $totalcount); 840 } 841 842 /** 843 * Moves one or more courses out of the category they are currently in and into a new category. 844 * 845 * This function works much the same way as action_category_move_courses_into however it allows courses from multiple 846 * categories to be moved into a single category. 847 * 848 * @param int|\core_course_category $categoryorid The category to move them into. 849 * @param array|int $courseids An array of course id's or optionally just a single course id. 850 * @return bool True on success or false on failure. 851 * @throws \moodle_exception 852 */ 853 public static function move_courses_into_category($categoryorid, $courseids = array()) { 854 global $DB; 855 if (!is_array($courseids)) { 856 // Just a single course ID. 857 $courseids = array($courseids); 858 } 859 // Bulk move courses from one category to another. 860 if (count($courseids) === 0) { 861 return false; 862 } 863 if ($categoryorid instanceof \core_course_category) { 864 $moveto = $categoryorid; 865 } else { 866 $moveto = \core_course_category::get($categoryorid); 867 } 868 if (!$moveto->can_move_courses_out_of() || !$moveto->can_move_courses_into()) { 869 throw new \moodle_exception('cannotmovecourses'); 870 } 871 872 list($where, $params) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED); 873 $sql = "SELECT c.id, c.category FROM {course} c WHERE c.id {$where}"; 874 $courses = $DB->get_records_sql($sql, $params); 875 $checks = array(); 876 foreach ($courseids as $id) { 877 if (!isset($courses[$id])) { 878 throw new \moodle_exception('invalidcourseid'); 879 } 880 $catid = $courses[$id]->category; 881 if (!isset($checks[$catid])) { 882 $coursecat = \core_course_category::get($catid); 883 $checks[$catid] = $coursecat->can_move_courses_out_of() && $coursecat->can_move_courses_into(); 884 } 885 if (!$checks[$catid]) { 886 throw new \moodle_exception('cannotmovecourses'); 887 } 888 } 889 return \move_courses($courseids, $moveto->id); 890 } 891 892 /** 893 * Returns an array of courseids and visiblity for all courses within the given category. 894 * @param int $categoryid 895 * @return array 896 */ 897 public static function get_category_courses_visibility($categoryid) { 898 global $DB; 899 $sql = "SELECT c.id, c.visible 900 FROM {course} c 901 WHERE c.category = :category"; 902 $params = array('category' => (int)$categoryid); 903 return $DB->get_records_sql($sql, $params); 904 } 905 906 /** 907 * Returns an array of all categoryids that have the given category as a parent and their visible value. 908 * @param int $categoryid 909 * @return array 910 */ 911 public static function get_category_children_visibility($categoryid) { 912 global $DB; 913 $category = \core_course_category::get($categoryid); 914 $select = $DB->sql_like('path', ':path'); 915 $path = $category->path . '/%'; 916 917 $sql = "SELECT c.id, c.visible 918 FROM {course_categories} c 919 WHERE ".$select; 920 $params = array('path' => $path); 921 return $DB->get_records_sql($sql, $params); 922 } 923 924 /** 925 * Records when a category is expanded or collapsed so that when the user 926 * 927 * @param \core_course_category $coursecat The category we're working with. 928 * @param bool $expanded True if the category is expanded now. 929 */ 930 public static function record_expanded_category(\core_course_category $coursecat, $expanded = true) { 931 // If this ever changes we are going to reset it and reload the categories as required. 932 self::$expandedcategories = null; 933 $categoryid = $coursecat->id; 934 $path = $coursecat->get_parents(); 935 /* @var \cache_session $cache */ 936 $cache = \cache::make('core', 'userselections'); 937 $categories = $cache->get('categorymanagementexpanded'); 938 if (!is_array($categories)) { 939 if (!$expanded) { 940 // No categories recorded, nothing to remove. 941 return; 942 } 943 $categories = array(); 944 } 945 if ($expanded) { 946 $ref =& $categories; 947 foreach ($coursecat->get_parents() as $path) { 948 if (!isset($ref[$path]) || !is_array($ref[$path])) { 949 $ref[$path] = array(); 950 } 951 $ref =& $ref[$path]; 952 } 953 if (!isset($ref[$categoryid])) { 954 $ref[$categoryid] = true; 955 } 956 } else { 957 $found = true; 958 $ref =& $categories; 959 foreach ($coursecat->get_parents() as $path) { 960 if (!isset($ref[$path])) { 961 $found = false; 962 break; 963 } 964 $ref =& $ref[$path]; 965 } 966 if ($found) { 967 $ref[$categoryid] = null; 968 unset($ref[$categoryid]); 969 } 970 } 971 $cache->set('categorymanagementexpanded', $categories); 972 } 973 974 /** 975 * Returns the categories that should be expanded when displaying the interface. 976 * 977 * @param int|null $withpath If specified a path to require as the parent. 978 * @return \core_course_category[] An array of Category ID's to expand. 979 */ 980 public static function get_expanded_categories($withpath = null) { 981 if (self::$expandedcategories === null) { 982 /* @var \cache_session $cache */ 983 $cache = \cache::make('core', 'userselections'); 984 self::$expandedcategories = $cache->get('categorymanagementexpanded'); 985 if (self::$expandedcategories === false) { 986 self::$expandedcategories = array(); 987 } 988 } 989 if (empty($withpath)) { 990 return array_keys(self::$expandedcategories); 991 } 992 $parents = explode('/', trim($withpath, '/')); 993 $ref =& self::$expandedcategories; 994 foreach ($parents as $parent) { 995 if (!isset($ref[$parent])) { 996 return array(); 997 } 998 $ref =& $ref[$parent]; 999 } 1000 if (is_array($ref)) { 1001 return array_keys($ref); 1002 } else { 1003 return array($parent); 1004 } 1005 } 1006 1007 /** 1008 * Get an array of the capabilities required to copy a course. 1009 * 1010 * @return array 1011 */ 1012 public static function get_course_copy_capabilities(): array { 1013 return array('moodle/backup:backupcourse', 'moodle/restore:restorecourse', 'moodle/course:view', 'moodle/course:create'); 1014 } 1015 1016 /** 1017 * Returns true if the current user can copy this course. 1018 * 1019 * @param int $courseid 1020 * @return bool 1021 */ 1022 public static function can_copy_course(int $courseid): bool { 1023 $coursecontext = \context_course::instance($courseid); 1024 return has_all_capabilities(self::get_course_copy_capabilities(), $coursecontext); 1025 } 1026 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body