1a66a739e816a6ee1616c15e85f9416a9fae33f6
[moodle.git] / course / manage.php
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/>.
17 /**
18  * Allows the admin to create, delete and rename course categories rearrange courses
19  *
20  * @package   core
21  * @copyright 2013 Marina Glancy
22  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  */
25 require_once("../config.php");
26 require_once($CFG->dirroot.'/course/lib.php');
27 require_once($CFG->libdir.'/coursecatlib.php');
29 // Category id.
30 $id = optional_param('categoryid', 0, PARAM_INT);
31 // Which page to show.
32 $page = optional_param('page', 0, PARAM_INT);
33 // How many per page.
34 $perpage = optional_param('perpage', $CFG->coursesperpage, PARAM_INT);
36 $search    = optional_param('search', '', PARAM_RAW);  // search words
37 $blocklist = optional_param('blocklist', 0, PARAM_INT);
38 $modulelist= optional_param('modulelist', '', PARAM_PLUGIN);
39 if (!$id && !empty($search)) {
40     $searchcriteria = array('search' => $search);
41 } else if (!$id && !empty($blocklist)) {
42     $searchcriteria = array('blocklist' => $blocklist);
43 } else if (!$id && !empty($modulelist)) {
44     $searchcriteria = array('modulelist' => $modulelist);
45 } else {
46     $searchcriteria = array();
47 }
49 // Actions to manage courses.
50 $hide = optional_param('hide', 0, PARAM_INT);
51 $show = optional_param('show', 0, PARAM_INT);
52 $moveup = optional_param('moveup', 0, PARAM_INT);
53 $movedown = optional_param('movedown', 0, PARAM_INT);
54 $moveto = optional_param('moveto', 0, PARAM_INT);
55 $resort = optional_param('resort', 0, PARAM_BOOL);
57 // Actions to manage categories.
58 $deletecat = optional_param('deletecat', 0, PARAM_INT);
59 $hidecat = optional_param('hidecat', 0, PARAM_INT);
60 $showcat = optional_param('showcat', 0, PARAM_INT);
61 $movecat = optional_param('movecat', 0, PARAM_INT);
62 $movetocat = optional_param('movetocat', -1, PARAM_INT);
63 $moveupcat = optional_param('moveupcat', 0, PARAM_INT);
64 $movedowncat = optional_param('movedowncat', 0, PARAM_INT);
66 require_login();
68 // Retrieve coursecat object
69 // This will also make sure that category is accessible and create default category if missing
70 $coursecat = coursecat::get($id);
72 if ($id) {
73     $PAGE->set_category_by_id($id);
74     $PAGE->set_url(new moodle_url('/course/manage.php', array('categoryid' => $id)));
75     // This is sure to be the category context.
76     $context = $PAGE->context;
77     if (!can_edit_in_category($coursecat->id)) {
78         redirect(new moodle_url('/course/index.php', array('categoryid' => $coursecat->id)));
79     }
80 } else {
81     $context = context_system::instance();
82     $PAGE->set_context($context);
83     $PAGE->set_url(new moodle_url('/course/manage.php'));
84     if (!can_edit_in_category()) {
85         redirect(new moodle_url('/course/index.php'));
86     }
87 }
89 $canmanage = has_capability('moodle/category:manage', $context);
91 // Process any category actions.
92 if (!empty($deletecat) and confirm_sesskey()) {
93     // Delete a category.
94     $cattodelete = coursecat::get($deletecat);
95     $context = context_coursecat::instance($deletecat);
96     require_capability('moodle/category:manage', $context);
97     require_capability('moodle/category:manage', get_category_or_system_context($cattodelete->parent));
99     $heading = get_string('deletecategory', 'moodle', format_string($cattodelete->name, true, array('context' => $context)));
101     require_once($CFG->dirroot.'/course/delete_category_form.php');
102     $mform = new delete_category_form(null, $cattodelete);
103     if ($mform->is_cancelled()) {
104         redirect(new moodle_url('/course/manage.php'));
105     }
107     // Start output.
108     echo $OUTPUT->header();
109     echo $OUTPUT->heading($heading);
111     if ($data = $mform->get_data()) {
112         // The form has been submit handle it.
113         if ($data->fulldelete == 1 && $cattodelete->can_delete_full()) {
114             $cattodeletename = $cattodelete->get_formatted_name();
115             $deletedcourses = $cattodelete->delete_full(true);
116             foreach ($deletedcourses as $course) {
117                 echo $OUTPUT->notification(get_string('coursedeleted', '', $course->shortname), 'notifysuccess');
118             }
119             echo $OUTPUT->notification(get_string('coursecategorydeleted', '', $cattodeletename), 'notifysuccess');
120             echo $OUTPUT->continue_button(new moodle_url('/course/manage.php'));
122         } else if ($data->fulldelete == 0 && $cattodelete->can_move_content_to($data->newparent)) {
123             $cattodelete->delete_move($data->newparent, true);
124             echo $OUTPUT->continue_button(new moodle_url('/course/manage.php'));
125         } else {
126             // Some error in parameters (user is cheating?)
127             $mform->display();
128         }
129     } else {
130         // Display the form.
131         $mform->display();
132     }
133     // Finish output and exit.
134     echo $OUTPUT->footer();
135     exit();
138 if (!empty($movecat) and ($movetocat >= 0) and confirm_sesskey()) {
139     // Move a category to a new parent if required.
140     $cattomove = coursecat::get($movecat);
141     if ($cattomove->parent != $movetocat) {
142         if ($cattomove->can_change_parent($movetocat)) {
143             $cattomove->change_parent($movetocat);
144         } else {
145             print_error('cannotmovecategory');
146         }
147     }
150 // Hide or show a category.
151 if ($hidecat and confirm_sesskey()) {
152     $cattohide = coursecat::get($hidecat);
153     require_capability('moodle/category:manage', get_category_or_system_context($cattohide->parent));
154     $cattohide->hide();
155 } else if ($showcat and confirm_sesskey()) {
156     $cattoshow = coursecat::get($showcat);
157     require_capability('moodle/category:manage', get_category_or_system_context($cattoshow->parent));
158     $cattoshow->show();
161 if ((!empty($moveupcat) or !empty($movedowncat)) and confirm_sesskey()) {
162     // Move a category up or down.
163     fix_course_sortorder();
164     $swapcategory = null;
166     if (!empty($moveupcat)) {
167         require_capability('moodle/category:manage', context_coursecat::instance($moveupcat));
168         if ($movecategory = $DB->get_record('course_categories', array('id' => $moveupcat))) {
169             $params = array($movecategory->sortorder, $movecategory->parent);
170             if ($swapcategory = $DB->get_records_select('course_categories', "sortorder<? AND parent=?", $params, 'sortorder DESC', '*', 0, 1)) {
171                 $swapcategory = reset($swapcategory);
172             }
173         }
174     } else {
175         require_capability('moodle/category:manage', context_coursecat::instance($movedowncat));
176         if ($movecategory = $DB->get_record('course_categories', array('id' => $movedowncat))) {
177             $params = array($movecategory->sortorder, $movecategory->parent);
178             if ($swapcategory = $DB->get_records_select('course_categories', "sortorder>? AND parent=?", $params, 'sortorder ASC', '*', 0, 1)) {
179                 $swapcategory = reset($swapcategory);
180             }
181         }
182     }
183     if ($swapcategory and $movecategory) {
184         $DB->set_field('course_categories', 'sortorder', $swapcategory->sortorder, array('id' => $movecategory->id));
185         $DB->set_field('course_categories', 'sortorder', $movecategory->sortorder, array('id' => $swapcategory->id));
186         cache_helper::purge_by_event('changesincoursecat');
187         add_to_log(SITEID, "category", "move", "editcategory.php?id=$movecategory->id", $movecategory->id);
188     }
190     // Finally reorder courses.
191     fix_course_sortorder();
194 if ($coursecat->id && $canmanage && $resort && confirm_sesskey()) {
195     // Resort the category.
196     if ($courses = get_courses($coursecat->id, '', 'c.id,c.fullname,c.sortorder')) {
197         core_collator::asort_objects_by_property($courses, 'fullname', core_collator::SORT_NATURAL);
198         $i = 1;
199         foreach ($courses as $course) {
200             $DB->set_field('course', 'sortorder', $coursecat->sortorder + $i, array('id' => $course->id));
201             $i++;
202         }
203         // This should not be needed but we do it just to be safe.
204         fix_course_sortorder();
205         cache_helper::purge_by_event('changesincourse');
206     }
209 if (!empty($moveto) && ($data = data_submitted()) && confirm_sesskey()) {
210     // Move a specified course to a new category.
211     // User must have category update in both cats to perform this.
212     require_capability('moodle/category:manage', $context);
213     require_capability('moodle/category:manage', context_coursecat::instance($moveto));
215     if (!$destcategory = $DB->get_record('course_categories', array('id' => $data->moveto))) {
216         print_error('cannotfindcategory', '', '', $data->moveto);
217     }
219     $courses = array();
220     foreach ($data as $key => $value) {
221         if (preg_match('/^c\d+$/', $key)) {
222             $courseid = substr($key, 1);
223             array_push($courses, $courseid);
224             // Check this course's category.
225             if ($movingcourse = $DB->get_record('course', array('id' => $courseid))) {
226                 if ($id && $movingcourse->category != $id ) {
227                     print_error('coursedoesnotbelongtocategory');
228                 }
229             } else {
230                 print_error('cannotfindcourse');
231             }
232         }
233     }
234     move_courses($courses, $data->moveto);
237 if ((!empty($hide) or !empty($show)) && confirm_sesskey()) {
238     // Hide or show a course.
239     if (!empty($hide)) {
240         $course = get_course($hide);
241         $visible = 0;
242     } else {
243         $course = get_course($show);
244         $visible = 1;
245     }
246     $coursecontext = context_course::instance($course->id);
247     require_capability('moodle/course:visibility', $coursecontext);
248     // Set the visibility of the course. we set the old flag when user manually changes visibility of course.
249     $params = array('id' => $course->id, 'visible' => $visible, 'visibleold' => $visible, 'timemodified' => time());
250     update_course_record((object)$params);
251     cache_helper::purge_by_event('changesincourse');
253     // Update the course object we pass to the event class.
254     $course->visible = $params['visible'];
255     $course->visibleold = $params['visibleold'];
256     $course->timemodified = $params['timemodified'];
258     // Trigger a course updated event.
259     $event = \core\event\course_updated::create(array(
260         'objectid' => $course->id,
261         'context' => $coursecontext,
262         'other' => array('shortname' => $course->shortname,
263                          'fullname' => $course->fullname)
264     ));
265     $event->add_record_snapshot('course', $course);
266     $event->set_legacy_logdata(array($course->id, 'course', ($visible ? 'show' : 'hide'), 'edit.php?id=' . $course->id, $course->id));
267     $event->trigger();
270 if ((!empty($moveup) or !empty($movedown)) && confirm_sesskey()) {
271     // Move a course up or down.
272     require_capability('moodle/category:manage', $context);
274     // Ensure the course order has continuous ordering.
275     fix_course_sortorder();
276     $swapcourse = null;
278     if (!empty($moveup)) {
279         if ($movecourse = $DB->get_record('course', array('id' => $moveup))) {
280             $swapcourse = $DB->get_record('course', array('sortorder' => $movecourse->sortorder - 1));
281         }
282     } else {
283         if ($movecourse = $DB->get_record('course', array('id' => $movedown))) {
284             $swapcourse = $DB->get_record('course', array('sortorder' => $movecourse->sortorder + 1));
285         }
286     }
287     if ($swapcourse and $movecourse) {
288         // Check course's category.
289         if ($movecourse->category != $id) {
290             print_error('coursedoesnotbelongtocategory');
291         }
292         $DB->set_field('course', 'sortorder', $swapcourse->sortorder, array('id' => $movecourse->id));
293         $DB->set_field('course', 'sortorder', $movecourse->sortorder, array('id' => $swapcourse->id));
294         cache_helper::purge_by_event('changesincourse');
296         // Update $movecourse's sortorder.
297         $movecourse->sortorder = $swapcourse->sortorder;
299         // Trigger a course updated event.
300         $event = \core\event\course_updated::create(array(
301             'objectid' => $movecourse->id,
302             'context' => context_course::instance($movecourse->id),
303             'other' => array('shortname' => $movecourse->shortname,
304                              'fullname' => $movecourse->fullname)
305         ));
306         $event->add_record_snapshot('course', $movecourse);
307         $event->set_legacy_logdata(array($movecourse->id, 'course', 'move', 'edit.php?id=' . $movecourse->id, $movecourse->id));
308         $event->trigger();
309     }
312 // Prepare the standard URL params for this page. We'll need them later.
313 $urlparams = array('categoryid' => $id);
314 if ($page) {
315     $urlparams['page'] = $page;
317 if ($perpage) {
318     $urlparams['perpage'] = $perpage;
320 $urlparams += $searchcriteria;
322 $PAGE->set_pagelayout('coursecategory');
323 $courserenderer = $PAGE->get_renderer('core', 'course');
325 if (can_edit_in_category()) {
326     // Integrate into the admin tree only if the user can edit categories at the top level,
327     // otherwise the admin block does not appear to this user, and you get an error.
328     require_once($CFG->libdir . '/adminlib.php');
329     if ($id) {
330         navigation_node::override_active_url(new moodle_url('/course/index.php', array('categoryid' => $id)));
331     }
332     admin_externalpage_setup('coursemgmt', '', $urlparams, $CFG->wwwroot . '/course/manage.php');
333     $settingsnode = $PAGE->settingsnav->find_active_node();
334     if ($id && $settingsnode) {
335         $settingsnode->make_inactive();
336         $settingsnode->force_open();
337         $PAGE->navbar->add($settingsnode->text, $settingsnode->action);
338     }
339 } else {
340     $site = get_site();
341     $PAGE->set_title("$site->shortname: $coursecat->name");
342     $PAGE->set_heading($site->fullname);
343     $PAGE->set_button($courserenderer->course_search_form('', 'navbar'));
346 // Start output.
347 echo $OUTPUT->header();
349 if (!empty($searchcriteria)) {
350     echo $OUTPUT->heading(new lang_string('searchresults'));
351 } else if (!$coursecat->id) {
352     // Print out the categories with all the knobs.
353     $table = new html_table;
354     $table->id = 'coursecategories';
355     $table->attributes['class'] = 'admintable generaltable editcourse';
356     $table->head = array(
357         get_string('categories'),
358         get_string('courses'),
359         get_string('edit'),
360         get_string('movecategoryto'),
361     );
362     $table->colclasses = array(
363         'leftalign name',
364         'centeralign count',
365         'centeralign icons',
366         'leftalign actions'
367     );
368     $table->data = array();
370     print_category_edit($table, $coursecat);
372     echo html_writer::table($table);
373 } else {
374     // Print the category selector.
375     $displaylist = coursecat::make_categories_list();
376     $select = new single_select(new moodle_url('/course/manage.php'), 'categoryid', $displaylist, $coursecat->id, null, 'switchcategory');
377     $select->set_label(get_string('categories').':');
379     echo html_writer::start_tag('div', array('class' => 'categorypicker'));
380     echo $OUTPUT->render($select);
381     echo html_writer::end_tag('div');
384 if ($canmanage && empty($searchcriteria)) {
385     echo $OUTPUT->container_start('buttons');
386     // Print button to update this category.
387     if ($id) {
388         $url = new moodle_url('/course/editcategory.php', array('id' => $id));
389         echo $OUTPUT->single_button($url, get_string('editcategorythis'), 'get');
390     }
392     // Print button for creating new categories.
393     $url = new moodle_url('/course/editcategory.php', array('parent' => $id));
394     if ($id) {
395         $title = get_string('addsubcategory');
396     } else {
397         $title = get_string('addnewcategory');
398     }
399     echo $OUTPUT->single_button($url, $title, 'get');
400     echo $OUTPUT->container_end();
403 if (!empty($searchcriteria)) {
404     $courses = coursecat::get(0)->search_courses($searchcriteria, array('recursive' => true,
405         'offset' => $page * $perpage, 'limit' => $perpage, 'sort' => array('fullname' => 1)));
406     $numcourses = count($courses);
407     $totalcount = coursecat::get(0)->search_courses_count($searchcriteria, array('recursive' => true));
408 } else if ($coursecat->id) {
409     // Print out all the sub-categories (plain mode).
410     // In order to view hidden subcategories the user must have the viewhiddencategories.
411     // capability in the current category..
412     if (has_capability('moodle/category:viewhiddencategories', $context)) {
413         $categorywhere = '';
414     } else {
415         $categorywhere = 'AND cc.visible = 1';
416     }
417     // We're going to preload the context for the subcategory as we know that we
418     // need it later on for formatting.
419     $ctxselect = context_helper::get_preload_record_columns_sql('ctx');
420     $sql = "SELECT cc.*, $ctxselect
421               FROM {course_categories} cc
422               JOIN {context} ctx ON cc.id = ctx.instanceid
423              WHERE cc.parent = :parentid AND
424                    ctx.contextlevel = :contextlevel
425                    $categorywhere
426           ORDER BY cc.sortorder ASC";
427     $subcategories = $DB->get_recordset_sql($sql, array('parentid' => $coursecat->id, 'contextlevel' => CONTEXT_COURSECAT));
428     // Prepare a table to display the sub categories.
429     $table = new html_table;
430     $table->attributes = array(
431         'border' => '0',
432         'cellspacing' => '2',
433         'cellpadding' => '4',
434         'class' => 'generalbox boxaligncenter category_subcategories'
435     );
436     $table->head = array(new lang_string('subcategories'));
437     $table->data = array();
438     $baseurl = new moodle_url('/course/manage.php');
439     foreach ($subcategories as $subcategory) {
440         // Preload the context we will need it to format the category name shortly.
441         context_helper::preload_from_record($subcategory);
442         $context = context_coursecat::instance($subcategory->id);
443         // Prepare the things we need to create a link to the subcategory.
444         $attributes = $subcategory->visible ? array() : array('class' => 'dimmed');
445         $text = format_string($subcategory->name, true, array('context' => $context));
446         // Add the subcategory to the table.
447         $baseurl->param('categoryid', $subcategory->id);
448         $table->data[] = array(html_writer::link($baseurl, $text, $attributes));
449     }
451     $subcategorieswereshown = (count($table->data) > 0);
452     if ($subcategorieswereshown) {
453         echo html_writer::table($table);
454     }
456     $courses = get_courses_page($coursecat->id, 'c.sortorder ASC',
457             'c.id,c.sortorder,c.shortname,c.fullname,c.summary,c.visible',
458             $totalcount, $page*$perpage, $perpage);
459     $numcourses = count($courses);
460 } else {
461     $subcategorieswereshown = true;
462     $courses = array();
463     $numcourses = $totalcount = 0;
466 if (!$courses) {
467     // There is no course to display.
468     if (empty($subcategorieswereshown)) {
469         echo $OUTPUT->heading(get_string("nocoursesyet"));
470     }
471 } else {
472     // Display a basic list of courses with paging/editing options.
473     $table = new html_table;
474     $table->attributes = array('border' => 0, 'cellspacing' => 0, 'cellpadding' => '4', 'class' => 'generalbox boxaligncenter');
475     $table->head = array(
476         get_string('courses'),
477         get_string('edit'),
478         get_string('select')
479     );
480     $table->colclasses = array(null, null, 'mdl-align');
481     if (!empty($searchcriteria)) {
482         // add 'Category' column
483         array_splice($table->head, 1, 0, array(get_string('category')));
484         array_splice($table->colclasses, 1, 0, array(null));
485     }
486     $table->data = array();
488     $count = 0;
489     $abletomovecourses = false;
491     // Checking if we are at the first or at the last page, to allow courses to
492     // be moved up and down beyond the paging border.
493     if ($totalcount > $perpage) {
494         $atfirstpage = ($page == 0);
495         if ($perpage > 0) {
496             $atlastpage = (($page + 1) == ceil($totalcount / $perpage));
497         } else {
498             $atlastpage = true;
499         }
500     } else {
501         $atfirstpage = true;
502         $atlastpage = true;
503     }
505     $baseurl = new moodle_url('/course/manage.php', $urlparams + array('sesskey' => sesskey()));
506     foreach ($courses as $acourse) {
507         $coursecontext = context_course::instance($acourse->id);
509         $count++;
510         $up = ($count > 1 || !$atfirstpage);
511         $down = ($count < $numcourses || !$atlastpage);
513         $courseurl = new moodle_url('/course/view.php', array('id' => $acourse->id));
514         $attributes = array();
515         $attributes['class'] = $acourse->visible ? '' : 'dimmed';
516         $coursename = get_course_display_name_for_list($acourse);
517         $coursename = format_string($coursename, true, array('context' => $coursecontext));
518         $coursename = html_writer::link($courseurl, $coursename, $attributes);
520         $icons = array();
521         // Update course icon.
522         if (has_capability('moodle/course:update', $coursecontext)) {
523             $url = new moodle_url('/course/edit.php', array('id' => $acourse->id, 'category' => $id, 'returnto' => 'catmanage'));
524             $icons[] = $OUTPUT->action_icon($url, new pix_icon('t/edit', get_string('settings')));
525         }
527         // Role assignment icon.
528         if (has_capability('moodle/course:enrolreview', $coursecontext)) {
529             $url = new moodle_url('/enrol/users.php', array('id' => $acourse->id));
530             $icons[] = $OUTPUT->action_icon($url, new pix_icon('t/enrolusers', get_string('enrolledusers', 'enrol')));
531         }
533         // Delete course icon.
534         if (can_delete_course($acourse->id)) {
535             $url = new moodle_url('/course/delete.php', array('id' => $acourse->id));
536             $icons[] = $OUTPUT->action_icon($url, new pix_icon('t/delete', get_string('delete')));
537         }
539         // Change visibility.
540         // Users with no capability to view hidden courses, should not be able to lock themselves out.
541         if (has_any_capability(array('moodle/course:visibility', 'moodle/course:viewhiddencourses'), $coursecontext)) {
542             if (!empty($acourse->visible)) {
543                 $url = new moodle_url($baseurl, array('hide' => $acourse->id));
544                 $icons[] = $OUTPUT->action_icon($url, new pix_icon('t/hide', get_string('hide')));
545             } else {
546                 $url = new moodle_url($baseurl, array('show' => $acourse->id));
547                 $icons[] = $OUTPUT->action_icon($url, new pix_icon('t/show', get_string('show')));
548             }
549         }
551         // Backup course icon.
552         if (has_capability('moodle/backup:backupcourse', $coursecontext)) {
553             $url = new moodle_url('/backup/backup.php', array('id' => $acourse->id));
554             $icons[] = $OUTPUT->action_icon($url, new pix_icon('t/backup', get_string('backup')));
555         }
557         // Restore course icon.
558         if (has_capability('moodle/restore:restorecourse', $coursecontext)) {
559             $url = new moodle_url('/backup/restorefile.php', array('contextid' => $coursecontext->id));
560             $icons[] = $OUTPUT->action_icon($url, new pix_icon('t/restore', get_string('restore')));
561         }
563         if ($canmanage) {
564             if ($up && empty($searchcriteria)) {
565                 $url = new moodle_url($baseurl, array('moveup' => $acourse->id));
566                 $icons[] = $OUTPUT->action_icon($url, new pix_icon('t/up', get_string('moveup')));
567             }
568             if ($down && empty($searchcriteria)) {
569                 $url = new moodle_url($baseurl, array('movedown' => $acourse->id));
570                 $icons[] = $OUTPUT->action_icon($url, new pix_icon('t/down', get_string('movedown')));
571             }
572             $abletomovecourses = true;
573         }
575         $table->data[] = new html_table_row(array(
576             new html_table_cell($coursename),
577             new html_table_cell(join('', $icons)),
578             new html_table_cell(html_writer::empty_tag('input', array('type' => 'checkbox', 'name' => 'c'.$acourse->id)))
579         ));
581         if (!empty($searchcriteria)) {
582             // add 'Category' column
583             $category = coursecat::get($acourse->category, IGNORE_MISSING, true);
584             $cell = new html_table_cell($category->get_formatted_name());
585             $cell->attributes['class'] = $category->visible ? '' : 'dimmed_text';
586             array_splice($table->data[count($table->data) - 1]->cells, 1, 0, array($cell));
587         }
588     }
590     if ($abletomovecourses) {
591         $movetocategories = coursecat::make_categories_list('moodle/category:manage');
592         $movetocategories[$id] = get_string('moveselectedcoursesto');
594         $cell = new html_table_cell();
595         $cell->colspan = 3;
596         $cell->attributes['class'] = 'mdl-right';
597         $cell->text = html_writer::label(get_string('moveselectedcoursesto'), 'movetoid', false, array('class' => 'accesshide'));
598         $cell->text .= html_writer::select($movetocategories, 'moveto', $id, null, array('id' => 'movetoid', 'class' => 'autosubmit'));
599         $cell->text .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'categoryid', 'value' => $id));
600         $PAGE->requires->yui_module('moodle-core-formautosubmit',
601             'M.core.init_formautosubmit',
602             array(array('selectid' => 'movetoid', 'nothing' => $id))
603         );
604         $table->data[] = new html_table_row(array($cell));
605     }
607     $actionurl = new moodle_url('/course/manage.php');
608     $pagingurl = new moodle_url('/course/manage.php', array('categoryid' => $id, 'perpage' => $perpage) + $searchcriteria);
610     echo $OUTPUT->paging_bar($totalcount, $page, $perpage, $pagingurl);
611     echo html_writer::start_tag('form', array('id' => 'movecourses', 'action' => $actionurl, 'method' => 'post'));
612     echo html_writer::start_tag('div');
613     echo html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'sesskey', 'value' => sesskey()));
614     foreach ($searchcriteria as $key => $value) {
615         echo html_writer::empty_tag('input', array('type' => 'hidden', 'name' => $key, 'value' => $value));
616     }
617     echo html_writer::table($table);
618     echo html_writer::end_tag('div');
619     echo html_writer::end_tag('form');
620     echo html_writer::empty_tag('br');
623 echo html_writer::start_tag('div', array('class' => 'buttons'));
624 if ($canmanage and $numcourses > 1 && empty($searchcriteria)) {
625     // Print button to re-sort courses by name.
626     $url = new moodle_url('/course/manage.php', array('categoryid' => $id, 'resort' => 'name', 'sesskey' => sesskey()));
627     echo $OUTPUT->single_button($url, get_string('resortcoursesbyname'), 'get');
630 if (has_capability('moodle/course:create', $context) && empty($searchcriteria)) {
631     // Print button to create a new course.
632     $url = new moodle_url('/course/edit.php');
633     if ($coursecat->id) {
634         $url->params(array('category' => $coursecat->id, 'returnto' => 'catmanage'));
635     } else {
636         $url->params(array('category' => $CFG->defaultrequestcategory, 'returnto' => 'topcatmanage'));
637     }
638     echo $OUTPUT->single_button($url, get_string('addnewcourse'), 'get');
641 if (!empty($CFG->enablecourserequests) && $id == $CFG->defaultrequestcategory) {
642     print_course_request_buttons(context_system::instance());
644 echo html_writer::end_tag('div');
646 echo $courserenderer->course_search_form();
648 echo $OUTPUT->footer();
650 /**
651  * Recursive function to print all the categories ready for editing.
652  *
653  * @param html_table $table The table to add data to.
654  * @param coursecat $category The category to render
655  * @param int $depth The depth of the category.
656  * @param bool $up True if this category can be moved up.
657  * @param bool $down True if this category can be moved down.
658  */
659 function print_category_edit(html_table $table, coursecat $category, $depth = -1, $up = false, $down = false) {
660     global $OUTPUT;
662     static $str = null;
664     if (is_null($str)) {
665         $str = new stdClass;
666         $str->edit = new lang_string('edit');
667         $str->delete = new lang_string('delete');
668         $str->moveup = new lang_string('moveup');
669         $str->movedown = new lang_string('movedown');
670         $str->edit = new lang_string('editthiscategory');
671         $str->hide = new lang_string('hide');
672         $str->show = new lang_string('show');
673         $str->cohorts = new lang_string('cohorts', 'cohort');
674         $str->spacer = $OUTPUT->spacer().' ';
675     }
677     if ($category->id) {
679         $categorycontext = context_coursecat::instance($category->id);
681         $attributes = array();
682         $attributes['class'] = $category->visible ? '' : 'dimmed';
683         $attributes['title'] = $str->edit;
684         $categoryurl = new moodle_url('/course/manage.php', array('categoryid' => $category->id, 'sesskey' => sesskey()));
685         $categoryname = $category->get_formatted_name();
686         $categorypadding = str_repeat('&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;', $depth);
687         $categoryname = $categorypadding . html_writer::link($categoryurl, $categoryname, $attributes);
689         $icons = array();
690         if (has_capability('moodle/category:manage', $categorycontext)) {
691             // Edit category.
692             $icons[] = $OUTPUT->action_icon(
693                 new moodle_url('/course/editcategory.php', array('id' => $category->id)),
694                 new pix_icon('t/edit', $str->edit, 'moodle', array('class' => 'iconsmall')),
695                 null, array('title' => $str->edit)
696             );
697             // Delete category.
698             $icons[] = $OUTPUT->action_icon(
699                 new moodle_url('/course/manage.php', array('deletecat' => $category->id, 'sesskey' => sesskey())),
700                 new pix_icon('t/delete', $str->delete, 'moodle', array('class' => 'iconsmall')),
701                 null, array('title' => $str->delete)
702             );
703             // Change visibility.
704             if (!empty($category->visible)) {
705                 $icons[] = $OUTPUT->action_icon(
706                     new moodle_url('/course/manage.php', array('hidecat' => $category->id, 'sesskey' => sesskey())),
707                     new pix_icon('t/hide', $str->hide, 'moodle', array('class' => 'iconsmall')),
708                     null, array('title' => $str->hide)
709                 );
710             } else {
711                 $icons[] = $OUTPUT->action_icon(
712                     new moodle_url('/course/manage.php', array('showcat' => $category->id, 'sesskey' => sesskey())),
713                     new pix_icon('t/show', $str->show, 'moodle', array('class' => 'iconsmall')),
714                     null, array('title' => $str->show)
715                 );
716             }
717             // Cohorts.
718             if (has_any_capability(array('moodle/cohort:manage', 'moodle/cohort:view'), $categorycontext)) {
719                 $icons[] = $OUTPUT->action_icon(
720                     new moodle_url('/cohort/index.php', array('contextid' => $categorycontext->id)),
721                     new pix_icon('t/cohort', $str->cohorts, 'moodle', array('class' => 'iconsmall')),
722                     null, array('title' => $str->cohorts)
723                 );
724             }
725             // Move up/down.
726             if ($up) {
727                 $icons[] = $OUTPUT->action_icon(
728                     new moodle_url('/course/manage.php', array('moveupcat' => $category->id, 'sesskey' => sesskey())),
729                     new pix_icon('t/up', $str->moveup, 'moodle', array('class' => 'iconsmall')),
730                     null, array('title' => $str->moveup)
731                 );
732             } else {
733                 $icons[] = $str->spacer;
734             }
735             if ($down) {
736                 $icons[] = $OUTPUT->action_icon(
737                     new moodle_url('/course/manage.php', array('movedowncat' => $category->id, 'sesskey' => sesskey())),
738                     new pix_icon('t/down', $str->movedown, 'moodle', array('class' => 'iconsmall')),
739                     null, array('title' => $str->movedown)
740                 );
741             } else {
742                 $icons[] = $str->spacer;
743             }
744         }
746         $actions = '';
747         if (has_capability('moodle/category:manage', $categorycontext)) {
748             $popupurl = new moodle_url('/course/manage.php', array('movecat' => $category->id, 'sesskey' => sesskey()));
749             $tempdisplaylist = array(0 => get_string('top')) + coursecat::make_categories_list('moodle/category:manage', $category->id);
750             $select = new single_select($popupurl, 'movetocat', $tempdisplaylist, $category->parent, null, "moveform$category->id");
751             $select->set_label(get_string('frontpagecategorynames'), array('class' => 'accesshide'));
752             $actions = $OUTPUT->render($select);
753         }
755         $table->data[] = new html_table_row(array(
756             // Category name.
757             new html_table_cell($categoryname),
758             // Course count.
759             new html_table_cell($category->coursecount),
760             // Icons.
761             new html_table_cell(join(' ', $icons)),
762             // Actions.
763             new html_table_cell($actions)
764         ));
765     }
767     if ($categories = $category->get_children()) {
768         // Print all the children recursively.
769         $countcats = count($categories);
770         $count = 0;
771         $first = true;
772         $last = false;
773         foreach ($categories as $cat) {
774             $count++;
775             if ($count == $countcats) {
776                 $last = true;
777             }
778             $up = $first ? false : true;
779             $down = $last ? false : true;
780             $first = false;
782             print_category_edit($table, $cat, $depth+1, $up, $down);
783         }
784     }