PageRenderTime 81ms CodeModel.GetById 27ms RepoModel.GetById 1ms app.codeStats 0ms

/course/category.php

https://github.com/thepurpleblob/gumoodle
PHP | 457 lines | 335 code | 59 blank | 63 comment | 83 complexity | ab59fbf2c5e6d46aff881eb201baa08f MD5 | raw file
Possible License(s): Apache-2.0, GPL-3.0, BSD-3-Clause, LGPL-2.1, AGPL-3.0, MPL-2.0-no-copyleft-exception, LGPL-3.0
  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. * Displays the top level category or all courses
  18. * In editing mode, allows the admin to edit a category,
  19. * and rearrange courses
  20. *
  21. * @package core
  22. * @subpackage course
  23. * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
  24. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  25. */
  26. require_once("../config.php");
  27. require_once($CFG->dirroot.'/course/lib.php');
  28. $id = required_param('id', PARAM_INT); // Category id
  29. $page = optional_param('page', 0, PARAM_INT); // which page to show
  30. $perpage = optional_param('perpage', $CFG->coursesperpage, PARAM_INT); // how many per page
  31. $categoryedit = optional_param('categoryedit', -1, PARAM_BOOL);
  32. $hide = optional_param('hide', 0, PARAM_INT);
  33. $show = optional_param('show', 0, PARAM_INT);
  34. $moveup = optional_param('moveup', 0, PARAM_INT);
  35. $movedown = optional_param('movedown', 0, PARAM_INT);
  36. $moveto = optional_param('moveto', 0, PARAM_INT);
  37. $resort = optional_param('resort', 0, PARAM_BOOL);
  38. $sesskey = optional_param('sesskey', '', PARAM_RAW);
  39. if (empty($id)) {
  40. print_error("unknowcategory");
  41. }
  42. $PAGE->set_category_by_id($id);
  43. $PAGE->set_url(new moodle_url('/course/category.php', array('id' => $id)));
  44. // This is sure to be the category context
  45. $context = $PAGE->context;
  46. // And the object has been loaded for us no need for another DB call
  47. $category = $PAGE->category;
  48. $canedit = can_edit_in_category($category->id);
  49. if ($canedit) {
  50. if ($categoryedit !== -1) {
  51. $USER->editing = $categoryedit;
  52. }
  53. require_login();
  54. $editingon = $PAGE->user_is_editing();
  55. } else {
  56. if ($CFG->forcelogin) {
  57. require_login();
  58. }
  59. $editingon = false;
  60. }
  61. if (!$category->visible) {
  62. require_capability('moodle/category:viewhiddencategories', $context);
  63. }
  64. $canmanage = has_capability('moodle/category:manage', $context);
  65. $sesskeyprovided = !empty($sesskey) && confirm_sesskey($sesskey);
  66. // Process any category actions.
  67. if ($canmanage && $resort && $sesskeyprovided) {
  68. // Resort the category if requested
  69. if ($courses = get_courses($category->id, "fullname ASC", 'c.id,c.fullname,c.sortorder')) {
  70. $i = 1;
  71. foreach ($courses as $course) {
  72. $DB->set_field('course', 'sortorder', $category->sortorder+$i, array('id'=>$course->id));
  73. $i++;
  74. }
  75. fix_course_sortorder(); // should not be needed
  76. }
  77. }
  78. // Process any course actions.
  79. if ($editingon && $sesskeyprovided) {
  80. // Move a specified course to a new category
  81. if (!empty($moveto) and $data = data_submitted()) {
  82. // Some courses are being moved
  83. // user must have category update in both cats to perform this
  84. require_capability('moodle/category:manage', $context);
  85. require_capability('moodle/category:manage', get_context_instance(CONTEXT_COURSECAT, $moveto));
  86. if (!$destcategory = $DB->get_record('course_categories', array('id' => $data->moveto))) {
  87. print_error('cannotfindcategory', '', '', $data->moveto);
  88. }
  89. $courses = array();
  90. foreach ($data as $key => $value) {
  91. if (preg_match('/^c\d+$/', $key)) {
  92. $courseid = substr($key, 1);
  93. array_push($courses, $courseid);
  94. // check this course's category
  95. if ($movingcourse = $DB->get_record('course', array('id'=>$courseid))) {
  96. if ($movingcourse->category != $id ) {
  97. print_error('coursedoesnotbelongtocategory');
  98. }
  99. } else {
  100. print_error('cannotfindcourse');
  101. }
  102. }
  103. }
  104. move_courses($courses, $data->moveto);
  105. }
  106. // Hide or show a course
  107. if (!empty($hide) or !empty($show)) {
  108. if (!empty($hide)) {
  109. $course = $DB->get_record('course', array('id' => $hide));
  110. $visible = 0;
  111. } else {
  112. $course = $DB->get_record('course', array('id' => $show));
  113. $visible = 1;
  114. }
  115. if ($course) {
  116. $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
  117. require_capability('moodle/course:visibility', $coursecontext);
  118. // Set the visibility of the course. we set the old flag when user manually changes visibility of course.
  119. $DB->update_record('course', array('id' => $course->id, 'visible' => $visible, 'visibleold' => $visible, 'timemodified' => time()));
  120. }
  121. }
  122. // Move a course up or down
  123. if (!empty($moveup) or !empty($movedown)) {
  124. require_capability('moodle/category:manage', $context);
  125. // Ensure the course order has continuous ordering
  126. fix_course_sortorder();
  127. $swapcourse = NULL;
  128. if (!empty($moveup)) {
  129. if ($movecourse = $DB->get_record('course', array('id' => $moveup))) {
  130. $swapcourse = $DB->get_record('course', array('sortorder' => $movecourse->sortorder - 1));
  131. }
  132. } else {
  133. if ($movecourse = $DB->get_record('course', array('id' => $movedown))) {
  134. $swapcourse = $DB->get_record('course', array('sortorder' => $movecourse->sortorder + 1));
  135. }
  136. }
  137. if ($swapcourse and $movecourse) {
  138. // check course's category
  139. if ($movecourse->category != $id) {
  140. print_error('coursedoesnotbelongtocategory');
  141. }
  142. $DB->set_field('course', 'sortorder', $swapcourse->sortorder, array('id' => $movecourse->id));
  143. $DB->set_field('course', 'sortorder', $movecourse->sortorder, array('id' => $swapcourse->id));
  144. }
  145. }
  146. } // End of editing stuff
  147. // Prepare the standard URL params for this page. We'll need them later.
  148. $urlparams = array('id' => $id);
  149. if ($page) {
  150. $urlparams['page'] = $page;
  151. }
  152. if ($perpage) {
  153. $urlparams['perpage'] = $perpage;
  154. }
  155. // Begin output
  156. if ($editingon && can_edit_in_category()) {
  157. // Integrate into the admin tree only if the user can edit categories at the top level,
  158. // otherwise the admin block does not appear to this user, and you get an error.
  159. require_once($CFG->libdir . '/adminlib.php');
  160. admin_externalpage_setup('coursemgmt', '', $urlparams, $CFG->wwwroot . '/course/category.php');
  161. $PAGE->set_context($context); // Ensure that we are actually showing blocks etc for the cat context
  162. $settingsnode = $PAGE->settingsnav->find_active_node();
  163. if ($settingsnode) {
  164. $settingsnode->make_inactive();
  165. $settingsnode->force_open();
  166. $PAGE->navbar->add($settingsnode->text, $settingsnode->action);
  167. }
  168. echo $OUTPUT->header();
  169. } else {
  170. $site = get_site();
  171. $PAGE->set_title("$site->shortname: $category->name");
  172. $PAGE->set_heading($site->fullname);
  173. $PAGE->set_button(print_course_search('', true, 'navbar'));
  174. $PAGE->set_pagelayout('coursecategory');
  175. echo $OUTPUT->header();
  176. }
  177. /// Print the category selector
  178. $displaylist = array();
  179. $notused = array();
  180. make_categories_list($displaylist, $notused);
  181. echo '<div class="categorypicker">';
  182. $select = new single_select(new moodle_url('/course/category.php'), 'id', $displaylist, $category->id, null, 'switchcategory');
  183. $select->set_label(get_string('categories').':');
  184. echo $OUTPUT->render($select);
  185. echo '</div>';
  186. /// Print current category description
  187. if (!$editingon && $category->description) {
  188. echo $OUTPUT->box_start();
  189. $options = new stdClass;
  190. $options->noclean = true;
  191. $options->para = false;
  192. $options->overflowdiv = true;
  193. if (!isset($category->descriptionformat)) {
  194. $category->descriptionformat = FORMAT_MOODLE;
  195. }
  196. $text = file_rewrite_pluginfile_urls($category->description, 'pluginfile.php', $context->id, 'coursecat', 'description', null);
  197. echo format_text($text, $category->descriptionformat, $options);
  198. echo $OUTPUT->box_end();
  199. }
  200. if ($editingon && $canmanage) {
  201. echo $OUTPUT->container_start('buttons');
  202. // Print button to update this category
  203. $url = new moodle_url('/course/editcategory.php', array('id' => $category->id));
  204. echo $OUTPUT->single_button($url, get_string('editcategorythis'), 'get');
  205. // Print button for creating new categories
  206. $url = new moodle_url('/course/editcategory.php', array('parent' => $category->id));
  207. echo $OUTPUT->single_button($url, get_string('addsubcategory'), 'get');
  208. echo $OUTPUT->container_end();
  209. }
  210. // Print out all the sub-categories
  211. // In order to view hidden subcategories the user must have the viewhiddencategories
  212. // capability in the current category.
  213. if (has_capability('moodle/category:viewhiddencategories', $context)) {
  214. $categorywhere = '';
  215. } else {
  216. $categorywhere = 'AND cc.visible = 1';
  217. }
  218. // We're going to preload the context for the subcategory as we know that we
  219. // need it later on for formatting.
  220. $ctxselect = context_helper::get_preload_record_columns_sql('ctx');
  221. $sql = "SELECT cc.*, $ctxselect
  222. FROM {course_categories} cc
  223. JOIN {context} ctx ON cc.id = ctx.instanceid
  224. WHERE cc.parent = :parentid AND
  225. ctx.contextlevel = :contextlevel
  226. $categorywhere
  227. ORDER BY cc.sortorder ASC";
  228. $subcategories = $DB->get_recordset_sql($sql, array('parentid' => $category->id, 'contextlevel' => CONTEXT_COURSECAT));
  229. // Prepare a table to display the sub categories.
  230. $table = new html_table;
  231. $table->attributes = array('border' => '0', 'cellspacing' => '2', 'cellpadding' => '4', 'class' => 'generalbox boxaligncenter category_subcategories');
  232. $table->head = array(new lang_string('subcategories'));
  233. $table->data = array();
  234. $baseurl = new moodle_url('/course/category.php');
  235. foreach ($subcategories as $subcategory) {
  236. // Preload the context we will need it to format the category name shortly.
  237. context_helper::preload_from_record($subcategory);
  238. $context = get_context_instance(CONTEXT_COURSECAT, $subcategory->id);
  239. // Prepare the things we need to create a link to the subcategory
  240. $attributes = $subcategory->visible ? array() : array('class' => 'dimmed');
  241. $text = format_string($subcategory->name, true, array('context' => $context));
  242. // Add the subcategory to the table
  243. $baseurl->param('id', $subcategory->id);
  244. $table->data[] = array(html_writer::link($baseurl, $text, $attributes));
  245. }
  246. $subcategorieswereshown = (count($table->data) > 0);
  247. if ($subcategorieswereshown) {
  248. echo html_writer::table($table);
  249. }
  250. // Print out all the courses
  251. $courses = get_courses_page($category->id, 'c.sortorder ASC',
  252. 'c.id,c.sortorder,c.shortname,c.fullname,c.summary,c.visible',
  253. $totalcount, $page*$perpage, $perpage);
  254. $numcourses = count($courses);
  255. if (!$courses) {
  256. if (empty($subcategorieswereshown)) {
  257. echo $OUTPUT->heading(get_string("nocoursesyet"));
  258. }
  259. } else if ($numcourses <= COURSE_MAX_SUMMARIES_PER_PAGE and !$page and !$editingon) {
  260. echo $OUTPUT->box_start('courseboxes');
  261. print_courses($category);
  262. echo $OUTPUT->box_end();
  263. } else {
  264. echo $OUTPUT->paging_bar($totalcount, $page, $perpage, "/course/category.php?id=$category->id&perpage=$perpage");
  265. echo '<form id="movecourses" action="category.php" method="post"><div>';
  266. echo '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
  267. echo '<table border="0" cellspacing="2" cellpadding="4" class="generalbox boxaligncenter"><tr>';
  268. echo '<th class="header" scope="col">'.get_string('courses').'</th>';
  269. if ($editingon) {
  270. echo '<th class="header" scope="col">'.get_string('edit').'</th>';
  271. echo '<th class="header" scope="col">'.get_string('select').'</th>';
  272. } else {
  273. echo '<th class="header" scope="col">&nbsp;</th>';
  274. }
  275. echo '</tr>';
  276. $count = 0;
  277. $abletomovecourses = false; // for now
  278. // Checking if we are at the first or at the last page, to allow courses to
  279. // be moved up and down beyond the paging border
  280. if ($totalcount > $perpage) {
  281. $atfirstpage = ($page == 0);
  282. if ($perpage > 0) {
  283. $atlastpage = (($page + 1) == ceil($totalcount / $perpage));
  284. } else {
  285. $atlastpage = true;
  286. }
  287. } else {
  288. $atfirstpage = true;
  289. $atlastpage = true;
  290. }
  291. $baseurl = new moodle_url('/course/category.php', $urlparams + array('sesskey' => sesskey()));
  292. foreach ($courses as $acourse) {
  293. $coursecontext = get_context_instance(CONTEXT_COURSE, $acourse->id);
  294. $count++;
  295. $up = ($count > 1 || !$atfirstpage);
  296. $down = ($count < $numcourses || !$atlastpage);
  297. $linkcss = $acourse->visible ? '' : ' class="dimmed" ';
  298. echo '<tr>';
  299. $coursename = get_course_display_name_for_list($acourse);
  300. echo '<td><a '.$linkcss.' href="view.php?id='.$acourse->id.'">'. format_string($coursename) .'</a></td>';
  301. if ($editingon) {
  302. echo '<td>';
  303. if (has_capability('moodle/course:update', $coursecontext)) {
  304. $url = new moodle_url('/course/edit.php', array('id' => $acourse->id, 'category' => $id, 'returnto' => 'category'));
  305. echo $OUTPUT->action_icon($url, new pix_icon('t/edit', get_string('settings')));
  306. }
  307. // role assignment link
  308. if (has_capability('moodle/course:enrolreview', $coursecontext)) {
  309. $url = new moodle_url('/enrol/users.php', array('id' => $acourse->id));
  310. echo $OUTPUT->action_icon($url, new pix_icon('i/users', get_string('enrolledusers', 'enrol')));
  311. }
  312. if (can_delete_course($acourse->id)) {
  313. $url = new moodle_url('/course/delete.php', array('id' => $acourse->id));
  314. echo $OUTPUT->action_icon($url, new pix_icon('t/delete', get_string('delete')));
  315. }
  316. // MDL-8885, users with no capability to view hidden courses, should not be able to lock themselves out
  317. if (has_capability('moodle/course:visibility', $coursecontext) && has_capability('moodle/course:viewhiddencourses', $coursecontext)) {
  318. if (!empty($acourse->visible)) {
  319. $url = new moodle_url($baseurl, array('hide' => $acourse->id));
  320. echo $OUTPUT->action_icon($url, new pix_icon('t/hide', get_string('hide')));
  321. } else {
  322. $url = new moodle_url($baseurl, array('show' => $acourse->id));
  323. echo $OUTPUT->action_icon($url, new pix_icon('t/show', get_string('show')));
  324. }
  325. }
  326. if (has_capability('moodle/backup:backupcourse', $coursecontext)) {
  327. $url = new moodle_url('/backup/backup.php', array('id' => $acourse->id));
  328. echo $OUTPUT->action_icon($url, new pix_icon('t/backup', get_string('backup')));
  329. }
  330. if (has_capability('moodle/restore:restorecourse', $coursecontext)) {
  331. $url = new moodle_url('/backup/restorefile.php', array('contextid' => $coursecontext->id));
  332. echo $OUTPUT->action_icon($url, new pix_icon('t/restore', get_string('restore')));
  333. }
  334. if ($canmanage) {
  335. if ($up) {
  336. $url = new moodle_url($baseurl, array('moveup' => $acourse->id));
  337. echo $OUTPUT->action_icon($url, new pix_icon('t/up', get_string('moveup')));
  338. }
  339. if ($down) {
  340. $url = new moodle_url($baseurl, array('movedown' => $acourse->id));
  341. echo $OUTPUT->action_icon($url, new pix_icon('t/down', get_string('movedown')));
  342. }
  343. $abletomovecourses = true;
  344. }
  345. echo '</td>';
  346. echo '<td align="center">';
  347. echo '<input type="checkbox" name="c'.$acourse->id.'" />';
  348. echo '</td>';
  349. } else {
  350. echo '<td align="right">';
  351. // print enrol info
  352. if ($icons = enrol_get_course_info_icons($acourse)) {
  353. foreach ($icons as $pix_icon) {
  354. echo $OUTPUT->render($pix_icon);
  355. }
  356. }
  357. if (!empty($acourse->summary)) {
  358. $url = new moodle_url("/course/info.php?id=$acourse->id");
  359. echo $OUTPUT->action_link($url, '<img alt="'.get_string('info').'" class="icon" src="'.$OUTPUT->pix_url('i/info') . '" />',
  360. new popup_action('click', $url, 'courseinfo'), array('title'=>get_string('summary')));
  361. }
  362. echo "</td>";
  363. }
  364. echo "</tr>";
  365. }
  366. if ($abletomovecourses) {
  367. $movetocategories = array();
  368. $notused = array();
  369. make_categories_list($movetocategories, $notused, 'moodle/category:manage');
  370. $movetocategories[$category->id] = get_string('moveselectedcoursesto');
  371. echo '<tr><td colspan="3" align="right">';
  372. echo html_writer::label(get_string('moveselectedcoursesto'), 'movetoid', false, array('class' => 'accesshide'));
  373. echo html_writer::select($movetocategories, 'moveto', $category->id, null, array('id'=>'movetoid'));
  374. $PAGE->requires->js_init_call('M.util.init_select_autosubmit', array('movecourses', 'movetoid', false));
  375. echo '<input type="hidden" name="id" value="'.$category->id.'" />';
  376. echo '</td></tr>';
  377. }
  378. echo '</table>';
  379. echo '</div></form>';
  380. echo '<br />';
  381. }
  382. echo '<div class="buttons">';
  383. if ($canmanage and $numcourses > 1) {
  384. // Print button to re-sort courses by name
  385. $url = new moodle_url('/course/category.php', array('id' => $category->id, 'resort' => 'name', 'sesskey' => sesskey()));
  386. echo $OUTPUT->single_button($url, get_string('resortcoursesbyname'), 'get');
  387. }
  388. if (has_capability('moodle/course:create', $context)) {
  389. // Print button to create a new course
  390. $url = new moodle_url('/course/edit.php', array('category' => $category->id, 'returnto' => 'category'));
  391. echo $OUTPUT->single_button($url, get_string('addnewcourse'), 'get');
  392. }
  393. if (!empty($CFG->enablecourserequests) && $category->id == $CFG->defaultrequestcategory) {
  394. print_course_request_buttons(get_context_instance(CONTEXT_SYSTEM));
  395. }
  396. echo '</div>';
  397. print_course_search();
  398. echo $OUTPUT->footer();