Proyectos de Subversion Moodle

Rev

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