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