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