See Release Notes
Long Term Support Release
Differences Between: [Versions 39 and 310] [Versions 39 and 311] [Versions 39 and 400] [Versions 39 and 401] [Versions 39 and 402] [Versions 39 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 interfaces. 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 require_once('../config.php'); 26 require_once($CFG->dirroot.'/course/lib.php'); 27 28 $categoryid = optional_param('categoryid', null, PARAM_INT); 29 $selectedcategoryid = optional_param('selectedcategoryid', null, PARAM_INT); 30 $courseid = optional_param('courseid', null, PARAM_INT); 31 $action = optional_param('action', false, PARAM_ALPHA); 32 $page = optional_param('page', 0, PARAM_INT); 33 $perpage = optional_param('perpage', null, PARAM_INT); 34 $viewmode = optional_param('view', 'default', PARAM_ALPHA); // Can be one of default, combined, courses, or categories. 35 36 // Search related params. 37 $search = optional_param('search', '', PARAM_RAW); // Search words. Shortname, fullname, idnumber and summary get searched. 38 $blocklist = optional_param('blocklist', 0, PARAM_INT); // Find courses containing this block. 39 $modulelist = optional_param('modulelist', '', PARAM_PLUGIN); // Find courses containing the given modules. 40 41 if (!in_array($viewmode, array('default', 'combined', 'courses', 'categories'))) { 42 $viewmode = 'default'; 43 } 44 45 $issearching = ($search !== '' || $blocklist !== 0 || $modulelist !== ''); 46 if ($issearching) { 47 $viewmode = 'courses'; 48 } 49 50 $url = new moodle_url('/course/management.php'); 51 $systemcontext = $context = context_system::instance(); 52 if ($courseid) { 53 $record = get_course($courseid); 54 $course = new core_course_list_element($record); 55 $category = core_course_category::get($course->category); 56 $categoryid = $category->id; 57 $context = context_coursecat::instance($category->id); 58 $url->param('categoryid', $categoryid); 59 $url->param('courseid', $course->id); 60 61 } else if ($categoryid) { 62 $courseid = null; 63 $course = null; 64 $category = core_course_category::get($categoryid); 65 $context = context_coursecat::instance($category->id); 66 $url->param('categoryid', $category->id); 67 68 } else { 69 $course = null; 70 $courseid = null; 71 $topchildren = core_course_category::top()->get_children(); 72 if (empty($topchildren)) { 73 throw new moodle_exception('cannotviewcategory', 'error'); 74 } 75 $category = reset($topchildren); 76 $categoryid = $category->id; 77 $context = context_coursecat::instance($category->id); 78 $url->param('categoryid', $category->id); 79 } 80 81 // Check if there is a selected category param, and if there is apply it. 82 if ($course === null && $selectedcategoryid !== null && $selectedcategoryid !== $categoryid) { 83 $url->param('categoryid', $selectedcategoryid); 84 } 85 86 if ($page !== 0) { 87 $url->param('page', $page); 88 } 89 if ($viewmode !== 'default') { 90 $url->param('view', $viewmode); 91 } 92 if ($search !== '') { 93 $url->param('search', $search); 94 } 95 if ($blocklist !== 0) { 96 $url->param('blocklist', $search); 97 } 98 if ($modulelist !== '') { 99 $url->param('modulelist', $search); 100 } 101 102 $strmanagement = new lang_string('coursecatmanagement'); 103 $pageheading = format_string($SITE->fullname, true, array('context' => $systemcontext)); 104 105 $PAGE->set_context($context); 106 $PAGE->set_url($url); 107 $PAGE->set_pagelayout('admin'); 108 $PAGE->set_title($strmanagement); 109 $PAGE->set_heading($pageheading); 110 $PAGE->requires->js_call_amd('core_course/copy_modal', 'init', array($context->id)); 111 112 // This is a system level page that operates on other contexts. 113 require_login(); 114 115 if (!core_course_category::has_capability_on_any(array('moodle/category:manage', 'moodle/course:create'))) { 116 // The user isn't able to manage any categories. Lets redirect them to the relevant course/index.php page. 117 $url = new moodle_url('/course/index.php'); 118 if ($categoryid) { 119 $url->param('categoryid', $categoryid); 120 } 121 redirect($url); 122 } 123 124 // If the user poses any of these capabilities then they will be able to see the admin 125 // tree and the management link within it. 126 // This is the most accurate form of navigation. 127 $capabilities = array( 128 'moodle/site:config', 129 'moodle/backup:backupcourse', 130 'moodle/category:manage', 131 'moodle/course:create', 132 'moodle/site:approvecourse' 133 ); 134 if ($category && !has_any_capability($capabilities, $systemcontext)) { 135 // If the user doesn't poses any of these system capabilities then we're going to mark the manage link in the settings block 136 // as active, tell the page to ignore the active path and just build what the user would expect. 137 // This will at least give the page some relevant navigation. 138 navigation_node::override_active_url(new moodle_url('/course/management.php', array('categoryid' => $category->id))); 139 $PAGE->set_category_by_id($category->id); 140 $PAGE->navbar->ignore_active(true); 141 $PAGE->navbar->add(get_string('coursemgmt', 'admin'), $PAGE->url->out_omit_querystring()); 142 } else { 143 // If user has system capabilities, make sure the "Manage courses and categories" item in Administration block is active. 144 navigation_node::require_admin_tree(); 145 navigation_node::override_active_url(new moodle_url('/course/management.php')); 146 } 147 if (!$issearching && $category !== null) { 148 $parents = core_course_category::get_many($category->get_parents()); 149 $parents[] = $category; 150 foreach ($parents as $parent) { 151 $PAGE->navbar->add( 152 $parent->get_formatted_name(), 153 new moodle_url('/course/management.php', array('categoryid' => $parent->id)) 154 ); 155 } 156 if ($course instanceof core_course_list_element) { 157 // Use the list name so that it matches whats being displayed below. 158 $PAGE->navbar->add($course->get_formatted_name()); 159 } 160 } 161 162 $notificationspass = array(); 163 $notificationsfail = array(); 164 165 if ($action !== false && confirm_sesskey()) { 166 // Actions: 167 // - resortcategories : Resort the courses in the given category. 168 // - resortcourses : Resort courses 169 // - showcourse : make a course visible. 170 // - hidecourse : make a course hidden. 171 // - movecourseup : move the selected course up one. 172 // - movecoursedown : move the selected course down. 173 // - showcategory : make a category visible. 174 // - hidecategory : make a category hidden. 175 // - movecategoryup : move category up. 176 // - movecategorydown : move category down. 177 // - deletecategory : delete the category either in full, or moving contents. 178 // - bulkaction : performs bulk actions: 179 // - bulkmovecourses. 180 // - bulkmovecategories. 181 // - bulkresortcategories. 182 $redirectback = false; 183 $redirectmessage = false; 184 switch ($action) { 185 case 'resortcategories' : 186 $sort = required_param('resort', PARAM_ALPHA); 187 $cattosort = core_course_category::get((int)optional_param('categoryid', 0, PARAM_INT)); 188 $redirectback = \core_course\management\helper::action_category_resort_subcategories($cattosort, $sort); 189 break; 190 case 'resortcourses' : 191 // They must have specified a category. 192 required_param('categoryid', PARAM_INT); 193 $sort = required_param('resort', PARAM_ALPHA); 194 \core_course\management\helper::action_category_resort_courses($category, $sort); 195 break; 196 case 'showcourse' : 197 $redirectback = \core_course\management\helper::action_course_show($course); 198 break; 199 case 'hidecourse' : 200 $redirectback = \core_course\management\helper::action_course_hide($course); 201 break; 202 case 'movecourseup' : 203 // They must have specified a category and a course. 204 required_param('categoryid', PARAM_INT); 205 required_param('courseid', PARAM_INT); 206 $redirectback = \core_course\management\helper::action_course_change_sortorder_up_one($course, $category); 207 break; 208 case 'movecoursedown' : 209 // They must have specified a category and a course. 210 required_param('categoryid', PARAM_INT); 211 required_param('courseid', PARAM_INT); 212 $redirectback = \core_course\management\helper::action_course_change_sortorder_down_one($course, $category); 213 break; 214 case 'showcategory' : 215 // They must have specified a category. 216 required_param('categoryid', PARAM_INT); 217 $redirectback = \core_course\management\helper::action_category_show($category); 218 break; 219 case 'hidecategory' : 220 // They must have specified a category. 221 required_param('categoryid', PARAM_INT); 222 $redirectback = \core_course\management\helper::action_category_hide($category); 223 break; 224 case 'movecategoryup' : 225 // They must have specified a category. 226 required_param('categoryid', PARAM_INT); 227 $redirectback = \core_course\management\helper::action_category_change_sortorder_up_one($category); 228 break; 229 case 'movecategorydown' : 230 // They must have specified a category. 231 required_param('categoryid', PARAM_INT); 232 $redirectback = \core_course\management\helper::action_category_change_sortorder_down_one($category); 233 break; 234 case 'deletecategory': 235 // They must have specified a category. 236 required_param('categoryid', PARAM_INT); 237 if (!$category->can_delete()) { 238 throw new moodle_exception('permissiondenied', 'error', '', null, 'core_course_category::can_resort'); 239 } 240 $mform = new core_course_deletecategory_form(null, $category); 241 if ($mform->is_cancelled()) { 242 redirect($PAGE->url); 243 } 244 // Start output. 245 /* @var core_course_management_renderer|core_renderer $renderer */ 246 $renderer = $PAGE->get_renderer('core_course', 'management'); 247 echo $renderer->header(); 248 echo $renderer->heading(get_string('deletecategory', 'moodle', $category->get_formatted_name())); 249 250 if ($data = $mform->get_data()) { 251 // The form has been submit handle it. 252 if ($data->fulldelete == 1 && $category->can_delete_full()) { 253 $continueurl = new moodle_url('/course/management.php'); 254 if ($category->parent != '0') { 255 $continueurl->param('categoryid', $category->parent); 256 } 257 $notification = get_string('coursecategorydeleted', '', $category->get_formatted_name()); 258 $deletedcourses = $category->delete_full(true); 259 foreach ($deletedcourses as $course) { 260 echo $renderer->notification(get_string('coursedeleted', '', $course->shortname), 'notifysuccess'); 261 } 262 echo $renderer->notification($notification, 'notifysuccess'); 263 echo $renderer->continue_button($continueurl); 264 } else if ($data->fulldelete == 0 && $category->can_move_content_to($data->newparent)) { 265 $continueurl = new moodle_url('/course/management.php', array('categoryid' => $data->newparent)); 266 $category->delete_move($data->newparent, true); 267 echo $renderer->continue_button($continueurl); 268 } else { 269 // Some error in parameters (user is cheating?) 270 $mform->display(); 271 } 272 } else { 273 // Display the form. 274 $mform->display(); 275 } 276 // Finish output and exit. 277 echo $renderer->footer(); 278 exit(); 279 break; 280 case 'bulkaction': 281 $bulkmovecourses = optional_param('bulkmovecourses', false, PARAM_BOOL); 282 $bulkmovecategories = optional_param('bulkmovecategories', false, PARAM_BOOL); 283 $bulkresortcategories = optional_param('bulksort', false, PARAM_BOOL); 284 285 if ($bulkmovecourses) { 286 // Move courses out of the current category and into a new category. 287 // They must have specified a category. 288 required_param('categoryid', PARAM_INT); 289 $movetoid = required_param('movecoursesto', PARAM_INT); 290 $courseids = optional_param_array('bc', false, PARAM_INT); 291 if ($courseids === false) { 292 break; 293 } 294 $moveto = core_course_category::get($movetoid); 295 try { 296 // If this fails we want to catch the exception and report it. 297 $redirectback = \core_course\management\helper::move_courses_into_category($moveto, 298 $courseids); 299 if ($redirectback) { 300 $a = new stdClass; 301 $a->category = $moveto->get_formatted_name(); 302 $a->courses = count($courseids); 303 $redirectmessage = get_string('bulkmovecoursessuccess', 'moodle', $a); 304 } 305 } catch (moodle_exception $ex) { 306 $redirectback = false; 307 $notificationsfail[] = $ex->getMessage(); 308 } 309 } else if ($bulkmovecategories) { 310 $categoryids = optional_param_array('bcat', array(), PARAM_INT); 311 $movetocatid = required_param('movecategoriesto', PARAM_INT); 312 $movetocat = core_course_category::get($movetocatid); 313 $movecount = 0; 314 foreach ($categoryids as $id) { 315 $cattomove = core_course_category::get($id); 316 if ($id == $movetocatid) { 317 $notificationsfail[] = get_string('movecategoryownparent', 'error', $cattomove->get_formatted_name()); 318 continue; 319 } 320 // Don't allow user to move selected category into one of it's own sub-categories. 321 if (strpos($movetocat->path, $cattomove->path . '/') === 0) { 322 $notificationsfail[] = get_string('movecategoryparentconflict', 'error', $cattomove->get_formatted_name()); 323 continue; 324 } 325 if ($cattomove->parent != $movetocatid) { 326 if ($cattomove->can_change_parent($movetocatid)) { 327 $cattomove->change_parent($movetocatid); 328 $movecount++; 329 } else { 330 $notificationsfail[] = get_string('movecategorynotpossible', 'error', $cattomove->get_formatted_name()); 331 } 332 } 333 } 334 if ($movecount > 1) { 335 $a = new stdClass; 336 $a->count = $movecount; 337 $a->to = $movetocat->get_formatted_name(); 338 $movesuccessstrkey = 'movecategoriessuccess'; 339 if ($movetocatid == 0) { 340 $movesuccessstrkey = 'movecategoriestotopsuccess'; 341 } 342 $notificationspass[] = get_string($movesuccessstrkey, 'moodle', $a); 343 } else if ($movecount === 1) { 344 $a = new stdClass; 345 $a->moved = $cattomove->get_formatted_name(); 346 $a->to = $movetocat->get_formatted_name(); 347 $movesuccessstrkey = 'movecategorysuccess'; 348 if ($movetocatid == 0) { 349 $movesuccessstrkey = 'movecategorytotopsuccess'; 350 } 351 $notificationspass[] = get_string($movesuccessstrkey, 'moodle', $a); 352 } 353 } else if ($bulkresortcategories) { 354 $for = required_param('selectsortby', PARAM_ALPHA); 355 $sortcategoriesby = required_param('resortcategoriesby', PARAM_ALPHA); 356 $sortcoursesby = required_param('resortcoursesby', PARAM_ALPHA); 357 358 if ($sortcategoriesby === 'none' && $sortcoursesby === 'none') { 359 // They're not sorting anything. 360 break; 361 } 362 if (!in_array($sortcategoriesby, array('idnumber', 'idnumberdesc', 363 'name', 'namedesc'))) { 364 $sortcategoriesby = false; 365 } 366 if (!in_array($sortcoursesby, array('timecreated', 'timecreateddesc', 367 'idnumber', 'idnumberdesc', 368 'fullname', 'fullnamedesc', 369 'shortname', 'shortnamedesc'))) { 370 $sortcoursesby = false; 371 } 372 373 if ($for === 'thiscategory') { 374 $categoryids = array( 375 required_param('currentcategoryid', PARAM_INT) 376 ); 377 $categories = core_course_category::get_many($categoryids); 378 } else if ($for === 'selectedcategories') { 379 // Bulk resort selected categories. 380 $categoryids = optional_param_array('bcat', false, PARAM_INT); 381 $sort = required_param('resortcategoriesby', PARAM_ALPHA); 382 if ($categoryids === false) { 383 break; 384 } 385 $categories = core_course_category::get_many($categoryids); 386 } else if ($for === 'allcategories') { 387 if ($sortcategoriesby && core_course_category::top()->can_resort_subcategories()) { 388 \core_course\management\helper::action_category_resort_subcategories( 389 core_course_category::top(), $sortcategoriesby); 390 } 391 $categorieslist = core_course_category::make_categories_list('moodle/category:manage'); 392 $categoryids = array_keys($categorieslist); 393 $categories = core_course_category::get_many($categoryids); 394 unset($categorieslist); 395 } else { 396 break; 397 } 398 foreach ($categories as $cat) { 399 if ($sortcategoriesby && $cat->can_resort_subcategories()) { 400 // Don't clean up here, we'll do it once we're all done. 401 \core_course\management\helper::action_category_resort_subcategories($cat, $sortcategoriesby, false); 402 } 403 if ($sortcoursesby && $cat->can_resort_courses()) { 404 \core_course\management\helper::action_category_resort_courses($cat, $sortcoursesby, false); 405 } 406 } 407 core_course_category::resort_categories_cleanup($sortcoursesby !== false); 408 if ($category === null && count($categoryids) === 1) { 409 // They're bulk sorting just a single category and they've not selected a category. 410 // Lets for convenience sake auto-select the category that has been resorted for them. 411 redirect(new moodle_url($PAGE->url, array('categoryid' => reset($categoryids)))); 412 } 413 } 414 } 415 if ($redirectback) { 416 if ($redirectmessage) { 417 redirect($PAGE->url, $redirectmessage, 5); 418 } else { 419 redirect($PAGE->url); 420 } 421 } 422 } 423 424 if (!is_null($perpage)) { 425 set_user_preference('coursecat_management_perpage', $perpage); 426 } else { 427 $perpage = get_user_preferences('coursecat_management_perpage', $CFG->coursesperpage); 428 } 429 if ((int)$perpage != $perpage || $perpage < 2) { 430 $perpage = $CFG->coursesperpage; 431 } 432 433 $categorysize = 4; 434 $coursesize = 4; 435 $detailssize = 4; 436 if ($viewmode === 'default' || $viewmode === 'combined') { 437 if (isset($courseid)) { 438 $class = 'columns-3'; 439 } else { 440 $categorysize = 5; 441 $coursesize = 7; 442 $class = 'columns-2'; 443 } 444 } else if ($viewmode === 'categories') { 445 $categorysize = 12; 446 $class = 'columns-1'; 447 } else if ($viewmode === 'courses') { 448 if (isset($courseid)) { 449 $coursesize = 6; 450 $detailssize = 6; 451 $class = 'columns-2'; 452 } else { 453 $coursesize = 12; 454 $class = 'columns-1'; 455 } 456 } 457 if ($viewmode === 'default' || $viewmode === 'combined') { 458 $class .= ' viewmode-cobmined'; 459 } else { 460 $class .= ' viewmode-'.$viewmode; 461 } 462 if (($viewmode === 'default' || $viewmode === 'combined' || $viewmode === 'courses') && !empty($courseid)) { 463 $class .= ' course-selected'; 464 } 465 466 /* @var core_course_management_renderer|core_renderer $renderer */ 467 $renderer = $PAGE->get_renderer('core_course', 'management'); 468 $renderer->enhance_management_interface(); 469 470 $displaycategorylisting = ($viewmode === 'default' || $viewmode === 'combined' || $viewmode === 'categories'); 471 $displaycourselisting = ($viewmode === 'default' || $viewmode === 'combined' || $viewmode === 'courses'); 472 $displaycoursedetail = (isset($courseid)); 473 474 echo $renderer->header(); 475 476 if (!$issearching) { 477 echo $renderer->management_heading($strmanagement, $viewmode, $categoryid); 478 } else { 479 echo $renderer->management_heading(new lang_string('searchresults')); 480 } 481 482 if (count($notificationspass) > 0) { 483 echo $renderer->notification(join('<br />', $notificationspass), 'notifysuccess'); 484 } 485 if (count($notificationsfail) > 0) { 486 echo $renderer->notification(join('<br />', $notificationsfail)); 487 } 488 489 // Start the management form. 490 echo $renderer->management_form_start(); 491 492 echo $renderer->accessible_skipto_links($displaycategorylisting, $displaycourselisting, $displaycoursedetail); 493 494 echo $renderer->grid_start('course-category-listings', $class); 495 496 if ($displaycategorylisting) { 497 echo $renderer->grid_column_start($categorysize, 'category-listing'); 498 echo $renderer->category_listing($category); 499 echo $renderer->grid_column_end(); 500 } 501 if ($displaycourselisting) { 502 echo $renderer->grid_column_start($coursesize, 'course-listing'); 503 if (!$issearching) { 504 echo $renderer->course_listing($category, $course, $page, $perpage, $viewmode); 505 } else { 506 list($courses, $coursescount, $coursestotal) = 507 \core_course\management\helper::search_courses($search, $blocklist, $modulelist, $page, $perpage); 508 echo $renderer->search_listing($courses, $coursestotal, $course, $page, $perpage, $search); 509 } 510 echo $renderer->grid_column_end(); 511 if ($displaycoursedetail) { 512 echo $renderer->grid_column_start($detailssize, 'course-detail'); 513 echo $renderer->course_detail($course); 514 echo $renderer->grid_column_end(); 515 } 516 } 517 echo $renderer->grid_end(); 518 519 // End of the management form. 520 echo $renderer->management_form_end(); 521 echo $renderer->course_search_form($search); 522 523 echo $renderer->footer();
title
Description
Body
title
Description
Body
title
Description
Body
title
Body