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