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