Commit | Line | Data |
---|---|---|
5dc361e1 SH |
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.'/lib/coursecatlib.php'); | |
27 | require_once($CFG->dirroot.'/course/lib.php'); | |
28 | ||
29 | $categoryid = optional_param('categoryid', 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 | $issearching = ($search !== '' || $blocklist !== 0 || $modulelist !== ''); | |
42 | if ($issearching) { | |
43 | $viewmode = 'courses'; | |
44 | } | |
45 | ||
46 | $url = new moodle_url('/course/management.php'); | |
47 | navigation_node::override_active_url($url); | |
b488058f | 48 | $systemcontext = $context = context_system::instance(); |
5dc361e1 SH |
49 | if ($courseid) { |
50 | $record = get_course($courseid); | |
51 | $course = new course_in_list($record); | |
52 | $category = coursecat::get($course->category); | |
53 | $categoryid = $category->id; | |
b488058f | 54 | $context = context_coursecat::instance($category->id); |
5dc361e1 SH |
55 | $url->param('courseid', $course->id); |
56 | } else if ($categoryid) { | |
57 | $courseid = null; | |
58 | $course = null; | |
59 | $category = coursecat::get($categoryid); | |
b488058f | 60 | $context = context_coursecat::instance($category->id); |
5dc361e1 SH |
61 | $url->param('categoryid', $category->id); |
62 | } else { | |
63 | $course = null; | |
64 | $courseid = null; | |
65 | $category = null; | |
66 | $categoryid = null; | |
67 | if ($viewmode === 'default') { | |
68 | $viewmode = 'categories'; | |
69 | } | |
b488058f | 70 | $context = $systemcontext; |
5dc361e1 SH |
71 | } |
72 | if ($page !== 0) { | |
73 | $url->param('page', $page); | |
74 | } | |
75 | if ($viewmode !== 'default') { | |
76 | $url->param('view', $viewmode); | |
77 | } | |
78 | if ($search !== '') { | |
79 | $url->param('search', $search); | |
80 | } | |
81 | if ($blocklist !== 0) { | |
82 | $url->param('blocklist', $search); | |
83 | } | |
84 | if ($modulelist !== '') { | |
85 | $url->param('modulelist', $search); | |
86 | } | |
87 | ||
b488058f | 88 | $title = format_string($SITE->fullname, true, array('context' => $systemcontext)); |
5dc361e1 SH |
89 | |
90 | $PAGE->set_context($context); | |
91 | $PAGE->set_url($url); | |
92 | $PAGE->set_pagelayout('admin'); | |
93 | $PAGE->set_title($title); | |
94 | $PAGE->set_heading($title); | |
95 | ||
b488058f SH |
96 | // If the user poses any of these capabilities then they will be able to see the admin |
97 | // tree and the management link within it. | |
98 | // This is the most accurate form of navigation. | |
99 | $capabilities = array( | |
100 | 'moodle/site:config', | |
101 | 'moodle/backup:backupcourse', | |
102 | 'moodle/category:manage', | |
103 | 'moodle/course:create', | |
104 | 'moodle/site:approvecourse' | |
105 | ); | |
106 | if ($category && !has_any_capability($capabilities, $systemcontext)) { | |
107 | // If the user doesn't poses any of these system capabilities then we're going to trigger | |
108 | // the page to set the specific category being viewed. | |
109 | // This will at least give the page some relevant navigation. | |
110 | $PAGE->set_category_by_id($category->id); | |
111 | navigation_node::override_active_url(new moodle_url('/course/management.php', array('categoryid' => $category->id))); | |
112 | } else if (!$issearching && $category !== null) { | |
5dc361e1 SH |
113 | $parents = coursecat::get_many($category->get_parents()); |
114 | foreach ($parents as $parent) { | |
115 | $PAGE->navbar->add($parent->get_formatted_name()); | |
116 | } | |
117 | $PAGE->navbar->add($category->get_formatted_name()); | |
118 | if ($course instanceof course_in_list) { | |
119 | // Use the list name so that it matches whats being displayed below. | |
120 | $PAGE->navbar->add($course->get_formatted_name()); | |
121 | } | |
122 | } | |
123 | ||
b488058f | 124 | // This is a system level page that operates on other contexts. |
5dc361e1 SH |
125 | require_login(); |
126 | ||
127 | $notificationspass = array(); | |
128 | $notificationsfail = array(); | |
129 | ||
130 | if ($action !== false && confirm_sesskey()) { | |
131 | // Actions: | |
132 | // - resortcategories : Resort the courses in the given category. | |
133 | // - resortcourses : Resort courses | |
134 | // - showcourse : make a course visible. | |
135 | // - hidecourse : make a course hidden. | |
136 | // - movecourseup : move the selected course up one. | |
137 | // - movecoursedown : move the selected course down. | |
138 | // - showcategory : make a category visible. | |
139 | // - hidecategory : make a category hidden. | |
140 | // - movecategoryup : move category up. | |
141 | // - movecategorydown : move category down. | |
142 | // - deletecategory : delete the category either in full, or moving contents. | |
143 | // - bulkaction : performs bulk actions: | |
144 | // - bulkmovecourses. | |
145 | // - bulkmovecategories. | |
146 | // - bulkresortcategories. | |
147 | $redirectback = false; | |
148 | switch ($action) { | |
149 | case 'resortcategories' : | |
150 | $sort = required_param('resort', PARAM_ALPHA); | |
b488058f SH |
151 | $cattosort = coursecat::get((int)optional_param('categoryid', 0, PARAM_INT)); |
152 | $redirectback = \core_course\management\helper::action_category_resort_subcategories($cattosort, $sort); | |
5dc361e1 SH |
153 | break; |
154 | case 'resortcourses' : | |
155 | // They must have specified a category. | |
156 | required_param('categoryid', PARAM_INT); | |
157 | $sort = required_param('resort', PARAM_ALPHA); | |
158 | \core_course\management\helper::action_category_resort_courses($category, $sort); | |
159 | break; | |
160 | case 'showcourse' : | |
161 | $redirectback = \core_course\management\helper::action_course_show($course); | |
162 | break; | |
163 | case 'hidecourse' : | |
164 | $redirectback = \core_course\management\helper::action_course_hide($course); | |
165 | break; | |
166 | case 'movecourseup' : | |
167 | // They must have specified a category and a course. | |
168 | required_param('categoryid', PARAM_INT); | |
169 | required_param('courseid', PARAM_INT); | |
5aff38e4 | 170 | $redirectback = \core_course\management\helper::action_course_change_sortorder_up_one($course, $category); |
5dc361e1 SH |
171 | break; |
172 | case 'movecoursedown' : | |
173 | // They must have specified a category and a course. | |
174 | required_param('categoryid', PARAM_INT); | |
175 | required_param('courseid', PARAM_INT); | |
5aff38e4 | 176 | $redirectback = \core_course\management\helper::action_course_change_sortorder_down_one($course, $category); |
5dc361e1 SH |
177 | break; |
178 | case 'showcategory' : | |
179 | // They must have specified a category. | |
180 | required_param('categoryid', PARAM_INT); | |
181 | $redirectback = \core_course\management\helper::action_category_show($category); | |
182 | break; | |
183 | case 'hidecategory' : | |
184 | // They must have specified a category. | |
185 | required_param('categoryid', PARAM_INT); | |
186 | $redirectback = \core_course\management\helper::action_category_hide($category); | |
187 | break; | |
188 | case 'movecategoryup' : | |
189 | // They must have specified a category. | |
190 | required_param('categoryid', PARAM_INT); | |
5aff38e4 | 191 | $redirectback = \core_course\management\helper::action_category_change_sortorder_up_one($category); |
5dc361e1 SH |
192 | break; |
193 | case 'movecategorydown' : | |
194 | // They must have specified a category. | |
195 | required_param('categoryid', PARAM_INT); | |
5aff38e4 | 196 | $redirectback = \core_course\management\helper::action_category_change_sortorder_down_one($category); |
5dc361e1 SH |
197 | break; |
198 | case 'deletecategory': | |
199 | // They must have specified a category. | |
200 | required_param('categoryid', PARAM_INT); | |
201 | if (!$category->can_delete()) { | |
202 | throw new moodle_exception('permissiondenied', 'error', '', null, 'coursecat::can_resort'); | |
203 | } | |
204 | require_once($CFG->dirroot.'/course/delete_category_form.php'); | |
67e1f268 | 205 | $mform = new core_course_deletecategory_form(null, $category); |
5dc361e1 SH |
206 | if ($mform->is_cancelled()) { |
207 | redirect(new moodle_url('/course/management.php')); | |
208 | } | |
209 | // Start output. | |
210 | /* @var core_course_management_renderer|core_renderer $renderer */ | |
211 | $renderer = $PAGE->get_renderer('core_course', 'management'); | |
212 | echo $renderer->header(); | |
213 | echo $renderer->heading(get_string('deletecategory', 'moodle', $category->get_formatted_name())); | |
214 | ||
215 | if ($data = $mform->get_data()) { | |
216 | // The form has been submit handle it. | |
217 | if ($data->fulldelete == 1 && $category->can_delete_full()) { | |
218 | $continueurl = new moodle_url('/course/management.php', array('categoryid' => $category->parent)); | |
219 | $notification = get_string('coursecategorydeleted', '', $category->get_formatted_name()); | |
220 | $deletedcourses = $category->delete_full(true); | |
221 | foreach ($deletedcourses as $course) { | |
222 | echo $renderer->notification(get_string('coursedeleted', '', $course->shortname), 'notifysuccess'); | |
223 | } | |
224 | echo $renderer->notification($notification, 'notifysuccess'); | |
225 | echo $renderer->continue_button($continueurl); | |
226 | } else if ($data->fulldelete == 0 && $category->can_move_content_to($data->newparent)) { | |
227 | $continueurl = new moodle_url('/course/management.php', array('categoryid' => $data->newparent)); | |
228 | $category->delete_move($data->newparent, true); | |
229 | echo $renderer->continue_button($continueurl); | |
230 | } else { | |
231 | // Some error in parameters (user is cheating?) | |
232 | $mform->display(); | |
233 | } | |
234 | } else { | |
235 | // Display the form. | |
236 | $mform->display(); | |
237 | } | |
238 | // Finish output and exit. | |
239 | echo $renderer->footer(); | |
240 | exit(); | |
241 | break; | |
242 | case 'bulkaction': | |
243 | $bulkmovecourses = optional_param('bulkmovecourses', false, PARAM_BOOL); | |
244 | $bulkmovecategories = optional_param('bulkmovecategories', false, PARAM_BOOL); | |
245 | $bulkresortcategories = optional_param('bulkresortcategories', false, PARAM_BOOL); | |
246 | ||
247 | if ($bulkmovecourses) { | |
248 | // Move courses out of the current category and into a new category. | |
249 | // They must have specified a category. | |
250 | required_param('categoryid', PARAM_INT); | |
251 | $movetoid = required_param('movecoursesto', PARAM_INT); | |
252 | $courseids = optional_param_array('bc', false, PARAM_INT); | |
253 | if ($courseids === false) { | |
254 | break; | |
255 | } | |
256 | $moveto = coursecat::get($movetoid); | |
67e1f268 SH |
257 | try { |
258 | // If this fails we want to catch the exception and report it. | |
259 | $redirectback = \core_course\management\helper::action_category_move_courses_into($category, $moveto, | |
260 | $courseids); | |
261 | } catch (moodle_exception $ex) { | |
262 | $redirectback = false; | |
263 | $notificationsfail[] = $ex->getMessage(); | |
264 | } | |
5dc361e1 SH |
265 | } else if ($bulkmovecategories) { |
266 | $categoryids = optional_param_array('bcat', false, PARAM_INT); | |
267 | $movetocatid = required_param('movecategoriesto', PARAM_INT); | |
268 | $movetocat = coursecat::get($movetocatid); | |
269 | $movecount = 0; | |
270 | foreach ($categoryids as $id) { | |
271 | $cattomove = coursecat::get($id); | |
272 | if ($id == $movetocatid) { | |
273 | $notificationsfail[] = get_string('movecategoryownparent', 'error', $cattomove->get_formatted_name()); | |
274 | continue; | |
275 | } | |
276 | if (strpos($movetocat->path, $cattomove->path) === 0) { | |
277 | $notificationsfail[] = get_string('movecategoryparentconflict', 'error', $cattomove->get_formatted_name()); | |
278 | continue; | |
279 | } | |
280 | if ($cattomove->parent != $movetocatid) { | |
281 | if ($cattomove->can_change_parent($movetocatid)) { | |
282 | $cattomove->change_parent($movetocatid); | |
283 | $movecount++; | |
284 | } else { | |
285 | $notificationsfail[] = get_string('movecategorynotpossible', 'error', $cattomove->get_formatted_name()); | |
286 | } | |
287 | } | |
288 | } | |
289 | if ($movecount > 1) { | |
290 | $a = new stdClass; | |
291 | $a->count = $movecount; | |
292 | $a->to = $movetocat->get_formatted_name(); | |
293 | $notificationspass[] = get_string('movecategoriessuccess', 'moodle', $a); | |
294 | } else if ($movecount === 1) { | |
295 | $a = new stdClass; | |
296 | $a->moved = $cattomove->get_formatted_name(); | |
297 | $a->to = $movetocat->get_formatted_name(); | |
298 | $notificationspass[] = get_string('movecategorysuccess', 'moodle', $a); | |
299 | } | |
300 | } else if ($bulkresortcategories) { | |
301 | // Bulk resort selected categories. | |
302 | $categoryids = optional_param_array('bcat', false, PARAM_INT); | |
303 | $sort = required_param('resortcategoriesby', PARAM_ALPHA); | |
304 | if ($categoryids === false) { | |
305 | break; | |
306 | } | |
307 | $categories = coursecat::get_many($categoryids); | |
308 | foreach ($categories as $cat) { | |
309 | // Don't clean up here, we'll do it once we're all done. | |
310 | \core_course\management\helper::action_category_resort_subcategories($cat, $sort, false); | |
311 | } | |
312 | coursecat::resort_categories_cleanup(); | |
b488058f SH |
313 | if ($category === null && count($categoryids) === 1) { |
314 | // They're bulk sorting just a single category and they've not selected a category. | |
315 | // Lets for convenience sake auto-select the category that has been resorted for them. | |
316 | redirect(new moodle_url($PAGE->url, array('categoryid' => reset($categoryids)))); | |
317 | } | |
5dc361e1 SH |
318 | } |
319 | } | |
320 | if ($redirectback) { | |
321 | redirect($PAGE->url); | |
322 | } | |
323 | } | |
324 | ||
325 | if (!is_null($perpage)) { | |
326 | set_user_preference('coursecat_management_perpage', $perpage); | |
327 | } else { | |
328 | $perpage = get_user_preferences('coursecat_management_perpage', $CFG->coursesperpage); | |
329 | } | |
330 | if ((int)$perpage != $perpage || $perpage < 2) { | |
331 | $perpage = $CFG->coursesperpage; | |
332 | } | |
333 | ||
334 | $categorysize = 4; | |
335 | $coursesize = 4; | |
336 | $detailssize = 4; | |
337 | if ($viewmode === 'default' || $viewmode === 'combined') { | |
338 | if (isset($courseid)) { | |
339 | $class = 'columns-3'; | |
340 | } else { | |
341 | $categorysize = 5; | |
342 | $coursesize = 7; | |
343 | $class = 'columns-2'; | |
344 | } | |
345 | } else if ($viewmode === 'categories') { | |
346 | $categorysize = 12; | |
347 | $class = 'columns-1'; | |
348 | } else if ($viewmode === 'courses') { | |
349 | if (isset($courseid)) { | |
350 | $coursesize = 6; | |
351 | $detailssize = 6; | |
352 | $class = 'columns-2'; | |
353 | } else { | |
354 | $coursesize = 12; | |
355 | $class = 'columns-1'; | |
356 | } | |
357 | } | |
358 | ||
359 | /* @var core_course_management_renderer|core_renderer $renderer */ | |
360 | $renderer = $PAGE->get_renderer('core_course', 'management'); | |
361 | $renderer->enhance_management_interface(); | |
362 | ||
363 | echo $renderer->header(); | |
364 | ||
365 | if (!$issearching) { | |
366 | echo $renderer->management_heading(new lang_string('coursecatmanagement'), $viewmode, $categoryid); | |
367 | } else { | |
368 | echo $renderer->management_heading(new lang_string('searchresults')); | |
369 | } | |
370 | ||
371 | if (count($notificationspass) > 0) { | |
372 | echo $renderer->notification(join('<br />', $notificationspass), 'notifysuccess'); | |
373 | } | |
374 | if (count($notificationsfail) > 0) { | |
375 | echo $renderer->notification(join('<br />', $notificationsfail)); | |
376 | } | |
377 | ||
378 | // Start the management form. | |
379 | echo $renderer->management_form_start(); | |
380 | ||
381 | echo $renderer->grid_start('course-category-listings', $class); | |
382 | if ($viewmode === 'default' || $viewmode === 'combined' || $viewmode === 'categories') { | |
383 | echo $renderer->grid_column_start($categorysize, 'category-listing'); | |
384 | echo $renderer->category_listing($category); | |
385 | echo $renderer->grid_column_end(); | |
386 | } | |
387 | if ($viewmode === 'default' || $viewmode === 'combined' || $viewmode === 'courses') { | |
388 | echo $renderer->grid_column_start($coursesize, 'course-listing'); | |
389 | if (!$issearching) { | |
390 | echo $renderer->course_listing($category, $course, $page, $perpage); | |
391 | } else { | |
392 | list($courses, $coursescount, $coursestotal) = | |
393 | \core_course\management\helper::search_courses($search, $blocklist, $modulelist, $page, $perpage); | |
394 | echo $renderer->search_listing($courses, $coursestotal, $course, $page, $perpage); | |
395 | } | |
396 | echo $renderer->grid_column_end(); | |
397 | if (isset($courseid)) { | |
398 | echo $renderer->grid_column_start($detailssize, 'course-detail'); | |
399 | echo $renderer->course_detail($course); | |
400 | echo $renderer->grid_column_end(); | |
401 | } | |
402 | } | |
403 | echo $renderer->grid_end(); | |
404 | ||
405 | if (!empty($CFG->enablecourserequests) && $id == $CFG->defaultrequestcategory) { | |
406 | print_course_request_buttons(context_system::instance()); | |
407 | } | |
408 | ||
409 | // End of the management form. | |
410 | echo $renderer->management_form_end(); | |
411 | echo $renderer->footer(); |