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