2 // This file is part of Moodle - http://moodle.org/
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.
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
18 * Course and category management interfaces.
20 * @package core_course
21 * @copyright 2013 Sam Hemelryk
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 require_once('../config.php');
26 require_once($CFG->dirroot
.'/lib/coursecatlib.php');
27 require_once($CFG->dirroot
.'/course/lib.php');
29 $categoryid = optional_param('categoryid', null, PARAM_INT
);
30 $selectedcategoryid = optional_param('selectedcategoryid', null, PARAM_INT
);
31 $courseid = optional_param('courseid', null, PARAM_INT
);
32 $action = optional_param('action', false, PARAM_ALPHA
);
33 $page = optional_param('page', 0, PARAM_INT
);
34 $perpage = optional_param('perpage', null, PARAM_INT
);
35 $viewmode = optional_param('view', 'default', PARAM_ALPHA
); // Can be one of default, combined, courses, or categories.
37 // Search related params.
38 $search = optional_param('search', '', PARAM_RAW
); // Search words. Shortname, fullname, idnumber and summary get searched.
39 $blocklist = optional_param('blocklist', 0, PARAM_INT
); // Find courses containing this block.
40 $modulelist = optional_param('modulelist', '', PARAM_PLUGIN
); // Find courses containing the given modules.
42 if (!in_array($viewmode, array('default', 'combined', 'courses', 'categories'))) {
43 $viewmode = 'default';
46 $issearching = ($search !== '' ||
$blocklist !== 0 ||
$modulelist !== '');
48 $viewmode = 'courses';
51 $url = new moodle_url('/course/management.php');
52 $systemcontext = $context = context_system
::instance();
54 $record = get_course($courseid);
55 $course = new course_in_list($record);
56 $category = coursecat
::get($course->category
);
57 $categoryid = $category->id
;
58 $context = context_coursecat
::instance($category->id
);
59 $url->param('categoryid', $categoryid);
60 $url->param('courseid', $course->id
);
62 } else if ($categoryid) {
65 $category = coursecat
::get($categoryid);
66 $context = context_coursecat
::instance($category->id
);
67 $url->param('categoryid', $category->id
);
74 if ($viewmode === 'default') {
75 $viewmode = 'categories';
77 $context = $systemcontext;
80 // Check if there is a selected category param, and if there is apply it.
81 if ($course === null && $selectedcategoryid !== null && $selectedcategoryid !== $categoryid) {
82 $url->param('categoryid', $selectedcategoryid);
86 $url->param('page', $page);
88 if ($viewmode !== 'default') {
89 $url->param('view', $viewmode);
92 $url->param('search', $search);
94 if ($blocklist !== 0) {
95 $url->param('blocklist', $search);
97 if ($modulelist !== '') {
98 $url->param('modulelist', $search);
101 $strmanagement = new lang_string('coursecatmanagement');
102 $pageheading = format_string($SITE->fullname
, true, array('context' => $systemcontext));
104 $PAGE->set_context($context);
105 $PAGE->set_url($url);
106 $PAGE->set_pagelayout('admin');
107 $PAGE->set_title($strmanagement);
108 $PAGE->set_heading($pageheading);
110 // This is a system level page that operates on other contexts.
113 if (!coursecat
::has_capability_on_any(array('moodle/category:manage', 'moodle/course:create'))) {
114 // The user isn't able to manage any categories. Lets redirect them to the relevant course/index.php page.
115 $url = new moodle_url('/course/index.php');
117 $url->param('categoryid', $categoryid);
122 // If the user poses any of these capabilities then they will be able to see the admin
123 // tree and the management link within it.
124 // This is the most accurate form of navigation.
125 $capabilities = array(
126 'moodle/site:config',
127 'moodle/backup:backupcourse',
128 'moodle/category:manage',
129 'moodle/course:create',
130 'moodle/site:approvecourse'
132 if ($category && !has_any_capability($capabilities, $systemcontext)) {
133 // If the user doesn't poses any of these system capabilities then we're going to mark the manage link in the settings block
134 // as active, tell the page to ignore the active path and just build what the user would expect.
135 // This will at least give the page some relevant navigation.
136 navigation_node
::override_active_url(new moodle_url('/course/management.php', array('categoryid' => $category->id
)));
137 $PAGE->set_category_by_id($category->id
);
138 $PAGE->navbar
->ignore_active(true);
139 $PAGE->navbar
->add(get_string('coursemgmt', 'admin'), $PAGE->url
->out_omit_querystring());
141 // If user has system capabilities, make sure the "Manage courses and categories" item in Administration block is active.
142 navigation_node
::require_admin_tree();
143 navigation_node
::override_active_url(new moodle_url('/course/management.php'));
145 if (!$issearching && $category !== null) {
146 $parents = coursecat
::get_many($category->get_parents());
147 $parents[] = $category;
148 foreach ($parents as $parent) {
150 $parent->get_formatted_name(),
151 new moodle_url('/course/management.php', array('categoryid' => $parent->id
))
154 if ($course instanceof course_in_list
) {
155 // Use the list name so that it matches whats being displayed below.
156 $PAGE->navbar
->add($course->get_formatted_name());
160 $notificationspass = array();
161 $notificationsfail = array();
163 if ($action !== false && confirm_sesskey()) {
165 // - resortcategories : Resort the courses in the given category.
166 // - resortcourses : Resort courses
167 // - showcourse : make a course visible.
168 // - hidecourse : make a course hidden.
169 // - movecourseup : move the selected course up one.
170 // - movecoursedown : move the selected course down.
171 // - showcategory : make a category visible.
172 // - hidecategory : make a category hidden.
173 // - movecategoryup : move category up.
174 // - movecategorydown : move category down.
175 // - deletecategory : delete the category either in full, or moving contents.
176 // - bulkaction : performs bulk actions:
177 // - bulkmovecourses.
178 // - bulkmovecategories.
179 // - bulkresortcategories.
180 $redirectback = false;
181 $redirectmessage = false;
183 case 'resortcategories' :
184 $sort = required_param('resort', PARAM_ALPHA
);
185 $cattosort = coursecat
::get((int)optional_param('categoryid', 0, PARAM_INT
));
186 $redirectback = \core_course\management\helper
::action_category_resort_subcategories($cattosort, $sort);
188 case 'resortcourses' :
189 // They must have specified a category.
190 required_param('categoryid', PARAM_INT
);
191 $sort = required_param('resort', PARAM_ALPHA
);
192 \core_course\management\helper
::action_category_resort_courses($category, $sort);
195 $redirectback = \core_course\management\helper
::action_course_show($course);
198 $redirectback = \core_course\management\helper
::action_course_hide($course);
200 case 'movecourseup' :
201 // They must have specified a category and a course.
202 required_param('categoryid', PARAM_INT
);
203 required_param('courseid', PARAM_INT
);
204 $redirectback = \core_course\management\helper
::action_course_change_sortorder_up_one($course, $category);
206 case 'movecoursedown' :
207 // They must have specified a category and a course.
208 required_param('categoryid', PARAM_INT
);
209 required_param('courseid', PARAM_INT
);
210 $redirectback = \core_course\management\helper
::action_course_change_sortorder_down_one($course, $category);
212 case 'showcategory' :
213 // They must have specified a category.
214 required_param('categoryid', PARAM_INT
);
215 $redirectback = \core_course\management\helper
::action_category_show($category);
217 case 'hidecategory' :
218 // They must have specified a category.
219 required_param('categoryid', PARAM_INT
);
220 $redirectback = \core_course\management\helper
::action_category_hide($category);
222 case 'movecategoryup' :
223 // They must have specified a category.
224 required_param('categoryid', PARAM_INT
);
225 $redirectback = \core_course\management\helper
::action_category_change_sortorder_up_one($category);
227 case 'movecategorydown' :
228 // They must have specified a category.
229 required_param('categoryid', PARAM_INT
);
230 $redirectback = \core_course\management\helper
::action_category_change_sortorder_down_one($category);
232 case 'deletecategory':
233 // They must have specified a category.
234 required_param('categoryid', PARAM_INT
);
235 if (!$category->can_delete()) {
236 throw new moodle_exception('permissiondenied', 'error', '', null, 'coursecat::can_resort');
238 require_once($CFG->dirroot
.'/course/delete_category_form.php');
239 $mform = new core_course_deletecategory_form(null, $category);
240 if ($mform->is_cancelled()) {
241 redirect($PAGE->url
);
244 /* @var core_course_management_renderer|core_renderer $renderer */
245 $renderer = $PAGE->get_renderer('core_course', 'management');
246 echo $renderer->header();
247 echo $renderer->heading(get_string('deletecategory', 'moodle', $category->get_formatted_name()));
249 if ($data = $mform->get_data()) {
250 // The form has been submit handle it.
251 if ($data->fulldelete
== 1 && $category->can_delete_full()) {
252 $continueurl = new moodle_url('/course/management.php');
253 if ($category->parent
!= '0') {
254 $continueurl->param('categoryid', $category->parent
);
256 $notification = get_string('coursecategorydeleted', '', $category->get_formatted_name());
257 $deletedcourses = $category->delete_full(true);
258 foreach ($deletedcourses as $course) {
259 echo $renderer->notification(get_string('coursedeleted', '', $course->shortname
), 'notifysuccess');
261 echo $renderer->notification($notification, 'notifysuccess');
262 echo $renderer->continue_button($continueurl);
263 } else if ($data->fulldelete
== 0 && $category->can_move_content_to($data->newparent
)) {
264 $continueurl = new moodle_url('/course/management.php', array('categoryid' => $data->newparent
));
265 $category->delete_move($data->newparent
, true);
266 echo $renderer->continue_button($continueurl);
268 // Some error in parameters (user is cheating?)
275 // Finish output and exit.
276 echo $renderer->footer();
280 $bulkmovecourses = optional_param('bulkmovecourses', false, PARAM_BOOL
);
281 $bulkmovecategories = optional_param('bulkmovecategories', false, PARAM_BOOL
);
282 $bulkresortcategories = optional_param('bulksort', false, PARAM_BOOL
);
284 if ($bulkmovecourses) {
285 // Move courses out of the current category and into a new category.
286 // They must have specified a category.
287 required_param('categoryid', PARAM_INT
);
288 $movetoid = required_param('movecoursesto', PARAM_INT
);
289 $courseids = optional_param_array('bc', false, PARAM_INT
);
290 if ($courseids === false) {
293 $moveto = coursecat
::get($movetoid);
295 // If this fails we want to catch the exception and report it.
296 $redirectback = \core_course\management\helper
::action_category_move_courses_into($category, $moveto,
300 $a->category
= $moveto->get_formatted_name();
301 $a->courses
= count($courseids);
302 $redirectmessage = get_string('bulkmovecoursessuccess', 'moodle', $a);
304 } catch (moodle_exception
$ex) {
305 $redirectback = false;
306 $notificationsfail[] = $ex->getMessage();
308 } else if ($bulkmovecategories) {
309 $categoryids = optional_param_array('bcat', array(), PARAM_INT
);
310 $movetocatid = required_param('movecategoriesto', PARAM_INT
);
311 $movetocat = coursecat
::get($movetocatid);
313 foreach ($categoryids as $id) {
314 $cattomove = coursecat
::get($id);
315 if ($id == $movetocatid) {
316 $notificationsfail[] = get_string('movecategoryownparent', 'error', $cattomove->get_formatted_name());
319 if (strpos($movetocat->path
, $cattomove->path
) === 0) {
320 $notificationsfail[] = get_string('movecategoryparentconflict', 'error', $cattomove->get_formatted_name());
323 if ($cattomove->parent
!= $movetocatid) {
324 if ($cattomove->can_change_parent($movetocatid)) {
325 $cattomove->change_parent($movetocatid);
328 $notificationsfail[] = get_string('movecategorynotpossible', 'error', $cattomove->get_formatted_name());
332 if ($movecount > 1) {
334 $a->count
= $movecount;
335 $a->to
= $movetocat->get_formatted_name();
336 $movesuccessstrkey = 'movecategoriessuccess';
337 if ($movetocatid == 0) {
338 $movesuccessstrkey = 'movecategoriestotopsuccess';
340 $notificationspass[] = get_string($movesuccessstrkey, 'moodle', $a);
341 } else if ($movecount === 1) {
343 $a->moved
= $cattomove->get_formatted_name();
344 $a->to
= $movetocat->get_formatted_name();
345 $movesuccessstrkey = 'movecategorysuccess';
346 if ($movetocatid == 0) {
347 $movesuccessstrkey = 'movecategorytotopsuccess';
349 $notificationspass[] = get_string($movesuccessstrkey, 'moodle', $a);
351 } else if ($bulkresortcategories) {
352 $for = required_param('selectsortby', PARAM_ALPHA
);
353 $sortcategoriesby = required_param('resortcategoriesby', PARAM_ALPHA
);
354 $sortcoursesby = required_param('resortcoursesby', PARAM_ALPHA
);
356 if ($sortcategoriesby === 'none' && $sortcoursesby === 'none') {
357 // They're not sorting anything.
360 if (!in_array($sortcategoriesby, array('idnumber', 'name'))) {
361 $sortcategoriesby = false;
363 if (!in_array($sortcoursesby, array('idnumber', 'fullname', 'shortname'))) {
364 $sortcoursesby = false;
367 if ($for === 'thiscategory') {
368 $categoryids = array(
369 required_param('currentcategoryid', PARAM_INT
)
371 $categories = coursecat
::get_many($categoryids);
372 } else if ($for === 'selectedcategories') {
373 // Bulk resort selected categories.
374 $categoryids = optional_param_array('bcat', false, PARAM_INT
);
375 $sort = required_param('resortcategoriesby', PARAM_ALPHA
);
376 if ($categoryids === false) {
379 $categories = coursecat
::get_many($categoryids);
380 } else if ($for === 'allcategories') {
381 if ($sortcategoriesby && coursecat
::get(0)->can_resort_subcategories()) {
382 \core_course\management\helper
::action_category_resort_subcategories(coursecat
::get(0), $sortcategoriesby);
384 $categorieslist = coursecat
::make_categories_list('moodle/category:manage');
385 $categoryids = array_keys($categorieslist);
386 $categories = coursecat
::get_many($categoryids);
387 unset($categorieslist);
391 foreach ($categories as $cat) {
392 if ($sortcategoriesby && $cat->can_resort_subcategories()) {
393 // Don't clean up here, we'll do it once we're all done.
394 \core_course\management\helper
::action_category_resort_subcategories($cat, $sortcategoriesby, false);
396 if ($sortcoursesby && $cat->can_resort_courses()) {
397 \core_course\management\helper
::action_category_resort_courses($cat, $sortcoursesby, false);
400 coursecat
::resort_categories_cleanup($sortcoursesby !== false);
401 if ($category === null && count($categoryids) === 1) {
402 // They're bulk sorting just a single category and they've not selected a category.
403 // Lets for convenience sake auto-select the category that has been resorted for them.
404 redirect(new moodle_url($PAGE->url
, array('categoryid' => reset($categoryids))));
409 if ($redirectmessage) {
410 redirect($PAGE->url
, $redirectmessage, 5);
412 redirect($PAGE->url
);
417 if (!is_null($perpage)) {
418 set_user_preference('coursecat_management_perpage', $perpage);
420 $perpage = get_user_preferences('coursecat_management_perpage', $CFG->coursesperpage
);
422 if ((int)$perpage != $perpage ||
$perpage < 2) {
423 $perpage = $CFG->coursesperpage
;
429 if ($viewmode === 'default' ||
$viewmode === 'combined') {
430 if (isset($courseid)) {
431 $class = 'columns-3';
435 $class = 'columns-2';
437 } else if ($viewmode === 'categories') {
439 $class = 'columns-1';
440 } else if ($viewmode === 'courses') {
441 if (isset($courseid)) {
444 $class = 'columns-2';
447 $class = 'columns-1';
450 if ($viewmode === 'default' ||
$viewmode === 'combined') {
451 $class .= ' viewmode-cobmined';
453 $class .= ' viewmode-'.$viewmode;
455 if (($viewmode === 'default' ||
$viewmode === 'combined' ||
$viewmode === 'courses') && !empty($courseid)) {
456 $class .= ' course-selected';
459 /* @var core_course_management_renderer|core_renderer $renderer */
460 $renderer = $PAGE->get_renderer('core_course', 'management');
461 $renderer->enhance_management_interface();
463 $displaycategorylisting = ($viewmode === 'default' ||
$viewmode === 'combined' ||
$viewmode === 'categories');
464 $displaycourselisting = ($viewmode === 'default' ||
$viewmode === 'combined' ||
$viewmode === 'courses');
465 $displaycoursedetail = (isset($courseid));
467 echo $renderer->header();
470 echo $renderer->management_heading($strmanagement, $viewmode, $categoryid);
472 echo $renderer->management_heading(new lang_string('searchresults'));
475 if (count($notificationspass) > 0) {
476 echo $renderer->notification(join('<br />', $notificationspass), 'notifysuccess');
478 if (count($notificationsfail) > 0) {
479 echo $renderer->notification(join('<br />', $notificationsfail));
482 // Start the management form.
483 echo $renderer->management_form_start();
485 echo $renderer->accessible_skipto_links($displaycategorylisting, $displaycourselisting, $displaycoursedetail);
487 echo $renderer->grid_start('course-category-listings', $class);
489 if ($displaycategorylisting) {
490 echo $renderer->grid_column_start($categorysize, 'category-listing');
491 echo $renderer->category_listing($category);
492 echo $renderer->grid_column_end();
494 if ($displaycourselisting) {
495 echo $renderer->grid_column_start($coursesize, 'course-listing');
497 echo $renderer->course_listing($category, $course, $page, $perpage);
499 list($courses, $coursescount, $coursestotal) =
500 \core_course\management\helper
::search_courses($search, $blocklist, $modulelist, $page, $perpage);
501 echo $renderer->search_listing($courses, $coursestotal, $course, $page, $perpage);
503 echo $renderer->grid_column_end();
504 if ($displaycoursedetail) {
505 echo $renderer->grid_column_start($detailssize, 'course-detail');
506 echo $renderer->course_detail($course);
507 echo $renderer->grid_column_end();
510 echo $renderer->grid_end();
512 // End of the management form.
513 echo $renderer->management_form_end();
514 echo $renderer->footer();