Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | Ultima modificación | Ver Log |

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