PageRenderTime 54ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/course/classes/management_renderer.php

https://github.com/bipulr/moodle
PHP | 1233 lines | 877 code | 78 blank | 278 comment | 126 complexity | 744633071b5b17eb38274e305b804b31 MD5 | raw file
Possible License(s): MIT, Apache-2.0, BSD-3-Clause, LGPL-3.0, GPL-3.0, LGPL-2.1

Large files files are truncated, but you can click here to view the full file

  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. * Contains renderers for the course management pages.
  18. *
  19. * @package core_course
  20. * @copyright 2013 Sam Hemelryk
  21. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  22. */
  23. defined('MOODLE_INTERNAL') || die;
  24. require_once($CFG->dirroot.'/course/renderer.php');
  25. /**
  26. * Main renderer for the course management pages.
  27. *
  28. * @package core_course
  29. * @copyright 2013 Sam Hemelryk
  30. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  31. */
  32. class core_course_management_renderer extends plugin_renderer_base {
  33. /**
  34. * Initialises the JS required to enhance the management interface.
  35. *
  36. * Thunderbirds are go, this function kicks into gear the JS that makes the
  37. * course management pages that much cooler.
  38. */
  39. public function enhance_management_interface() {
  40. $this->page->requires->yui_module('moodle-course-management', 'M.course.management.init');
  41. $this->page->requires->strings_for_js(
  42. array(
  43. 'show',
  44. 'showcategory',
  45. 'hide',
  46. 'expand',
  47. 'expandcategory',
  48. 'collapse',
  49. 'collapsecategory',
  50. 'confirmcoursemove',
  51. 'move',
  52. 'cancel',
  53. 'confirm'
  54. ),
  55. 'moodle'
  56. );
  57. }
  58. /**
  59. * Displays a heading for the management pages.
  60. *
  61. * @param string $heading The heading to display
  62. * @param string|null $viewmode The current view mode if there are options.
  63. * @param int|null $categoryid The currently selected category if there is one.
  64. * @return string
  65. */
  66. public function management_heading($heading, $viewmode = null, $categoryid = null) {
  67. $html = html_writer::start_div('coursecat-management-header clearfix');
  68. if (!empty($heading)) {
  69. $html .= $this->heading($heading);
  70. }
  71. if ($viewmode !== null) {
  72. $html .= html_writer::start_div();
  73. $html .= $this->view_mode_selector(\core_course\management\helper::get_management_viewmodes(), $viewmode);
  74. if ($viewmode === 'courses') {
  75. $categories = coursecat::make_categories_list(array('moodle/category:manage', 'moodle/course:create'));
  76. $nothing = false;
  77. if ($categoryid === null) {
  78. $nothing = array('' => get_string('selectacategory'));
  79. $categoryid = '';
  80. }
  81. $select = new single_select($this->page->url, 'categoryid', $categories, $categoryid, $nothing);
  82. $html .= $this->render($select);
  83. }
  84. $html .= html_writer::end_div();
  85. }
  86. $html .= html_writer::end_div();
  87. return $html;
  88. }
  89. /**
  90. * Prepares the form element for the course category listing bulk actions.
  91. *
  92. * @return string
  93. */
  94. public function management_form_start() {
  95. $form = array('action' => $this->page->url->out(), 'method' => 'POST', 'id' => 'coursecat-management');
  96. $html = html_writer::start_tag('form', $form);
  97. $html .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'sesskey', 'value' => sesskey()));
  98. $html .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'action', 'value' => 'bulkaction'));
  99. return $html;
  100. }
  101. /**
  102. * Closes the course category bulk management form.
  103. *
  104. * @return string
  105. */
  106. public function management_form_end() {
  107. return html_writer::end_tag('form');
  108. }
  109. /**
  110. * Presents a course category listing.
  111. *
  112. * @param coursecat $category The currently selected category. Also the category to highlight in the listing.
  113. * @return string
  114. */
  115. public function category_listing(coursecat $category = null) {
  116. if ($category === null) {
  117. $selectedparents = array();
  118. $selectedcategory = null;
  119. } else {
  120. $selectedparents = $category->get_parents();
  121. $selectedparents[] = $category->id;
  122. $selectedcategory = $category->id;
  123. }
  124. $catatlevel = \core_course\management\helper::get_expanded_categories('');
  125. $catatlevel[] = array_shift($selectedparents);
  126. $catatlevel = array_unique($catatlevel);
  127. $listing = coursecat::get(0)->get_children();
  128. $attributes = array(
  129. 'class' => 'ml',
  130. 'role' => 'tree',
  131. 'aria-labelledby' => 'category-listing-title'
  132. );
  133. $html = html_writer::start_div('category-listing');
  134. $html .= html_writer::tag('h3', get_string('categories'), array('id' => 'category-listing-title'));
  135. $html .= $this->category_listing_actions($category);
  136. $html .= html_writer::start_tag('ul', $attributes);
  137. foreach ($listing as $listitem) {
  138. // Render each category in the listing.
  139. $subcategories = array();
  140. if (in_array($listitem->id, $catatlevel)) {
  141. $subcategories = $listitem->get_children();
  142. }
  143. $html .= $this->category_listitem(
  144. $listitem,
  145. $subcategories,
  146. $listitem->get_children_count(),
  147. $selectedcategory,
  148. $selectedparents
  149. );
  150. }
  151. $html .= html_writer::end_tag('ul');
  152. $html .= $this->category_bulk_actions($category);
  153. $html .= html_writer::end_div();
  154. return $html;
  155. }
  156. /**
  157. * Renders a category list item.
  158. *
  159. * This function gets called recursively to render sub categories.
  160. *
  161. * @param coursecat $category The category to render as listitem.
  162. * @param coursecat[] $subcategories The subcategories belonging to the category being rented.
  163. * @param int $totalsubcategories The total number of sub categories.
  164. * @param int $selectedcategory The currently selected category
  165. * @param int[] $selectedcategories The path to the selected category and its ID.
  166. * @return string
  167. */
  168. public function category_listitem(coursecat $category, array $subcategories, $totalsubcategories,
  169. $selectedcategory = null, $selectedcategories = array()) {
  170. $isexpandable = ($totalsubcategories > 0);
  171. $isexpanded = (!empty($subcategories));
  172. $activecategory = ($selectedcategory === $category->id);
  173. $attributes = array(
  174. 'class' => 'listitem listitem-category',
  175. 'data-id' => $category->id,
  176. 'data-expandable' => $isexpandable ? '1' : '0',
  177. 'data-expanded' => $isexpanded ? '1' : '0',
  178. 'data-selected' => $activecategory ? '1' : '0',
  179. 'data-visible' => $category->visible ? '1' : '0',
  180. 'role' => 'treeitem',
  181. 'aria-expanded' => $isexpanded ? 'true' : 'false'
  182. );
  183. $text = $category->get_formatted_name();
  184. if ($category->parent) {
  185. $a = new stdClass;
  186. $a->category = $text;
  187. $a->parentcategory = $category->get_parent_coursecat()->get_formatted_name();
  188. $textlabel = get_string('categorysubcategoryof', 'moodle', $a);
  189. }
  190. $courseicon = $this->output->pix_icon('i/course', get_string('courses'));
  191. $bcatinput = array(
  192. 'type' => 'checkbox',
  193. 'name' => 'bcat[]',
  194. 'value' => $category->id,
  195. 'class' => 'bulk-action-checkbox',
  196. 'aria-label' => get_string('bulkactionselect', 'moodle', $text),
  197. 'data-action' => 'select'
  198. );
  199. if (!$category->can_resort_subcategories() && !$category->has_manage_capability()) {
  200. // Very very hardcoded here.
  201. $bcatinput['style'] = 'visibility:hidden';
  202. }
  203. $viewcaturl = new moodle_url('/course/management.php', array('categoryid' => $category->id));
  204. if ($isexpanded) {
  205. $icon = $this->output->pix_icon('t/switch_minus', get_string('collapse'), 'moodle', array('class' => 'tree-icon', 'title' => ''));
  206. $icon = html_writer::link(
  207. $viewcaturl,
  208. $icon,
  209. array(
  210. 'class' => 'float-left',
  211. 'data-action' => 'collapse',
  212. 'title' => get_string('collapsecategory', 'moodle', $text),
  213. 'aria-controls' => 'subcategoryof'.$category->id
  214. )
  215. );
  216. } else if ($isexpandable) {
  217. $icon = $this->output->pix_icon('t/switch_plus', get_string('expand'), 'moodle', array('class' => 'tree-icon', 'title' => ''));
  218. $icon = html_writer::link(
  219. $viewcaturl,
  220. $icon,
  221. array(
  222. 'class' => 'float-left',
  223. 'data-action' => 'expand',
  224. 'title' => get_string('expandcategory', 'moodle', $text)
  225. )
  226. );
  227. } else {
  228. $icon = $this->output->pix_icon(
  229. 'i/navigationitem',
  230. '',
  231. 'moodle',
  232. array('class' => 'tree-icon', 'title' => get_string('showcategory', 'moodle', $text))
  233. );
  234. $icon = html_writer::span($icon, 'float-left');
  235. }
  236. $actions = \core_course\management\helper::get_category_listitem_actions($category);
  237. $hasactions = !empty($actions) || $category->can_create_course();
  238. $html = html_writer::start_tag('li', $attributes);
  239. $html .= html_writer::start_div('clearfix');
  240. $html .= html_writer::start_div('float-left ba-checkbox');
  241. $html .= html_writer::empty_tag('input', $bcatinput).'&nbsp;';
  242. $html .= html_writer::end_div();
  243. $html .= $icon;
  244. if ($hasactions) {
  245. $textattributes = array('class' => 'float-left categoryname');
  246. } else {
  247. $textattributes = array('class' => 'float-left categoryname without-actions');
  248. }
  249. if (isset($textlabel)) {
  250. $textattributes['aria-label'] = $textlabel;
  251. }
  252. $html .= html_writer::link($viewcaturl, $text, $textattributes);
  253. $html .= html_writer::start_div('float-right');
  254. if ($category->idnumber) {
  255. $html .= html_writer::tag('span', s($category->idnumber), array('class' => 'dimmed idnumber'));
  256. }
  257. if ($hasactions) {
  258. $html .= $this->category_listitem_actions($category, $actions);
  259. }
  260. $countid = 'course-count-'.$category->id;
  261. $html .= html_writer::span(
  262. html_writer::span($category->get_courses_count()) .
  263. html_writer::span(get_string('courses'), 'accesshide', array('id' => $countid)) .
  264. $courseicon,
  265. 'course-count dimmed',
  266. array('aria-labelledby' => $countid)
  267. );
  268. $html .= html_writer::end_div();
  269. $html .= html_writer::end_div();
  270. if ($isexpanded) {
  271. $html .= html_writer::start_tag('ul',
  272. array('class' => 'ml', 'role' => 'group', 'id' => 'subcategoryof'.$category->id));
  273. $catatlevel = \core_course\management\helper::get_expanded_categories($category->path);
  274. $catatlevel[] = array_shift($selectedcategories);
  275. $catatlevel = array_unique($catatlevel);
  276. foreach ($subcategories as $listitem) {
  277. $childcategories = (in_array($listitem->id, $catatlevel)) ? $listitem->get_children() : array();
  278. $html .= $this->category_listitem(
  279. $listitem,
  280. $childcategories,
  281. $listitem->get_children_count(),
  282. $selectedcategory,
  283. $selectedcategories
  284. );
  285. }
  286. $html .= html_writer::end_tag('ul');
  287. }
  288. $html .= html_writer::end_tag('li');
  289. return $html;
  290. }
  291. /**
  292. * Renderers the actions that are possible for the course category listing.
  293. *
  294. * These are not the actions associated with an individual category listing.
  295. * That happens through category_listitem_actions.
  296. *
  297. * @param coursecat $category
  298. * @return string
  299. */
  300. public function category_listing_actions(coursecat $category = null) {
  301. $actions = array();
  302. $cancreatecategory = $category && $category->can_create_subcategory();
  303. $cancreatecategory = $cancreatecategory || coursecat::can_create_top_level_category();
  304. if ($category === null) {
  305. $category = coursecat::get(0);
  306. }
  307. if ($cancreatecategory) {
  308. $url = new moodle_url('/course/editcategory.php', array('parent' => $category->id));
  309. $actions[] = html_writer::link($url, get_string('createnewcategory'));
  310. }
  311. if (coursecat::can_approve_course_requests()) {
  312. $actions[] = html_writer::link(new moodle_url('/course/pending.php'), get_string('coursespending'));
  313. }
  314. if (count($actions) === 0) {
  315. return '';
  316. }
  317. return html_writer::div(join(' | ', $actions), 'listing-actions category-listing-actions');
  318. }
  319. /**
  320. * Renderers the actions for individual category list items.
  321. *
  322. * @param coursecat $category
  323. * @param array $actions
  324. * @return string
  325. */
  326. public function category_listitem_actions(coursecat $category, array $actions = null) {
  327. if ($actions === null) {
  328. $actions = \core_course\management\helper::get_category_listitem_actions($category);
  329. }
  330. $menu = new action_menu();
  331. $menu->attributes['class'] .= ' category-item-actions item-actions';
  332. $hasitems = false;
  333. foreach ($actions as $key => $action) {
  334. $hasitems = true;
  335. $menu->add(new action_menu_link(
  336. $action['url'],
  337. $action['icon'],
  338. $action['string'],
  339. in_array($key, array('show', 'hide', 'moveup', 'movedown')),
  340. array('data-action' => $key, 'class' => 'action-'.$key)
  341. ));
  342. }
  343. if (!$hasitems) {
  344. return '';
  345. }
  346. return $this->render($menu);
  347. }
  348. /**
  349. * Renders bulk actions for categories.
  350. *
  351. * @param coursecat $category The currently selected category if there is one.
  352. * @return string
  353. */
  354. public function category_bulk_actions(coursecat $category = null) {
  355. // Resort courses.
  356. // Change parent.
  357. if (!coursecat::can_resort_any() && !coursecat::can_change_parent_any()) {
  358. return '';
  359. }
  360. $strgo = new lang_string('go');
  361. $html = html_writer::start_div('category-bulk-actions bulk-actions');
  362. $html .= html_writer::div(get_string('categorybulkaction'), 'accesshide', array('tabindex' => '0'));
  363. if (coursecat::can_resort_any()) {
  364. $selectoptions = array(
  365. 'selectedcategories' => get_string('selectedcategories'),
  366. 'allcategories' => get_string('allcategories')
  367. );
  368. $form = html_writer::start_div();
  369. if ($category) {
  370. $selectoptions = array('thiscategory' => get_string('thiscategory')) + $selectoptions;
  371. $form .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'currentcategoryid', 'value' => $category->id));
  372. }
  373. $form .= html_writer::div(
  374. html_writer::select(
  375. $selectoptions,
  376. 'selectsortby',
  377. 'selectedcategories',
  378. false,
  379. array('aria-label' => get_string('selectcategorysort'))
  380. )
  381. );
  382. $form .= html_writer::div(
  383. html_writer::select(
  384. array(
  385. 'name' => get_string('sortcategoriesbyname'),
  386. 'idnumber' => get_string('sortcategoriesbyidnumber'),
  387. 'none' => get_string('dontsortcategories')
  388. ),
  389. 'resortcategoriesby',
  390. 'name',
  391. false,
  392. array('aria-label' => get_string('selectcategorysortby'))
  393. )
  394. );
  395. $form .= html_writer::div(
  396. html_writer::select(
  397. array(
  398. 'fullname' => get_string('sortcoursesbyfullname'),
  399. 'shortname' => get_string('sortcoursesbyshortname'),
  400. 'idnumber' => get_string('sortcoursesbyidnumber'),
  401. 'none' => get_string('dontsortcourses')
  402. ),
  403. 'resortcoursesby',
  404. 'fullname',
  405. false,
  406. array('aria-label' => get_string('selectcoursesortby'))
  407. )
  408. );
  409. $form .= html_writer::empty_tag('input', array('type' => 'submit', 'name' => 'bulksort', 'value' => get_string('sort')));
  410. $form .= html_writer::end_div();
  411. $html .= html_writer::start_div('detail-pair row yui3-g');
  412. $html .= html_writer::div(html_writer::span(get_string('sorting')), 'pair-key span3 yui3-u-1-4');
  413. $html .= html_writer::div($form, 'pair-value span9 yui3-u-3-4');
  414. $html .= html_writer::end_div();
  415. }
  416. if (coursecat::can_change_parent_any()) {
  417. $options = array();
  418. if (has_capability('moodle/category:manage', context_system::instance())) {
  419. $options[0] = coursecat::get(0)->get_formatted_name();
  420. }
  421. $options += coursecat::make_categories_list('moodle/category:manage');
  422. $select = html_writer::select(
  423. $options,
  424. 'movecategoriesto',
  425. '',
  426. array('' => 'choosedots'),
  427. array('aria-labelledby' => 'moveselectedcategoriesto')
  428. );
  429. $submit = array('type' => 'submit', 'name' => 'bulkmovecategories', 'value' => get_string('move'));
  430. $html .= $this->detail_pair(
  431. html_writer::span(get_string('moveselectedcategoriesto'), '', array('id' => 'moveselectedcategoriesto')),
  432. $select . html_writer::empty_tag('input', $submit)
  433. );
  434. }
  435. $html .= html_writer::end_div();
  436. return $html;
  437. }
  438. /**
  439. * Renders a course listing.
  440. *
  441. * @param coursecat $category The currently selected category. This is what the listing is focused on.
  442. * @param course_in_list $course The currently selected course.
  443. * @param int $page The page being displayed.
  444. * @param int $perpage The number of courses to display per page.
  445. * @return string
  446. */
  447. public function course_listing(coursecat $category = null, course_in_list $course = null, $page = 0, $perpage = 20) {
  448. if ($category === null) {
  449. $html = html_writer::start_div('select-a-category');
  450. $html .= html_writer::tag('h3', get_string('courses'),
  451. array('id' => 'course-listing-title', 'tabindex' => '0'));
  452. $html .= $this->output->notification(get_string('selectacategory'), 'notifymessage');
  453. $html .= html_writer::end_div();
  454. return $html;
  455. }
  456. $page = max($page, 0);
  457. $perpage = max($perpage, 2);
  458. $totalcourses = $category->coursecount;
  459. $totalpages = ceil($totalcourses / $perpage);
  460. if ($page > $totalpages - 1) {
  461. $page = $totalpages - 1;
  462. }
  463. $options = array(
  464. 'offset' => $page * $perpage,
  465. 'limit' => $perpage
  466. );
  467. $courseid = isset($course) ? $course->id : null;
  468. $class = '';
  469. if ($page === 0) {
  470. $class .= ' firstpage';
  471. }
  472. if ($page + 1 === (int)$totalpages) {
  473. $class .= ' lastpage';
  474. }
  475. $html = html_writer::start_div('course-listing'.$class, array(
  476. 'data-category' => $category->id,
  477. 'data-page' => $page,
  478. 'data-totalpages' => $totalpages,
  479. 'data-totalcourses' => $totalcourses,
  480. 'data-canmoveoutof' => $category->can_move_courses_out_of() && $category->can_move_courses_into()
  481. ));
  482. $html .= html_writer::tag('h3', $category->get_formatted_name(),
  483. array('id' => 'course-listing-title', 'tabindex' => '0'));
  484. $html .= $this->course_listing_actions($category, $course, $perpage);
  485. $html .= $this->listing_pagination($category, $page, $perpage);
  486. $html .= html_writer::start_tag('ul', array('class' => 'ml', 'role' => 'group'));
  487. foreach ($category->get_courses($options) as $listitem) {
  488. $html .= $this->course_listitem($category, $listitem, $courseid);
  489. }
  490. $html .= html_writer::end_tag('ul');
  491. $html .= $this->listing_pagination($category, $page, $perpage, true);
  492. $html .= $this->course_bulk_actions($category);
  493. $html .= html_writer::end_div();
  494. return $html;
  495. }
  496. /**
  497. * Renders pagination for a course listing.
  498. *
  499. * @param coursecat $category The category to produce pagination for.
  500. * @param int $page The current page.
  501. * @param int $perpage The number of courses to display per page.
  502. * @param bool $showtotals Set to true to show the total number of courses and what is being displayed.
  503. * @return string
  504. */
  505. protected function listing_pagination(coursecat $category, $page, $perpage, $showtotals = false) {
  506. $html = '';
  507. $totalcourses = $category->get_courses_count();
  508. $totalpages = ceil($totalcourses / $perpage);
  509. if ($showtotals) {
  510. if ($totalpages == 0) {
  511. $str = get_string('nocoursesyet');
  512. } else if ($totalpages == 1) {
  513. $str = get_string('showingacourses', 'moodle', $totalcourses);
  514. } else {
  515. $a = new stdClass;
  516. $a->start = ($page * $perpage) + 1;
  517. $a->end = min((($page + 1) * $perpage), $totalcourses);
  518. $a->total = $totalcourses;
  519. $str = get_string('showingxofycourses', 'moodle', $a);
  520. }
  521. $html .= html_writer::div($str, 'listing-pagination-totals dimmed');
  522. }
  523. if ($totalcourses <= $perpage) {
  524. return $html;
  525. }
  526. $aside = 2;
  527. $span = $aside * 2 + 1;
  528. $start = max($page - $aside, 0);
  529. $end = min($page + $aside, $totalpages - 1);
  530. if (($end - $start) < $span) {
  531. if ($start == 0) {
  532. $end = min($totalpages - 1, $span - 1);
  533. } else if ($end == ($totalpages - 1)) {
  534. $start = max(0, $end - $span + 1);
  535. }
  536. }
  537. $items = array();
  538. $baseurl = new moodle_url('/course/management.php', array('categoryid' => $category->id));
  539. if ($page > 0) {
  540. $items[] = $this->action_button(new moodle_url($baseurl, array('page' => 0)), get_string('first'));
  541. $items[] = $this->action_button(new moodle_url($baseurl, array('page' => $page - 1)), get_string('prev'));
  542. $items[] = '...';
  543. }
  544. for ($i = $start; $i <= $end; $i++) {
  545. $class = '';
  546. if ($page == $i) {
  547. $class = 'active-page';
  548. }
  549. $pageurl = new moodle_url($baseurl, array('page' => $i));
  550. $items[] = $this->action_button($pageurl, $i + 1, null, $class, get_string('pagea', 'moodle', $i+1));
  551. }
  552. if ($page < ($totalpages - 1)) {
  553. $items[] = '...';
  554. $items[] = $this->action_button(new moodle_url($baseurl, array('page' => $page + 1)), get_string('next'));
  555. $items[] = $this->action_button(new moodle_url($baseurl, array('page' => $totalpages - 1)), get_string('last'));
  556. }
  557. $html .= html_writer::div(join('', $items), 'listing-pagination');
  558. return $html;
  559. }
  560. /**
  561. * Renderers a course list item.
  562. *
  563. * This function will be called for every course being displayed by course_listing.
  564. *
  565. * @param coursecat $category The currently selected category and the category the course belongs to.
  566. * @param course_in_list $course The course to produce HTML for.
  567. * @param int $selectedcourse The id of the currently selected course.
  568. * @return string
  569. */
  570. public function course_listitem(coursecat $category, course_in_list $course, $selectedcourse) {
  571. $text = $course->get_formatted_name();
  572. $attributes = array(
  573. 'class' => 'listitem listitem-course',
  574. 'data-id' => $course->id,
  575. 'data-selected' => ($selectedcourse == $course->id) ? '1' : '0',
  576. 'data-visible' => $course->visible ? '1' : '0'
  577. );
  578. $bulkcourseinput = array(
  579. 'type' => 'checkbox',
  580. 'name' => 'bc[]',
  581. 'value' => $course->id,
  582. 'class' => 'bulk-action-checkbox',
  583. 'aria-label' => get_string('bulkactionselect', 'moodle', $text),
  584. 'data-action' => 'select'
  585. );
  586. if (!$category->has_manage_capability()) {
  587. // Very very hardcoded here.
  588. $bulkcourseinput['style'] = 'visibility:hidden';
  589. }
  590. $viewcourseurl = new moodle_url($this->page->url, array('courseid' => $course->id));
  591. $html = html_writer::start_tag('li', $attributes);
  592. $html .= html_writer::start_div('clearfix');
  593. if ($category->can_resort_courses()) {
  594. // In order for dnd to be available the user must be able to resort the category children..
  595. $html .= html_writer::div($this->output->pix_icon('i/move_2d', get_string('dndcourse')), 'float-left drag-handle');
  596. }
  597. $html .= html_writer::start_div('ba-checkbox float-left');
  598. $html .= html_writer::empty_tag('input', $bulkcourseinput).'&nbsp;';
  599. $html .= html_writer::end_div();
  600. $html .= html_writer::link($viewcourseurl, $text, array('class' => 'float-left coursename'));
  601. $html .= html_writer::start_div('float-right');
  602. if ($course->idnumber) {
  603. $html .= html_writer::tag('span', s($course->idnumber), array('class' => 'dimmed idnumber'));
  604. }
  605. $html .= $this->course_listitem_actions($category, $course);
  606. $html .= html_writer::end_div();
  607. $html .= html_writer::end_div();
  608. $html .= html_writer::end_tag('li');
  609. return $html;
  610. }
  611. /**
  612. * Renderers actions for the course listing.
  613. *
  614. * Not to be confused with course_listitem_actions which renderers the actions for individual courses.
  615. *
  616. * @param coursecat $category
  617. * @param course_in_list $course The currently selected course.
  618. * @param int $perpage
  619. * @return string
  620. */
  621. public function course_listing_actions(coursecat $category, course_in_list $course = null, $perpage = 20) {
  622. $actions = array();
  623. if ($category->can_create_course()) {
  624. $url = new moodle_url('/course/edit.php', array('category' => $category->id, 'returnto' => 'catmanage'));
  625. $actions[] = html_writer::link($url, get_string('createnewcourse'));
  626. }
  627. if ($category->can_request_course()) {
  628. // Request a new course.
  629. $url = new moodle_url('/course/request.php', array('return' => 'management'));
  630. $actions[] = html_writer::link($url, get_string('requestcourse'));
  631. }
  632. if ($category->can_resort_courses()) {
  633. $params = $this->page->url->params();
  634. $params['action'] = 'resortcourses';
  635. $params['sesskey'] = sesskey();
  636. $baseurl = new moodle_url('/course/management.php', $params);
  637. $fullnameurl = new moodle_url($baseurl, array('resort' => 'fullname'));
  638. $shortnameurl = new moodle_url($baseurl, array('resort' => 'shortname'));
  639. $idnumberurl = new moodle_url($baseurl, array('resort' => 'idnumber'));
  640. $menu = new action_menu(array(
  641. new action_menu_link_secondary($fullnameurl, null, get_string('resortbyfullname')),
  642. new action_menu_link_secondary($shortnameurl, null, get_string('resortbyshortname')),
  643. new action_menu_link_secondary($idnumberurl, null, get_string('resortbyidnumber'))
  644. ));
  645. $menu->set_menu_trigger(get_string('resortcourses'));
  646. $actions[] = $this->render($menu);
  647. }
  648. $strall = get_string('all');
  649. $menu = new action_menu(array(
  650. new action_menu_link_secondary(new moodle_url($this->page->url, array('perpage' => 5)), null, 5),
  651. new action_menu_link_secondary(new moodle_url($this->page->url, array('perpage' => 10)), null, 10),
  652. new action_menu_link_secondary(new moodle_url($this->page->url, array('perpage' => 20)), null, 20),
  653. new action_menu_link_secondary(new moodle_url($this->page->url, array('perpage' => 50)), null, 50),
  654. new action_menu_link_secondary(new moodle_url($this->page->url, array('perpage' => 100)), null, 100),
  655. new action_menu_link_secondary(new moodle_url($this->page->url, array('perpage' => 999)), null, $strall),
  656. ));
  657. if ((int)$perpage === 999) {
  658. $perpage = $strall;
  659. }
  660. $menu->attributes['class'] .= ' courses-per-page';
  661. $menu->set_menu_trigger(get_string('perpagea', 'moodle', $perpage));
  662. $actions[] = $this->render($menu);
  663. return html_writer::div(join(' | ', $actions), 'listing-actions course-listing-actions');
  664. }
  665. /**
  666. * Renderers actions for individual course actions.
  667. *
  668. * @param coursecat $category The currently selected category.
  669. * @param course_in_list $course The course to renderer actions for.
  670. * @return string
  671. */
  672. public function course_listitem_actions(coursecat $category, course_in_list $course) {
  673. $actions = \core_course\management\helper::get_course_listitem_actions($category, $course);
  674. if (empty($actions)) {
  675. return '';
  676. }
  677. $actionshtml = array();
  678. foreach ($actions as $action) {
  679. $action['attributes']['role'] = 'button';
  680. $actionshtml[] = $this->output->action_icon($action['url'], $action['icon'], null, $action['attributes']);
  681. }
  682. return html_writer::span(join('', $actionshtml), 'course-item-actions item-actions');
  683. }
  684. /**
  685. * Renderers bulk actions that can be performed on courses.
  686. *
  687. * @param coursecat $category The currently selected category and the category in which courses that
  688. * are selectable belong.
  689. * @return string
  690. */
  691. public function course_bulk_actions(coursecat $category) {
  692. $html = html_writer::start_div('course-bulk-actions bulk-actions');
  693. if ($category->can_move_courses_out_of()) {
  694. $html .= html_writer::div(get_string('coursebulkaction'), 'accesshide', array('tabindex' => '0'));
  695. $options = coursecat::make_categories_list('moodle/category:manage');
  696. $select = html_writer::select(
  697. $options,
  698. 'movecoursesto',
  699. '',
  700. array('' => 'choosedots'),
  701. array('aria-labelledby' => 'moveselectedcoursesto')
  702. );
  703. $submit = array('type' => 'submit', 'name' => 'bulkmovecourses', 'value' => get_string('move'));
  704. $html .= $this->detail_pair(
  705. html_writer::span(get_string('moveselectedcoursesto'), '', array('id' => 'moveselectedcoursesto')),
  706. $select . html_writer::empty_tag('input', $submit)
  707. );
  708. }
  709. $html .= html_writer::end_div();
  710. return $html;
  711. }
  712. /**
  713. * Renderers detailed course information.
  714. *
  715. * @param course_in_list $course The course to display details for.
  716. * @return string
  717. */
  718. public function course_detail(course_in_list $course) {
  719. $details = \core_course\management\helper::get_course_detail_array($course);
  720. $fullname = $details['fullname']['value'];
  721. $html = html_writer::start_div('course-detail');
  722. $html .= html_writer::tag('h3', $fullname, array('id' => 'course-detail-title', 'tabindex' => '0'));
  723. $html .= $this->course_detail_actions($course);
  724. foreach ($details as $class => $data) {
  725. $html .= $this->detail_pair($data['key'], $data['value'], $class);
  726. }
  727. $html .= html_writer::end_div();
  728. return $html;
  729. }
  730. /**
  731. * Renderers a key value pair of information for display.
  732. *
  733. * @param string $key
  734. * @param string $value
  735. * @param string $class
  736. * @return string
  737. */
  738. protected function detail_pair($key, $value, $class ='') {
  739. $html = html_writer::start_div('detail-pair row yui3-g '.preg_replace('#[^a-zA-Z0-9_\-]#', '-', $class));
  740. $html .= html_writer::div(html_writer::span($key), 'pair-key span3 yui3-u-1-4');
  741. $html .= html_writer::div(html_writer::span($value), 'pair-value span9 yui3-u-3-4');
  742. $html .= html_writer::end_div();
  743. return $html;
  744. }
  745. /**
  746. * A collection of actions for a course.
  747. *
  748. * @param course_in_list $course The course to display actions for.
  749. * @return string
  750. */
  751. public function course_detail_actions(course_in_list $course) {
  752. $actions = \core_course\management\helper::get_course_detail_actions($course);
  753. if (empty($actions)) {
  754. return '';
  755. }
  756. $options = array();
  757. foreach ($actions as $action) {
  758. $options[] = $this->action_link($action['url'], $action['string']);
  759. }
  760. return html_writer::div(join(' | ', $options), 'listing-actions course-detail-listing-actions');
  761. }
  762. /**
  763. * Creates an action button (styled link)
  764. *
  765. * @param moodle_url $url The URL to go to when clicked.
  766. * @param string $text The text for the button.
  767. * @param string $id An id to give the button.
  768. * @param string $class A class to give the button.
  769. * @param array $attributes Any additional attributes
  770. * @return string
  771. */
  772. protected function action_button(moodle_url $url, $text, $id = null, $class = null, $title = null, array $attributes = array()) {
  773. if (isset($attributes['class'])) {
  774. $attributes['class'] .= ' yui3-button';
  775. } else {
  776. $attributes['class'] = 'yui3-button';
  777. }
  778. if (!is_null($id)) {
  779. $attributes['id'] = $id;
  780. }
  781. if (!is_null($class)) {
  782. $attributes['class'] .= ' '.$class;
  783. }
  784. if (is_null($title)) {
  785. $title = $text;
  786. }
  787. $attributes['title'] = $title;
  788. if (!isset($attributes['role'])) {
  789. $attributes['role'] = 'button';
  790. }
  791. return html_writer::link($url, $text, $attributes);
  792. }
  793. /**
  794. * Opens a grid.
  795. *
  796. * Call {@link core_course_management_renderer::grid_column_start()} to create columns.
  797. *
  798. * @param string $id An id to give this grid.
  799. * @param string $class A class to give this grid.
  800. * @return string
  801. */
  802. public function grid_start($id = null, $class = null) {
  803. $gridclass = 'grid-row-r row-fluid';
  804. if (is_null($class)) {
  805. $class = $gridclass;
  806. } else {
  807. $class .= ' ' . $gridclass;
  808. }
  809. $attributes = array();
  810. if (!is_null($id)) {
  811. $attributes['id'] = $id;
  812. }
  813. return html_writer::start_div($class, $attributes);
  814. }
  815. /**
  816. * Closes the grid.
  817. *
  818. * @return string
  819. */
  820. public function grid_end() {
  821. return html_writer::end_div();
  822. }
  823. /**
  824. * Opens a grid column
  825. *
  826. * @param int $size The number of segments this column should span.
  827. * @param string $id An id to give the column.
  828. * @param string $class A class to give the column.
  829. * @return string
  830. */
  831. public function grid_column_start($size, $id = null, $class = null) {
  832. // Calculate Bootstrap grid sizing.
  833. $bootstrapclass = 'span'.$size;
  834. // Calculate YUI grid sizing.
  835. if ($size === 12) {
  836. $maxsize = 1;
  837. $size = 1;
  838. } else {
  839. $maxsize = 12;
  840. $divisors = array(8, 6, 5, 4, 3, 2);
  841. foreach ($divisors as $divisor) {
  842. if (($maxsize % $divisor === 0) && ($size % $divisor === 0)) {
  843. $maxsize = $maxsize / $divisor;
  844. $size = $size / $divisor;
  845. break;
  846. }
  847. }
  848. }
  849. if ($maxsize > 1) {
  850. $yuigridclass = "grid-col-{$size}-{$maxsize} grid-col";
  851. } else {
  852. $yuigridclass = "grid-col-1 grid-col";
  853. }
  854. if (is_null($class)) {
  855. $class = $yuigridclass . ' ' . $bootstrapclass;
  856. } else {
  857. $class .= ' ' . $yuigridclass . ' ' . $bootstrapclass;
  858. }
  859. $attributes = array();
  860. if (!is_null($id)) {
  861. $attributes['id'] = $id;
  862. }
  863. return html_writer::start_div($class, $attributes);
  864. }
  865. /**
  866. * Closes a grid column.
  867. *
  868. * @return string
  869. */
  870. public function grid_column_end() {
  871. return html_writer::end_div();
  872. }
  873. /**
  874. * Renders an action_icon.
  875. *
  876. * This function uses the {@link core_renderer::action_link()} method for the
  877. * most part. What it does different is prepare the icon as HTML and use it
  878. * as the link text.
  879. *
  880. * @param string|moodle_url $url A string URL or moodel_url
  881. * @param pix_icon $pixicon
  882. * @param component_action $action
  883. * @param array $attributes associative array of html link attributes + disabled
  884. * @param bool $linktext show title next to image in link
  885. * @return string HTML fragment
  886. */
  887. public function action_icon($url, pix_icon $pixicon, component_action $action = null,
  888. array $attributes = null, $linktext = false) {
  889. if (!($url instanceof moodle_url)) {
  890. $url = new moodle_url($url);
  891. }
  892. $attributes = (array)$attributes;
  893. if (empty($attributes['class'])) {
  894. // Let devs override the class via $attributes.
  895. $attributes['class'] = 'action-icon';
  896. }
  897. $icon = $this->render($pixicon);
  898. if ($linktext) {
  899. $text = $pixicon->attributes['alt'];
  900. } else {
  901. $text = '';
  902. }
  903. return $this->action_link($url, $icon.$text, $action, $attributes);
  904. }
  905. /**
  906. * Displays a view mode selector.
  907. *
  908. * @param array $modes An array of view modes.
  909. * @param string $currentmode The current view mode.
  910. * @param moodle_url $url The URL to use when changing actions. Defaults to the page URL.
  911. * @param string $param The param name.
  912. * @return string
  913. */
  914. public function view_mode_selector(array $modes, $currentmode, moodle_url $url = null, $param = 'view') {
  915. if ($url === null) {
  916. $url = $this->page->url;
  917. }
  918. $menu = new action_menu;
  919. $menu->attributes['class'] .= ' view-mode-selector vms';
  920. $selected = null;
  921. foreach ($modes as $mode => $modestr) {
  922. $attributes = array(
  923. 'class' => 'vms-mode',
  924. 'data-mode' => $mode
  925. );
  926. if ($currentmode === $mode) {
  927. $attributes['class'] .= ' currentmode';
  928. $selected = $modestr;
  929. }
  930. if ($selected === null) {
  931. $selected = $modestr;
  932. }
  933. $modeurl = new moodle_url($url, array($param => $mode));
  934. if ($mode === 'default') {
  935. $modeurl->remove_params($param);
  936. }
  937. $menu->add(new action_menu_link_secondary($modeurl, null, $modestr, $attributes));
  938. }
  939. $menu->set_menu_trigger($selected);
  940. $html = html_writer::start_div('view-mode-selector vms');
  941. $html .= get_string('viewing').' '.$this->render($menu);
  942. $html .= html_writer::end_div();
  943. return $html;
  944. }
  945. /**
  946. * Displays a search result listing.
  947. *
  948. * @param array $courses The courses to display.
  949. * @param int $totalcourses The total number of courses to display.
  950. * @param course_in_list $course The currently selected course if there is one.
  951. * @param int $page The current page, starting at 0.
  952. * @param int $perpage The number of courses to display per page.
  953. * @return string
  954. */
  955. public function search_listing(array $courses, $totalcourses, course_in_list $course = null, $page = 0, $perpage = 20) {
  956. $page = max($page, 0);
  957. $perpage = max($perpage, 2);
  958. $totalpages = ceil($totalcourses / $perpage);
  959. if ($page > $totalpages - 1) {
  960. $page = $totalpages - 1;
  961. }
  962. $courseid = isset($course) ? $course->id : null;
  963. $first = true;
  964. $last = false;
  965. $i = $page * $perpage;
  966. $html = html_writer::start_div('course-listing', array(
  967. 'data-category' => 'search',
  968. 'data-page' => $page,
  969. 'data-totalpages' => $totalpages,
  970. 'data-totalcourses' => $totalcourses
  971. ));
  972. $html .= html_writer::tag('h3', get_string('courses'));
  973. $html .= $this->search_pagination($totalcourses, $page, $perpage);
  974. $html .= html_writer::start_tag('ul', array('class' => 'ml'));
  975. foreach ($courses as $listitem) {
  976. $i++;
  977. if ($i == $totalcourses) {
  978. $last = true;
  979. }
  980. $html .= $this->search_listitem($listitem, $courseid, $first, $last);
  981. $first = false;
  982. }
  983. $html .= html_writer::end_tag('ul');
  984. $html .= $this->search_pagination($totalcourses, $page, $perpage, true);
  985. $html .= html_writer::end_div();
  986. return $html;
  987. }
  988. /**
  989. * Displays pagination for search results.
  990. *
  991. * @param int $totalcourses The total number of courses to be displayed.
  992. * @param int $page The current page.
  993. * @param int $perpage The number of courses being displayed.
  994. * @param bool $showtotals Whether or not to print total information.
  995. * @return string
  996. */
  997. protected function search_pagination($totalcourses, $page, $perpage, $showtotals = false) {
  998. $html = '';
  999. $totalpages = ceil($totalcourses / $perpage);
  1000. if ($showtotals) {
  1001. if ($totalpages == 1) {
  1002. $str = get_string('showingacourses', 'moodle', $totalcourses);
  1003. } else {
  1004. $a = new stdClass;
  1005. $a->start = ($page * $perpage) + 1;
  1006. $a->end = min((($page + 1) * $perpage), $totalcourses);
  1007. $a->total = $totalcourses;
  1008. $str = get_string('showingxofycourses', 'moodle', $a);
  1009. }
  1010. $html .= html_writer::div($str, 'listing-pagination-totals dimmed');
  1011. }
  1012. if ($totalcourses < $perpage) {
  1013. return $html;
  1014. }
  1015. $aside = 2;
  1016. $span = $aside * 2 + 1;
  1017. $start = max($page - $aside, 0);
  1018. $end = min($page + $aside, $totalpages - 1);
  1019. if (($end - $start) < $span) {
  1020. if ($start == 0) {
  1021. $end = min($totalpages - 1, $span - 1);
  1022. } else if ($end == ($totalpages - 1)) {
  1023. $start = max(0, $end - $span + 1);
  1024. }
  1025. }
  1026. $items = array();
  1027. $baseurl = $this->page->url;
  1028. if ($page > 0) {
  1029. $items[] = $this->action_button(new moodle_url($baseurl, array('page' => 0)), get_string('first'));
  1030. $items[] = $this->action_button(new moodle_url($baseurl, array('page' => $page - 1)), get_string('prev'));
  1031. $items[] = '...';
  1032. }
  1033. for ($i = $start; $i <= $end; $i++) {
  1034. $class = '';
  1035. if ($page == $i) {
  1036. $class = 'active-page';
  1037. }
  1038. $items[] = $this->action_button(new moodle_url($baseurl, array('page' => $i)), $i + 1, null, $class);
  1039. }
  1040. if ($page < ($totalpages - 1)) {
  1041. $items[] = '...';
  1042. $items[] = $this->action_button(new moodle_url($baseurl, array('page' => $page + 1)), get_string('next'));
  1043. $items[] = $this->action_button(new moodle_url($baseurl, array('page' => $totalpages - 1)), get_string('last'));
  1044. }
  1045. $html .= html_writer::div(join('', $items), 'listing-pagination');
  1046. return $html;
  1047. }
  1048. /**
  1049. * Renderers a search result course list item.
  1050. *
  1051. * This function will be called for every course being displayed by course_listing.
  1052. *
  1053. * @param course_in_list $course The course to produce HTML for.
  1054. * @param int $selectedcourse The id of the currently selected course.
  1055. * @return string
  1056. */
  1057. public function search_listitem(course_in_list $course, $selectedcourse) {
  1058. $text = $course->get_formatted_name();
  1059. $attributes = array(
  1060. 'class' => 'listitem listitem-course',
  1061. 'data-id' => $course->id,
  1062. 'data-selected' => ($selectedcourse == $course->id) ? '1' : '0',
  1063. 'data-visible' => $course->visible ? '1' : '0'
  1064. );
  1065. $bulkcourseinput = array('type' => 'checkbox', 'name' => 'bc[]', 'value' => $course->id, 'class' => 'bulk-action-checkbox');
  1066. $viewcourseurl = new moodle_url($this->page->url, array('courseid' => $course->id));
  1067. $categoryname = coursecat::get($course->category)->get_formatted_name();
  1068. $html = html_writer::start_tag('li', $attributes);
  1069. $html .= html_writer::start_div('clearfix');
  1070. $html .= html_writer::start_div('float-left');
  1071. $html .= html_writer::empty_tag('input', $bulkcourseinput).'&nbsp;';
  1072. $html .= html_writer::end_div();
  1073. $html .= html_writer::link($viewcourseurl, $text, array('class' => 'float-left coursename'));
  1074. $html .= html_writer::tag('span', $categoryname, array('class' => 'float-left categoryname'));
  1075. $html .= html_writer::start_div('float-right');
  1076. $html .= $this->search_listitem_actions($course);
  1077. $html .= html_writer::tag('span', s($course->idnumber), array('class' => 'dimmed idnumber'));
  1078. $html .= html_writer::end_div();
  1079. $html .= html_writer::end_div();
  1080. $html .= html_writer::end_tag('li');
  1081. return $html;
  1082. }
  1083. /**
  1084. * Renderers actions for individual course actions.
  1085. *
  1086. * @param course_in_list $course The course to renderer actions for.
  1087. * @return string
  1088. */
  1089. public function search_listitem_actions(course_in_list $course) {
  1090. $baseurl = new moodle_url(
  1091. '/course/managementsearch.php',
  1092. array('courseid' => $course->id, 'categoryid' => $course->category, 'sesskey' => sesskey())
  1093. );
  1094. $actions = array();
  1095. // Edit.
  1096. if ($course->can_access()) {
  1097. if ($course->can_edit()) {
  1098. $actions[] = $this->output->action_icon(
  1099. new moodle_url('/course/edit.php', array('id' => $course->id)),
  1100. new pix_icon('t/edit', get_string('edit')),
  1101. null,
  1102. array('class' => 'action-edit')
  1103. );
  1104. }
  1105. // Show/Hide.
  1106. if ($course->can_change_visibility()) {
  1107. if ($course->visible) {
  1108. $actions[] = $this->output->action_icon(
  1109. new moodle_url($baseurl, array('action' => 'hidecourse')),
  1110. new pix_icon('t/show', get_string('hide')),
  1111. null,
  1112. array('data-action' => 'hide', 'class' => 'action-hide')
  1113. );
  1114. } else {
  1115. $actions[] = $this->output->action_icon(
  1116. new moodle_url($baseurl, array('action' => 'showcourse')),
  1117. new pix_icon('t/hide', get_string('show')),
  1118. null,
  1119. array('data-action' => 'show', 'class' => 'action-show')
  1120. );
  1121. }
  1122. }
  1123. }
  1124. if (empty($actions)) {
  1125. return '';
  1126. }
  1127. return html_writer::span(join('', $actions), 'course-item-actions item-actions');
  1128. }
  1129. /**
  1130. * Creates access hidden skip to links for the displayed sections.
  1131. *
  1132. * @param bool $displaycategorylisting
  1133. * @param bool $displaycourselisting
  1134. * @param bool $displaycoursedetail
  1135. * @return string
  1136. */
  1137. public function accessible_skipto_links($displaycategorylisting, $displaycourselisting, $displaycoursedetail) {
  1138. $html = html_writer::start_div('skiplinks accesshide');
  1139. $url = new moodle_url($this->page->url);
  1140. if ($displaycategorylisting) {
  1141. $url->set_anchor('category-listing');
  1142. $html .= html_writer::link($url, get_string('skiptocategorylisting'), array('class' => 'skip'));
  1143. }
  1144. if ($displaycourselisting) {

Large files files are truncated, but you can click here to view the full file