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