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
 * Renderer for use with the course section and all the goodness that falls
19
 * within it.
20
 *
21
 * This renderer should contain methods useful to courses, and categories.
22
 *
23
 * @package   moodlecore
24
 * @copyright 2010 Sam Hemelryk
25
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26
 */
27
 
28
 defined('MOODLE_INTERNAL') || die();
29
 
30
/**
31
 * The core course renderer
32
 *
33
 * Can be retrieved with the following:
34
 * $renderer = $PAGE->get_renderer('core','course');
35
 */
36
class core_course_renderer extends plugin_renderer_base {
37
    const COURSECAT_SHOW_COURSES_NONE = 0; /* do not show courses at all */
38
    const COURSECAT_SHOW_COURSES_COUNT = 5; /* do not show courses but show number of courses next to category name */
39
    const COURSECAT_SHOW_COURSES_COLLAPSED = 10;
40
    const COURSECAT_SHOW_COURSES_AUTO = 15; /* will choose between collapsed and expanded automatically */
41
    const COURSECAT_SHOW_COURSES_EXPANDED = 20;
42
    const COURSECAT_SHOW_COURSES_EXPANDED_WITH_CAT = 30;
43
 
44
    const COURSECAT_TYPE_CATEGORY = 0;
45
    const COURSECAT_TYPE_COURSE = 1;
46
 
47
    /**
48
     * A cache of strings
49
     * @var stdClass
50
     */
51
    protected $strings;
52
 
53
    /**
54
     * Whether a category content is being initially rendered with children. This is mainly used by the
55
     * core_course_renderer::corsecat_tree() to render the appropriate action for the Expand/Collapse all link on
56
     * page load.
57
     * @var bool
58
     */
59
    protected $categoryexpandedonload = false;
60
 
61
    /**
62
     * Override the constructor so that we can initialise the string cache
63
     *
64
     * @param moodle_page $page
65
     * @param string $target
66
     */
67
    public function __construct(moodle_page $page, $target) {
68
        $this->strings = new stdClass;
69
        $courseid = $page->course->id;
70
        parent::__construct($page, $target);
71
    }
72
 
73
    /**
74
     * Renders course info box.
75
     *
76
     * @param stdClass $course
77
     * @return string
78
     */
79
    public function course_info_box(stdClass $course) {
80
        $content = '';
81
        $content .= $this->output->box_start('generalbox info');
82
        $chelper = new coursecat_helper();
83
        $chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_EXPANDED);
84
        $content .= $this->coursecat_coursebox($chelper, $course);
85
        $content .= $this->output->box_end();
86
        return $content;
87
    }
88
 
89
    /**
1441 ariadna 90
     * Renders course info box.
91
     *
92
     * @param stdClass $course course object
93
     * @param string[] $widgets array of enrolment widgets
94
     * @param \core\url|null $returnurl return url
95
     * @return string
96
     */
97
    public function enrolment_options(stdClass $course, array $widgets, ?\core\url $returnurl = null): string {
98
        if (!$widgets) {
99
            if (isguestuser()) {
100
                $message = get_string('noguestaccess', 'enrol');
101
                $continuebutton = $this->output->continue_button(get_login_url());
102
            } else if ($returnurl) {
103
                $message = get_string('notenrollable', 'enrol');
104
                $continuebutton = $this->output->continue_button($returnurl);
105
            } else {
106
                $url = get_local_referer(false);
107
                if (empty($url)) {
108
                    $url = new moodle_url('/index.php');
109
                }
110
                $message = get_string('notenrollable', 'enrol');
111
                $continuebutton = $this->output->continue_button($url);
112
            }
113
        }
114
 
115
        $courseinfobox = $this->course_info_box($course);
116
 
117
        $templatecontext = [
118
            'heading' => get_string('enrolmentoptions', 'enrol'),
119
            'courseinfobox' => $courseinfobox,
120
            'widgets' => array_values($widgets),
121
            'message' => $message ?? '',
122
            'continuebutton' => $continuebutton ?? '',
123
        ];
124
        return $this->render_from_template('core_enrol/enrolment_options', $templatecontext);
125
    }
126
 
127
    /**
1 efrain 128
     * Renderers a structured array of courses and categories into a nice XHTML tree structure.
129
     *
130
     * @deprecated since 2.5
131
     *
132
     * @param array $ignored argument ignored
133
     * @return string
134
     */
135
    final public function course_category_tree(array $ignored) {
136
        debugging('Function core_course_renderer::course_category_tree() is deprecated, please use frontpage_combo_list()', DEBUG_DEVELOPER);
137
        return $this->frontpage_combo_list();
138
    }
139
 
140
    /**
141
     * Renderers a category for use with course_category_tree
142
     *
143
     * @deprecated since 2.5
144
     *
145
     * @param array $category
146
     * @param int $depth
147
     * @return string
148
     */
149
    final protected function course_category_tree_category(stdClass $category, $depth=1) {
150
        debugging('Function core_course_renderer::course_category_tree_category() is deprecated', DEBUG_DEVELOPER);
151
        return '';
152
    }
153
 
154
    /**
155
     * Render a modchooser.
156
     *
157
     * @param renderable $modchooser The chooser.
158
     * @return string
159
     */
160
    public function render_modchooser(renderable $modchooser) {
161
        return $this->render_from_template('core_course/modchooser', $modchooser->export_for_template($this));
162
    }
163
 
164
    /**
165
     * Build the HTML for the module chooser javascript popup.
166
     *
167
     * @param int $courseid The course id to fetch modules for.
168
     * @return string
169
     */
170
    public function course_activitychooser($courseid) {
171
 
172
        if (!$this->page->requires->should_create_one_time_item_now('core_course_modchooser')) {
173
            return '';
174
        }
175
 
176
        // Build an object of config settings that we can then hook into in the Activity Chooser.
177
        $chooserconfig = (object) [
178
            'tabmode' => get_config('core', 'activitychoosertabmode'),
179
        ];
180
        $this->page->requires->js_call_amd('core_course/activitychooser', 'init', [$courseid, $chooserconfig]);
181
 
182
        return '';
183
    }
184
 
185
    /**
186
     * Build the HTML for a specified set of modules
187
     *
188
     * @param array $modules A set of modules as used by the
189
     * course_modchooser_module function
190
     * @return string The composed HTML for the module
191
     */
192
    protected function course_modchooser_module_types($modules) {
193
        debugging('Method core_course_renderer::course_modchooser_module_types() is deprecated, ' .
194
            'see core_course_renderer::render_modchooser().', DEBUG_DEVELOPER);
195
        return '';
196
    }
197
 
198
    /**
199
     * Return the HTML for the specified module adding any required classes
200
     *
201
     * @param object $module An object containing the title, and link. An
202
     * icon, and help text may optionally be specified. If the module
203
     * contains subtypes in the types option, then these will also be
204
     * displayed.
205
     * @param array $classes Additional classes to add to the encompassing
206
     * div element
207
     * @return string The composed HTML for the module
208
     */
209
    protected function course_modchooser_module($module, $classes = array('option')) {
210
        debugging('Method core_course_renderer::course_modchooser_module() is deprecated, ' .
211
            'see core_course_renderer::render_modchooser().', DEBUG_DEVELOPER);
212
        return '';
213
    }
214
 
215
    protected function course_modchooser_title($title, $identifier = null) {
216
        debugging('Method core_course_renderer::course_modchooser_title() is deprecated, ' .
217
            'see core_course_renderer::render_modchooser().', DEBUG_DEVELOPER);
218
        return '';
219
    }
220
 
221
    /**
222
     * Renders HTML for the menus to add activities and resources to the current course
223
     *
224
     * Renders the ajax control (the link which when clicked produces the activity chooser modal). No noscript fallback.
225
     *
226
     * @param stdClass $course
227
     * @param int $section relative section number (field course_sections.section)
228
     * @param int $sectionreturn The section to link back to
229
     * @param array $displayoptions additional display options, for example blocks add
230
     *     option 'inblock' => true, suggesting to display controls vertically
231
     * @return string
232
     */
233
    function course_section_add_cm_control($course, $section, $sectionreturn = null, $displayoptions = array()) {
234
        // Check to see if user can add menus.
235
        if (!has_capability('moodle/course:manageactivities', context_course::instance($course->id))
236
                || !$this->page->user_is_editing()) {
237
            return '';
238
        }
239
 
1441 ariadna 240
        $sectioninfo = get_fast_modinfo($course)->get_section_info($section);
1 efrain 241
 
1441 ariadna 242
        $activitychooserbutton = new \core_course\output\activitychooserbutton($sectioninfo, null, $sectionreturn);
243
 
1 efrain 244
        // Load the JS for the modal.
245
        $this->course_activitychooser($course->id);
246
 
1441 ariadna 247
        return $this->render_from_template(
248
            'core_courseformat/local/content/divider',
249
            [
250
                'content' => $this->render($activitychooserbutton),
251
                'extraclasses' => 'always-visible my-3',
252
            ]
253
        );
1 efrain 254
    }
255
 
256
    /**
257
     * Renders html to display a course search form
258
     *
259
     * @param string $value default value to populate the search field
260
     * @return string
261
     */
262
    public function course_search_form($value = '') {
263
 
264
        $data = [
265
            'action' => \core_search\manager::get_course_search_url(),
266
            'btnclass' => 'btn-primary',
267
            'inputname' => 'q',
268
            'searchstring' => get_string('searchcourses'),
269
            'hiddenfields' => (object) ['name' => 'areaids', 'value' => 'core_course-course'],
270
            'query' => $value
271
        ];
272
        return $this->render_from_template('core/search_input', $data);
273
    }
274
 
275
    /**
276
     * Message displayed to the user when they try to access unavailable activity following URL
277
     *
278
     * This method is a very simplified version of {@link course_section_cm()} to be part of the error
279
     * notification only. It also does not check if module is visible on course page or not.
280
     *
281
     * The message will be displayed inside notification!
282
     *
283
     * @param cm_info $cm
284
     * @return string
285
     */
286
    public function course_section_cm_unavailable_error_message(cm_info $cm) {
287
        if ($cm->uservisible) {
288
            return null;
289
        }
290
        if (!$cm->availableinfo) {
291
            return get_string('activityiscurrentlyhidden');
292
        }
293
 
294
        $altname = get_accesshide(' ' . $cm->modfullname);
1441 ariadna 295
        $name = html_writer::empty_tag('img', ['src' => $cm->get_icon_url(),
296
                'class' => 'activityicon', 'alt' => '']) .
1 efrain 297
            html_writer::tag('span', ' '.$cm->get_formatted_name() . $altname, array('class' => 'instancename'));
298
        $formattedinfo = \core_availability\info::format_info($cm->availableinfo, $cm->get_course());
299
        return html_writer::div($name, 'activityinstance-error') .
300
        html_writer::div($formattedinfo, 'availabilityinfo-error');
301
    }
302
 
303
    /**
304
     * Displays a custom list of courses with paging bar if necessary
305
     *
306
     * If $paginationurl is specified but $totalcount is not, the link 'View more'
307
     * appears under the list.
308
     *
309
     * If both $paginationurl and $totalcount are specified, and $totalcount is
310
     * bigger than count($courses), a paging bar is displayed above and under the
311
     * courses list.
312
     *
313
     * @param array $courses array of course records (or instances of core_course_list_element) to show on this page
314
     * @param bool $showcategoryname whether to add category name to the course description
315
     * @param string $additionalclasses additional CSS classes to add to the div.courses
316
     * @param moodle_url $paginationurl url to view more or url to form links to the other pages in paging bar
317
     * @param int $totalcount total number of courses on all pages, if omitted $paginationurl will be displayed as 'View more' link
318
     * @param int $page current page number (defaults to 0 referring to the first page)
319
     * @param int $perpage number of records per page (defaults to $CFG->coursesperpage)
320
     * @return string
321
     */
322
    public function courses_list($courses, $showcategoryname = false, $additionalclasses = null, $paginationurl = null, $totalcount = null, $page = 0, $perpage = null) {
323
        global $CFG;
324
        // create instance of coursecat_helper to pass display options to function rendering courses list
325
        $chelper = new coursecat_helper();
326
        if ($showcategoryname) {
327
            $chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_EXPANDED_WITH_CAT);
328
        } else {
329
            $chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_EXPANDED);
330
        }
331
        if ($totalcount !== null && $paginationurl !== null) {
332
            // add options to display pagination
333
            if ($perpage === null) {
334
                $perpage = $CFG->coursesperpage;
335
            }
336
            $chelper->set_courses_display_options(array(
337
                'limit' => $perpage,
338
                'offset' => ((int)$page) * $perpage,
339
                'paginationurl' => $paginationurl,
340
            ));
341
        } else if ($paginationurl !== null) {
342
            // add options to display 'View more' link
343
            $chelper->set_courses_display_options(array('viewmoreurl' => $paginationurl));
344
            $totalcount = count($courses) + 1; // has to be bigger than count($courses) otherwise link will not be displayed
345
        }
346
        $chelper->set_attributes(array('class' => $additionalclasses));
347
        $content = $this->coursecat_courses($chelper, $courses, $totalcount);
348
        return $content;
349
    }
350
 
351
    /**
352
     * Returns HTML to display course name.
353
     *
354
     * @param coursecat_helper $chelper
355
     * @param core_course_list_element $course
356
     * @return string
357
     */
358
    protected function course_name(coursecat_helper $chelper, core_course_list_element $course): string {
359
        $content = '';
360
        if ($chelper->get_show_courses() >= self::COURSECAT_SHOW_COURSES_EXPANDED) {
361
            $nametag = 'h3';
362
        } else {
363
            $nametag = 'div';
364
        }
365
        $coursename = $chelper->get_course_formatted_name($course);
366
        $coursenamelink = html_writer::link(new moodle_url('/course/view.php', ['id' => $course->id]),
367
            $coursename, ['class' => $course->visible ? 'aalink' : 'aalink dimmed']);
368
        $content .= html_writer::tag($nametag, $coursenamelink, ['class' => 'coursename']);
369
        // If we display course in collapsed form but the course has summary or course contacts, display the link to the info page.
370
        $content .= html_writer::start_tag('div', ['class' => 'moreinfo']);
371
        if ($chelper->get_show_courses() < self::COURSECAT_SHOW_COURSES_EXPANDED) {
372
            if ($course->has_summary() || $course->has_course_contacts() || $course->has_course_overviewfiles()
373
                || $course->has_custom_fields()) {
374
                $url = new moodle_url('/course/info.php', ['id' => $course->id]);
375
                $image = $this->output->pix_icon('i/info', $this->strings->summary);
376
                $content .= html_writer::link($url, $image, ['title' => $this->strings->summary]);
377
                // Make sure JS file to expand course content is included.
378
                $this->coursecat_include_js();
379
            }
380
        }
381
        $content .= html_writer::end_tag('div');
382
        return $content;
383
    }
384
 
385
    /**
386
     * Returns HTML to display course enrolment icons.
387
     *
388
     * @param core_course_list_element $course
389
     * @return string
390
     */
391
    protected function course_enrolment_icons(core_course_list_element $course): string {
392
        $content = '';
393
        if ($icons = enrol_get_course_info_icons($course)) {
394
            $content .= html_writer::start_tag('div', ['class' => 'enrolmenticons']);
395
            foreach ($icons as $icon) {
396
                $content .= $this->render($icon);
397
            }
398
            $content .= html_writer::end_tag('div');
399
        }
400
        return $content;
401
    }
402
 
403
    /**
404
     * Displays one course in the list of courses.
405
     *
406
     * This is an internal function, to display an information about just one course
407
     * please use {@link core_course_renderer::course_info_box()}
408
     *
409
     * @param coursecat_helper $chelper various display options
410
     * @param core_course_list_element|stdClass $course
411
     * @param string $additionalclasses additional classes to add to the main <div> tag (usually
412
     *    depend on the course position in list - first/last/even/odd)
413
     * @return string
414
     */
415
    protected function coursecat_coursebox(coursecat_helper $chelper, $course, $additionalclasses = '') {
416
        if (!isset($this->strings->summary)) {
417
            $this->strings->summary = get_string('summary');
418
        }
419
        if ($chelper->get_show_courses() <= self::COURSECAT_SHOW_COURSES_COUNT) {
420
            return '';
421
        }
422
        if ($course instanceof stdClass) {
423
            $course = new core_course_list_element($course);
424
        }
425
        $content = '';
426
        $classes = trim('coursebox clearfix '. $additionalclasses);
427
        if ($chelper->get_show_courses() < self::COURSECAT_SHOW_COURSES_EXPANDED) {
428
            $classes .= ' collapsed';
429
        }
430
 
431
        // .coursebox
432
        $content .= html_writer::start_tag('div', array(
433
            'class' => $classes,
434
            'data-courseid' => $course->id,
435
            'data-type' => self::COURSECAT_TYPE_COURSE,
436
        ));
437
 
438
        $content .= html_writer::start_tag('div', array('class' => 'info'));
439
        $content .= $this->course_name($chelper, $course);
440
        $content .= $this->course_enrolment_icons($course);
441
        $content .= html_writer::end_tag('div');
442
 
443
        $content .= html_writer::start_tag('div', array('class' => 'content'));
444
        $content .= $this->coursecat_coursebox_content($chelper, $course);
445
        $content .= html_writer::end_tag('div');
446
 
447
        $content .= html_writer::end_tag('div'); // .coursebox
448
        return $content;
449
    }
450
 
451
    /**
452
     * Returns HTML to display course summary.
453
     *
454
     * @param coursecat_helper $chelper
455
     * @param core_course_list_element $course
456
     * @return string
457
     */
458
    protected function course_summary(coursecat_helper $chelper, core_course_list_element $course): string {
459
        $content = '';
460
        if ($course->has_summary()) {
461
            $content .= html_writer::start_tag('div', ['class' => 'summary']);
462
            $content .= $chelper->get_course_formatted_summary($course,
463
                array('overflowdiv' => true, 'noclean' => true, 'para' => false));
464
            $content .= html_writer::end_tag('div');
465
        }
466
        return $content;
467
    }
468
 
469
    /**
470
     * Returns HTML to display course contacts.
471
     *
472
     * @param core_course_list_element $course
473
     * @return string
474
     */
475
    protected function course_contacts(core_course_list_element $course) {
476
        $content = '';
477
        if ($course->has_course_contacts()) {
478
            $content .= html_writer::start_tag('ul', ['class' => 'teachers']);
479
            foreach ($course->get_course_contacts() as $coursecontact) {
480
                $rolenames = array_map(function ($role) {
481
                    return $role->displayname;
482
                }, $coursecontact['roles']);
1441 ariadna 483
                $name = html_writer::tag('span', implode(", ", $rolenames).': ', ['class' => 'fw-bold']);
1 efrain 484
                $name .= html_writer::link(
485
                   \core_user::get_profile_url($coursecontact['user'], context_system::instance()),
486
                   $coursecontact['username']
487
                );
488
                $content .= html_writer::tag('li', $name);
489
            }
490
            $content .= html_writer::end_tag('ul');
491
        }
492
        return $content;
493
    }
494
 
495
    /**
496
     * Returns HTML to display course overview files.
497
     *
498
     * @param core_course_list_element $course
499
     * @return string
500
     */
501
    protected function course_overview_files(core_course_list_element $course): string {
502
        global $CFG;
503
 
504
        $contentimages = $contentfiles = '';
505
        foreach ($course->get_course_overviewfiles() as $file) {
506
            $isimage = $file->is_valid_image();
507
            $url = moodle_url::make_file_url("$CFG->wwwroot/pluginfile.php",
508
                '/' . $file->get_contextid() . '/' . $file->get_component() . '/' .
509
                $file->get_filearea() . $file->get_filepath() . $file->get_filename(), !$isimage);
510
            if ($isimage) {
511
                $contentimages .= html_writer::tag('div',
512
                    html_writer::empty_tag('img', ['src' => $url, 'alt' => '']),
513
                    ['class' => 'courseimage']);
514
            } else {
515
                $image = $this->output->pix_icon(file_file_icon($file), $file->get_filename(), 'moodle');
516
                $filename = html_writer::tag('span', $image, ['class' => 'fp-icon']).
517
                    html_writer::tag('span', $file->get_filename(), ['class' => 'fp-filename']);
518
                $contentfiles .= html_writer::tag('span',
519
                    html_writer::link($url, $filename),
520
                    ['class' => 'coursefile fp-filename-icon text-break']);
521
            }
522
        }
523
        return $contentimages . $contentfiles;
524
    }
525
 
526
    /**
527
     * Returns HTML to display course category name.
528
     *
529
     * @param coursecat_helper $chelper
530
     * @param core_course_list_element $course
531
     * @return string
532
     */
533
    protected function course_category_name(coursecat_helper $chelper, core_course_list_element $course): string {
534
        $content = '';
535
        // Display course category if necessary (for example in search results).
536
        if ($chelper->get_show_courses() == self::COURSECAT_SHOW_COURSES_EXPANDED_WITH_CAT) {
537
            if ($cat = core_course_category::get($course->category, IGNORE_MISSING)) {
538
                $content .= html_writer::start_tag('div', ['class' => 'coursecat']);
1441 ariadna 539
                $content .= html_writer::tag('span', get_string('category').': ', ['class' => 'fw-bold']);
1 efrain 540
                $content .= html_writer::link(new moodle_url('/course/index.php', ['categoryid' => $cat->id]),
541
                        $cat->get_formatted_name(), ['class' => $cat->visible ? '' : 'dimmed']);
542
                $content .= html_writer::end_tag('div');
543
            }
544
        }
545
        return $content;
546
    }
547
 
548
    /**
549
     * Returns HTML to display course custom fields.
550
     *
551
     * @param core_course_list_element $course
552
     * @return string
553
     */
554
    protected function course_custom_fields(core_course_list_element $course): string {
555
        $content = '';
556
        if ($course->has_custom_fields()) {
557
            $handler = core_course\customfield\course_handler::create();
558
            $customfields = $handler->display_custom_fields_data($course->get_custom_fields());
559
            $content .= \html_writer::tag('div', $customfields, ['class' => 'customfields-container']);
560
        }
561
        return $content;
562
    }
563
 
564
    /**
565
     * Returns HTML to display course content (summary, course contacts and optionally category name)
566
     *
567
     * This method is called from coursecat_coursebox() and may be re-used in AJAX
568
     *
569
     * @param coursecat_helper $chelper various display options
570
     * @param stdClass|core_course_list_element $course
571
     * @return string
572
     */
573
    protected function coursecat_coursebox_content(coursecat_helper $chelper, $course) {
574
        if ($chelper->get_show_courses() < self::COURSECAT_SHOW_COURSES_EXPANDED) {
575
            return '';
576
        }
577
        if ($course instanceof stdClass) {
578
            $course = new core_course_list_element($course);
579
        }
580
        $content = \html_writer::start_tag('div', ['class' => 'd-flex']);
581
        $content .= $this->course_overview_files($course);
582
        $content .= \html_writer::start_tag('div', ['class' => 'flex-grow-1']);
583
        $content .= $this->course_summary($chelper, $course);
584
        $content .= $this->course_contacts($course);
585
        $content .= $this->course_category_name($chelper, $course);
586
        $content .= $this->course_custom_fields($course);
587
        $content .= \html_writer::end_tag('div');
588
        $content .= \html_writer::end_tag('div');
589
        return $content;
590
    }
591
 
592
    /**
593
     * Renders the list of courses
594
     *
595
     * This is internal function, please use {@link core_course_renderer::courses_list()} or another public
596
     * method from outside of the class
597
     *
598
     * If list of courses is specified in $courses; the argument $chelper is only used
599
     * to retrieve display options and attributes, only methods get_show_courses(),
600
     * get_courses_display_option() and get_and_erase_attributes() are called.
601
     *
602
     * @param coursecat_helper $chelper various display options
603
     * @param array $courses the list of courses to display
604
     * @param int|null $totalcount total number of courses (affects display mode if it is AUTO or pagination if applicable),
605
     *     defaulted to count($courses)
606
     * @return string
607
     */
608
    protected function coursecat_courses(coursecat_helper $chelper, $courses, $totalcount = null) {
609
        global $CFG;
610
        if ($totalcount === null) {
611
            $totalcount = count($courses);
612
        }
613
        if (!$totalcount) {
614
            // Courses count is cached during courses retrieval.
615
            return '';
616
        }
617
 
618
        if ($chelper->get_show_courses() == self::COURSECAT_SHOW_COURSES_AUTO) {
619
            // In 'auto' course display mode we analyse if number of courses is more or less than $CFG->courseswithsummarieslimit
620
            if ($totalcount <= $CFG->courseswithsummarieslimit) {
621
                $chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_EXPANDED);
622
            } else {
623
                $chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_COLLAPSED);
624
            }
625
        }
626
 
627
        // prepare content of paging bar if it is needed
628
        $paginationurl = $chelper->get_courses_display_option('paginationurl');
629
        $paginationallowall = $chelper->get_courses_display_option('paginationallowall');
630
        if ($totalcount > count($courses)) {
631
            // there are more results that can fit on one page
632
            if ($paginationurl) {
633
                // the option paginationurl was specified, display pagingbar
634
                $perpage = $chelper->get_courses_display_option('limit', $CFG->coursesperpage);
635
                $page = $chelper->get_courses_display_option('offset') / $perpage;
636
                $pagingbar = $this->paging_bar($totalcount, $page, $perpage,
637
                        $paginationurl->out(false, array('perpage' => $perpage)));
638
                if ($paginationallowall) {
639
                    $pagingbar .= html_writer::tag('div', html_writer::link($paginationurl->out(false, array('perpage' => 'all')),
640
                            get_string('showall', '', $totalcount)), array('class' => 'paging paging-showall'));
641
                }
642
            } else if ($viewmoreurl = $chelper->get_courses_display_option('viewmoreurl')) {
643
                // the option for 'View more' link was specified, display more link
644
                $viewmoretext = $chelper->get_courses_display_option('viewmoretext', new lang_string('viewmore'));
645
                $morelink = html_writer::tag(
646
                    'div',
647
                    html_writer::link($viewmoreurl, $viewmoretext, ['class' => 'btn btn-secondary']),
648
                    ['class' => 'paging paging-morelink']
649
                );
650
            }
651
        } else if (($totalcount > $CFG->coursesperpage) && $paginationurl && $paginationallowall) {
652
            // there are more than one page of results and we are in 'view all' mode, suggest to go back to paginated view mode
653
            $pagingbar = html_writer::tag('div', html_writer::link($paginationurl->out(false, array('perpage' => $CFG->coursesperpage)),
654
                get_string('showperpage', '', $CFG->coursesperpage)), array('class' => 'paging paging-showperpage'));
655
        }
656
 
657
        // display list of courses
658
        $attributes = $chelper->get_and_erase_attributes('courses');
659
        $content = html_writer::start_tag('div', $attributes);
660
 
661
        if (!empty($pagingbar)) {
662
            $content .= $pagingbar;
663
        }
664
 
665
        $coursecount = 0;
666
        foreach ($courses as $course) {
667
            $coursecount ++;
668
            $classes = ($coursecount%2) ? 'odd' : 'even';
669
            if ($coursecount == 1) {
670
                $classes .= ' first';
671
            }
672
            if ($coursecount >= count($courses)) {
673
                $classes .= ' last';
674
            }
675
            $content .= $this->coursecat_coursebox($chelper, $course, $classes);
676
        }
677
 
678
        if (!empty($pagingbar)) {
679
            $content .= $pagingbar;
680
        }
681
        if (!empty($morelink)) {
682
            $content .= $morelink;
683
        }
684
 
685
        $content .= html_writer::end_tag('div'); // .courses
686
        return $content;
687
    }
688
 
689
    /**
690
     * Renders the list of subcategories in a category
691
     *
692
     * @param coursecat_helper $chelper various display options
693
     * @param core_course_category $coursecat
694
     * @param int $depth depth of the category in the current tree
695
     * @return string
696
     */
697
    protected function coursecat_subcategories(coursecat_helper $chelper, $coursecat, $depth) {
698
        global $CFG;
699
        $subcategories = array();
700
        if (!$chelper->get_categories_display_option('nodisplay')) {
701
            $subcategories = $coursecat->get_children($chelper->get_categories_display_options());
702
        }
703
        $totalcount = $coursecat->get_children_count();
704
        if (!$totalcount) {
705
            // Note that we call core_course_category::get_children_count() AFTER core_course_category::get_children()
706
            // to avoid extra DB requests.
707
            // Categories count is cached during children categories retrieval.
708
            return '';
709
        }
710
 
711
        // prepare content of paging bar or more link if it is needed
712
        $paginationurl = $chelper->get_categories_display_option('paginationurl');
713
        $paginationallowall = $chelper->get_categories_display_option('paginationallowall');
714
        if ($totalcount > count($subcategories)) {
715
            if ($paginationurl) {
716
                // the option 'paginationurl was specified, display pagingbar
717
                $perpage = $chelper->get_categories_display_option('limit', $CFG->coursesperpage);
718
                $page = $chelper->get_categories_display_option('offset') / $perpage;
719
                $pagingbar = $this->paging_bar($totalcount, $page, $perpage,
720
                        $paginationurl->out(false, array('perpage' => $perpage)));
721
                if ($paginationallowall) {
722
                    $pagingbar .= html_writer::tag('div', html_writer::link($paginationurl->out(false, array('perpage' => 'all')),
723
                            get_string('showall', '', $totalcount)), array('class' => 'paging paging-showall'));
724
                }
725
            } else if ($viewmoreurl = $chelper->get_categories_display_option('viewmoreurl')) {
726
                // the option 'viewmoreurl' was specified, display more link (if it is link to category view page, add category id)
727
                if ($viewmoreurl->compare(new moodle_url('/course/index.php'), URL_MATCH_BASE)) {
728
                    $viewmoreurl->param('categoryid', $coursecat->id);
729
                }
730
                $viewmoretext = $chelper->get_categories_display_option('viewmoretext', new lang_string('viewmore'));
731
                $morelink = html_writer::tag('div', html_writer::link($viewmoreurl, $viewmoretext),
732
                        array('class' => 'paging paging-morelink'));
733
            }
734
        } else if (($totalcount > $CFG->coursesperpage) && $paginationurl && $paginationallowall) {
735
            // there are more than one page of results and we are in 'view all' mode, suggest to go back to paginated view mode
736
            $pagingbar = html_writer::tag('div', html_writer::link($paginationurl->out(false, array('perpage' => $CFG->coursesperpage)),
737
                get_string('showperpage', '', $CFG->coursesperpage)), array('class' => 'paging paging-showperpage'));
738
        }
739
 
740
        // display list of subcategories
741
        $content = html_writer::start_tag('div', array('class' => 'subcategories'));
742
 
743
        if (!empty($pagingbar)) {
744
            $content .= $pagingbar;
745
        }
746
 
747
        foreach ($subcategories as $subcategory) {
748
            $content .= $this->coursecat_category($chelper, $subcategory, $depth + 1);
749
        }
750
 
751
        if (!empty($pagingbar)) {
752
            $content .= $pagingbar;
753
        }
754
        if (!empty($morelink)) {
755
            $content .= $morelink;
756
        }
757
 
758
        $content .= html_writer::end_tag('div');
759
        return $content;
760
    }
761
 
762
    /**
763
     * Make sure that javascript file for AJAX expanding of courses and categories content is included
764
     */
765
    protected function coursecat_include_js() {
766
        if (!$this->page->requires->should_create_one_time_item_now('core_course_categoryexpanderjsinit')) {
767
            return;
768
        }
769
 
770
        // We must only load this module once.
771
        $this->page->requires->yui_module('moodle-course-categoryexpander',
772
                'Y.Moodle.course.categoryexpander.init');
773
    }
774
 
775
    /**
776
     * Returns HTML to display the subcategories and courses in the given category
777
     *
778
     * This method is re-used by AJAX to expand content of not loaded category
779
     *
780
     * @param coursecat_helper $chelper various display options
781
     * @param core_course_category $coursecat
782
     * @param int $depth depth of the category in the current tree
783
     * @return string
784
     */
785
    protected function coursecat_category_content(coursecat_helper $chelper, $coursecat, $depth) {
786
        $content = '';
787
        // Subcategories
788
        $content .= $this->coursecat_subcategories($chelper, $coursecat, $depth);
789
 
790
        // AUTO show courses: Courses will be shown expanded if this is not nested category,
791
        // and number of courses no bigger than $CFG->courseswithsummarieslimit.
792
        $showcoursesauto = $chelper->get_show_courses() == self::COURSECAT_SHOW_COURSES_AUTO;
793
        if ($showcoursesauto && $depth) {
794
            // this is definitely collapsed mode
795
            $chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_COLLAPSED);
796
        }
797
 
798
        // Courses
799
        if ($chelper->get_show_courses() > core_course_renderer::COURSECAT_SHOW_COURSES_COUNT) {
800
            $courses = array();
801
            if (!$chelper->get_courses_display_option('nodisplay')) {
802
                $courses = $coursecat->get_courses($chelper->get_courses_display_options());
803
            }
804
            if ($viewmoreurl = $chelper->get_courses_display_option('viewmoreurl')) {
805
                // the option for 'View more' link was specified, display more link (if it is link to category view page, add category id)
806
                if ($viewmoreurl->compare(new moodle_url('/course/index.php'), URL_MATCH_BASE)) {
807
                    $chelper->set_courses_display_option('viewmoreurl', new moodle_url($viewmoreurl, array('categoryid' => $coursecat->id)));
808
                }
809
            }
810
            $content .= $this->coursecat_courses($chelper, $courses, $coursecat->get_courses_count());
811
        }
812
 
813
        if ($showcoursesauto) {
814
            // restore the show_courses back to AUTO
815
            $chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_AUTO);
816
        }
817
 
818
        return $content;
819
    }
820
 
821
    /**
822
     * Returns HTML to display a course category as a part of a tree
823
     *
824
     * This is an internal function, to display a particular category and all its contents
825
     * use {@link core_course_renderer::course_category()}
826
     *
827
     * @param coursecat_helper $chelper various display options
828
     * @param core_course_category $coursecat
829
     * @param int $depth depth of this category in the current tree
830
     * @return string
831
     */
832
    protected function coursecat_category(coursecat_helper $chelper, $coursecat, $depth) {
833
        // open category tag
834
        $classes = array('category');
835
        if (empty($coursecat->visible)) {
836
            $classes[] = 'dimmed_category';
837
        }
838
        if ($chelper->get_subcat_depth() > 0 && $depth >= $chelper->get_subcat_depth()) {
839
            // do not load content
840
            $categorycontent = '';
841
            $classes[] = 'notloaded';
842
            if ($coursecat->get_children_count() ||
843
                    ($chelper->get_show_courses() >= self::COURSECAT_SHOW_COURSES_COLLAPSED && $coursecat->get_courses_count())) {
844
                $classes[] = 'with_children';
845
                $classes[] = 'collapsed';
846
            }
847
        } else {
848
            // load category content
849
            $categorycontent = $this->coursecat_category_content($chelper, $coursecat, $depth);
850
            $classes[] = 'loaded';
851
            if (!empty($categorycontent)) {
852
                $classes[] = 'with_children';
853
                // Category content loaded with children.
854
                $this->categoryexpandedonload = true;
855
            }
856
        }
857
 
858
        // Make sure JS file to expand category content is included.
859
        $this->coursecat_include_js();
860
 
861
        $content = html_writer::start_tag('div', array(
862
            'class' => join(' ', $classes),
863
            'data-categoryid' => $coursecat->id,
864
            'data-depth' => $depth,
865
            'data-showcourses' => $chelper->get_show_courses(),
866
            'data-type' => self::COURSECAT_TYPE_CATEGORY,
867
        ));
868
 
869
        // category name
870
        $categoryname = $coursecat->get_formatted_name();
871
        $categoryname = html_writer::link(new moodle_url('/course/index.php',
872
                array('categoryid' => $coursecat->id)),
873
                $categoryname);
874
        if ($chelper->get_show_courses() == self::COURSECAT_SHOW_COURSES_COUNT
875
                && ($coursescount = $coursecat->get_courses_count())) {
876
            $categoryname .= html_writer::tag('span', ' ('. $coursescount.')',
877
                    array('title' => get_string('numberofcourses'), 'class' => 'numberofcourse'));
878
        }
879
        $content .= html_writer::start_tag('div', array('class' => 'info'));
880
 
881
        $content .= html_writer::tag(($depth > 1) ? 'h4' : 'h3', $categoryname, array('class' => 'categoryname aabtn'));
882
        $content .= html_writer::end_tag('div'); // .info
883
 
884
        // add category content to the output
885
        $content .= html_writer::tag('div', $categorycontent, array('class' => 'content'));
886
 
887
        $content .= html_writer::end_tag('div'); // .category
888
 
889
        // Return the course category tree HTML
890
        return $content;
891
    }
892
 
893
    /**
894
     * Returns HTML to display a tree of subcategories and courses in the given category
895
     *
896
     * @param coursecat_helper $chelper various display options
897
     * @param core_course_category $coursecat top category (this category's name and description will NOT be added to the tree)
898
     * @return string
899
     */
900
    protected function coursecat_tree(coursecat_helper $chelper, $coursecat) {
901
        // Reset the category expanded flag for this course category tree first.
902
        $this->categoryexpandedonload = false;
903
        $categorycontent = $this->coursecat_category_content($chelper, $coursecat, 0);
904
        if (empty($categorycontent)) {
905
            return '';
906
        }
907
 
908
        // Start content generation
909
        $content = '';
910
        $attributes = $chelper->get_and_erase_attributes('course_category_tree clearfix');
911
        $content .= html_writer::start_tag('div', $attributes);
912
 
913
        if ($coursecat->get_children_count()) {
914
            $classes = array(
915
                'collapseexpand', 'aabtn'
916
            );
917
 
918
            // Check if the category content contains subcategories with children's content loaded.
919
            if ($this->categoryexpandedonload) {
920
                $classes[] = 'collapse-all';
921
                $linkname = get_string('collapseall');
922
            } else {
923
                $linkname = get_string('expandall');
924
            }
925
 
926
            // Only show the collapse/expand if there are children to expand.
927
            $content .= html_writer::start_tag('div', array('class' => 'collapsible-actions'));
928
            $content .= html_writer::link('#', $linkname, array('class' => implode(' ', $classes)));
929
            $content .= html_writer::end_tag('div');
930
            $this->page->requires->strings_for_js(array('collapseall', 'expandall'), 'moodle');
931
        }
932
 
933
        $content .= html_writer::tag('div', $categorycontent, array('class' => 'content'));
934
 
935
        $content .= html_writer::end_tag('div'); // .course_category_tree
936
 
937
        return $content;
938
    }
939
 
940
    /**
941
     * Renders HTML to display particular course category - list of it's subcategories and courses
942
     *
943
     * Invoked from /course/index.php
944
     *
945
     * @param int|stdClass|core_course_category $category
946
     */
947
    public function course_category($category) {
948
        global $CFG;
949
        $usertop = core_course_category::user_top();
950
        if (empty($category)) {
951
            $coursecat = $usertop;
952
        } else if (is_object($category) && $category instanceof core_course_category) {
953
            $coursecat = $category;
954
        } else {
955
            $coursecat = core_course_category::get(is_object($category) ? $category->id : $category);
956
        }
957
        $site = get_site();
958
        $actionbar = new \core_course\output\category_action_bar($this->page, $coursecat);
959
        $output = $this->render_from_template('core_course/category_actionbar', $actionbar->export_for_template($this));
960
 
961
        if (core_course_category::is_simple_site()) {
962
            // There is only one category in the system, do not display link to it.
963
            $strfulllistofcourses = get_string('fulllistofcourses');
964
            $this->page->set_title($strfulllistofcourses);
965
        } else if (!$coursecat->id || !$coursecat->is_uservisible()) {
966
            $strcategories = get_string('categories');
967
            $this->page->set_title($strcategories);
968
        } else {
969
            $strfulllistofcourses = get_string('fulllistofcourses');
970
            $this->page->set_title($strfulllistofcourses);
971
        }
972
 
973
        // Print current category description
974
        $chelper = new coursecat_helper();
975
        if ($description = $chelper->get_category_formatted_description($coursecat)) {
976
            $output .= $this->box($description, array('class' => 'generalbox info'));
977
        }
978
 
979
        // Prepare parameters for courses and categories lists in the tree
980
        $chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_AUTO)
981
                ->set_attributes(array('class' => 'category-browse category-browse-'.$coursecat->id));
982
 
983
        $coursedisplayoptions = array();
984
        $catdisplayoptions = array();
985
        $browse = optional_param('browse', null, PARAM_ALPHA);
986
        $perpage = optional_param('perpage', $CFG->coursesperpage, PARAM_INT);
987
        $page = optional_param('page', 0, PARAM_INT);
988
        $baseurl = new moodle_url('/course/index.php');
989
        if ($coursecat->id) {
990
            $baseurl->param('categoryid', $coursecat->id);
991
        }
992
        if ($perpage != $CFG->coursesperpage) {
993
            $baseurl->param('perpage', $perpage);
994
        }
995
        $coursedisplayoptions['limit'] = $perpage;
996
        $catdisplayoptions['limit'] = $perpage;
997
        if ($browse === 'courses' || !$coursecat->get_children_count()) {
998
            $coursedisplayoptions['offset'] = $page * $perpage;
999
            $coursedisplayoptions['paginationurl'] = new moodle_url($baseurl, array('browse' => 'courses'));
1000
            $catdisplayoptions['nodisplay'] = true;
1001
            $catdisplayoptions['viewmoreurl'] = new moodle_url($baseurl, array('browse' => 'categories'));
1002
            $catdisplayoptions['viewmoretext'] = new lang_string('viewallsubcategories');
1003
        } else if ($browse === 'categories' || !$coursecat->get_courses_count()) {
1004
            $coursedisplayoptions['nodisplay'] = true;
1005
            $catdisplayoptions['offset'] = $page * $perpage;
1006
            $catdisplayoptions['paginationurl'] = new moodle_url($baseurl, array('browse' => 'categories'));
1007
            $coursedisplayoptions['viewmoreurl'] = new moodle_url($baseurl, array('browse' => 'courses'));
1008
            $coursedisplayoptions['viewmoretext'] = new lang_string('viewallcourses');
1009
        } else {
1010
            // we have a category that has both subcategories and courses, display pagination separately
1011
            $coursedisplayoptions['viewmoreurl'] = new moodle_url($baseurl, array('browse' => 'courses', 'page' => 1));
1012
            $catdisplayoptions['viewmoreurl'] = new moodle_url($baseurl, array('browse' => 'categories', 'page' => 1));
1013
        }
1014
        $chelper->set_courses_display_options($coursedisplayoptions)->set_categories_display_options($catdisplayoptions);
1015
 
1016
        // Display course category tree.
1017
        $output .= $this->coursecat_tree($chelper, $coursecat);
1018
 
1019
        return $output;
1020
    }
1021
 
1022
    /**
1023
     * Serves requests to /course/category.ajax.php
1024
     *
1025
     * In this renderer implementation it may expand the category content or
1026
     * course content.
1027
     *
1028
     * @return string
1029
     * @throws coding_exception
1030
     */
1031
    public function coursecat_ajax() {
1032
        global $DB, $CFG;
1033
 
1034
        $type = required_param('type', PARAM_INT);
1035
 
1036
        if ($type === self::COURSECAT_TYPE_CATEGORY) {
1037
            // This is a request for a category list of some kind.
1038
            $categoryid = required_param('categoryid', PARAM_INT);
1039
            $showcourses = required_param('showcourses', PARAM_INT);
1040
            $depth = required_param('depth', PARAM_INT);
1041
 
1042
            $category = core_course_category::get($categoryid);
1043
 
1044
            $chelper = new coursecat_helper();
1045
            $baseurl = new moodle_url('/course/index.php', array('categoryid' => $categoryid));
1046
            $coursedisplayoptions = array(
1047
                'limit' => $CFG->coursesperpage,
1048
                'viewmoreurl' => new moodle_url($baseurl, array('browse' => 'courses', 'page' => 1))
1049
            );
1050
            $catdisplayoptions = array(
1051
                'limit' => $CFG->coursesperpage,
1052
                'viewmoreurl' => new moodle_url($baseurl, array('browse' => 'categories', 'page' => 1))
1053
            );
1054
            $chelper->set_show_courses($showcourses)->
1055
                    set_courses_display_options($coursedisplayoptions)->
1056
                    set_categories_display_options($catdisplayoptions);
1057
 
1058
            return $this->coursecat_category_content($chelper, $category, $depth);
1059
        } else if ($type === self::COURSECAT_TYPE_COURSE) {
1060
            // This is a request for the course information.
1061
            $courseid = required_param('courseid', PARAM_INT);
1062
 
1441 ariadna 1063
            $course = $DB->get_record('course', ['id' => $courseid], '*', IGNORE_MISSING);
1064
            if ($course === false) {
1065
                throw new \moodle_exception('invalidcourseid');
1066
            }
1067
            $coursecontext = context_course::instance($course->id, MUST_EXIST);
1068
            if ($course->visible == 0 && !has_capability('moodle/course:viewhiddencourses', $coursecontext)) {
1069
                throw new \moodle_exception('invalidcourseid');
1070
            }
1 efrain 1071
 
1072
            $chelper = new coursecat_helper();
1073
            $chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_EXPANDED);
1074
            return $this->coursecat_coursebox_content($chelper, $course);
1075
        } else {
1076
            throw new coding_exception('Invalid request type');
1077
        }
1078
    }
1079
 
1080
    /**
1081
     * Renders html to display search result page
1082
     *
1083
     * @param array $searchcriteria may contain elements: search, blocklist, modulelist, tagid
1084
     * @return string
1085
     */
1086
    public function search_courses($searchcriteria) {
1087
        global $CFG;
1088
        $content = '';
1089
 
1090
        $search = '';
1091
        if (!empty($searchcriteria['search'])) {
1092
            $search = $searchcriteria['search'];
1093
        }
1094
        $content .= $this->course_search_form($search);
1095
 
1096
        if (!empty($searchcriteria)) {
1097
            // print search results
1098
 
1099
            $displayoptions = array('sort' => array('displayname' => 1));
1100
            // take the current page and number of results per page from query
1101
            $perpage = optional_param('perpage', 0, PARAM_RAW);
1102
            if ($perpage !== 'all') {
1103
                $displayoptions['limit'] = ((int)$perpage <= 0) ? $CFG->coursesperpage : (int)$perpage;
1104
                $page = optional_param('page', 0, PARAM_INT);
1105
                $displayoptions['offset'] = $displayoptions['limit'] * $page;
1106
            }
1107
            // options 'paginationurl' and 'paginationallowall' are only used in method coursecat_courses()
1108
            $displayoptions['paginationurl'] = new moodle_url('/course/search.php', $searchcriteria);
1109
            $displayoptions['paginationallowall'] = true; // allow adding link 'View all'
1110
 
1111
            $class = 'course-search-result';
1112
            foreach ($searchcriteria as $key => $value) {
1113
                if (!empty($value)) {
1114
                    $class .= ' course-search-result-'. $key;
1115
                }
1116
            }
1117
            $chelper = new coursecat_helper();
1118
            $chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_EXPANDED_WITH_CAT)->
1119
                    set_courses_display_options($displayoptions)->
1120
                    set_search_criteria($searchcriteria)->
1121
                    set_attributes(array('class' => $class));
1122
 
1123
            $courses = core_course_category::search_courses($searchcriteria, $chelper->get_courses_display_options());
1124
            $totalcount = core_course_category::search_courses_count($searchcriteria);
1125
            $courseslist = $this->coursecat_courses($chelper, $courses, $totalcount);
1126
 
1127
            if (!$totalcount) {
1128
                if (!empty($searchcriteria['search'])) {
1129
                    $content .= $this->heading(get_string('nocoursesfound', '', $searchcriteria['search']));
1130
                } else {
1131
                    $content .= $this->heading(get_string('novalidcourses'));
1132
                }
1133
            } else {
1134
                $content .= $this->heading(get_string('searchresults'). ": $totalcount");
1135
                $content .= $courseslist;
1136
            }
1137
        }
1138
        return $content;
1139
    }
1140
 
1141
    /**
1142
     * Renders html to print list of courses tagged with particular tag
1143
     *
1144
     * @param int $tagid id of the tag
1145
     * @param bool $exclusivemode if set to true it means that no other entities tagged with this tag
1146
     *             are displayed on the page and the per-page limit may be bigger
1147
     * @param int $fromctx context id where the link was displayed, may be used by callbacks
1148
     *            to display items in the same context first
1149
     * @param int $ctx context id where to search for records
1150
     * @param bool $rec search in subcontexts as well
1151
     * @param array $displayoptions
1152
     * @return string empty string if no courses are marked with this tag or rendered list of courses
1153
     */
1154
    public function tagged_courses($tagid, $exclusivemode = true, $ctx = 0, $rec = true, $displayoptions = null) {
1155
        global $CFG;
1156
        if (empty($displayoptions)) {
1157
            $displayoptions = array();
1158
        }
1159
        $showcategories = !core_course_category::is_simple_site();
1160
        $displayoptions += array('limit' => $CFG->coursesperpage, 'offset' => 0);
1161
        $chelper = new coursecat_helper();
1162
        $searchcriteria = array('tagid' => $tagid, 'ctx' => $ctx, 'rec' => $rec);
1163
        $chelper->set_show_courses($showcategories ? self::COURSECAT_SHOW_COURSES_EXPANDED_WITH_CAT :
1164
                    self::COURSECAT_SHOW_COURSES_EXPANDED)->
1165
                set_search_criteria($searchcriteria)->
1166
                set_courses_display_options($displayoptions)->
1167
                set_attributes(array('class' => 'course-search-result course-search-result-tagid'));
1168
                // (we set the same css class as in search results by tagid)
1169
        if ($totalcount = core_course_category::search_courses_count($searchcriteria)) {
1170
            $courses = core_course_category::search_courses($searchcriteria, $chelper->get_courses_display_options());
1171
            if ($exclusivemode) {
1172
                return $this->coursecat_courses($chelper, $courses, $totalcount);
1173
            } else {
1174
                $tagfeed = new core_tag\output\tagfeed();
1175
                $img = $this->output->pix_icon('i/course', '');
1176
                foreach ($courses as $course) {
1177
                    $url = course_get_url($course);
1178
                    $imgwithlink = html_writer::link($url, $img);
1179
                    $coursename = html_writer::link($url, $course->get_formatted_name());
1180
                    $details = '';
1181
                    if ($showcategories && ($cat = core_course_category::get($course->category, IGNORE_MISSING))) {
1182
                        $details = get_string('category').': '.
1183
                                html_writer::link(new moodle_url('/course/index.php', array('categoryid' => $cat->id)),
1184
                                        $cat->get_formatted_name(), array('class' => $cat->visible ? '' : 'dimmed'));
1185
                    }
1186
                    $tagfeed->add($imgwithlink, $coursename, $details);
1187
                }
1188
                return $this->output->render_from_template('core_tag/tagfeed', $tagfeed->export_for_template($this->output));
1189
            }
1190
        }
1191
        return '';
1192
    }
1193
 
1194
    /**
1195
     * Returns HTML to display one remote course
1196
     *
1197
     * @param stdClass $course remote course information, contains properties:
1198
           id, remoteid, shortname, fullname, hostid, summary, summaryformat, cat_name, hostname
1199
     * @return string
1200
     */
1201
    protected function frontpage_remote_course(stdClass $course) {
1202
        $url = new moodle_url('/auth/mnet/jump.php', array(
1203
            'hostid' => $course->hostid,
1204
            'wantsurl' => '/course/view.php?id='. $course->remoteid
1205
        ));
1206
 
1207
        $output = '';
1208
        $output .= html_writer::start_tag('div', array('class' => 'coursebox remotecoursebox clearfix'));
1209
        $output .= html_writer::start_tag('div', array('class' => 'info'));
1210
        $output .= html_writer::start_tag('h3', array('class' => 'coursename'));
1211
        $output .= html_writer::link($url, format_string($course->fullname), array('title' => get_string('entercourse')));
1212
        $output .= html_writer::end_tag('h3'); // .name
1213
        $output .= html_writer::tag('div', '', array('class' => 'moreinfo'));
1214
        $output .= html_writer::end_tag('div'); // .info
1215
        $output .= html_writer::start_tag('div', array('class' => 'content'));
1216
        $output .= html_writer::start_tag('div', array('class' => 'summary'));
1217
        $options = new stdClass();
1218
        $options->noclean = true;
1219
        $options->para = false;
1220
        $options->overflowdiv = true;
1221
        $output .= format_text($course->summary, $course->summaryformat, $options);
1222
        $output .= html_writer::end_tag('div'); // .summary
1223
        $addinfo = format_string($course->hostname) . ' : '
1224
            . format_string($course->cat_name) . ' : '
1225
            . format_string($course->shortname);
1226
        $output .= html_writer::tag('div', $addinfo, array('class' => 'remotecourseinfo'));
1227
        $output .= html_writer::end_tag('div'); // .content
1228
        $output .= html_writer::end_tag('div'); // .coursebox
1229
        return $output;
1230
    }
1231
 
1232
    /**
1233
     * Returns HTML to display one remote host
1234
     *
1235
     * @param array $host host information, contains properties: name, url, count
1236
     * @return string
1237
     */
1238
    protected function frontpage_remote_host($host) {
1239
        $output = '';
1240
        $output .= html_writer::start_tag('div', array('class' => 'coursebox remotehost clearfix'));
1241
        $output .= html_writer::start_tag('div', array('class' => 'info'));
1242
        $output .= html_writer::start_tag('h3', array('class' => 'name'));
1243
        $output .= html_writer::link($host['url'], s($host['name']), array('title' => s($host['name'])));
1244
        $output .= html_writer::end_tag('h3'); // .name
1245
        $output .= html_writer::tag('div', '', array('class' => 'moreinfo'));
1246
        $output .= html_writer::end_tag('div'); // .info
1247
        $output .= html_writer::start_tag('div', array('class' => 'content'));
1248
        $output .= html_writer::start_tag('div', array('class' => 'summary'));
1249
        $output .= $host['count'] . ' ' . get_string('courses');
1250
        $output .= html_writer::end_tag('div'); // .content
1251
        $output .= html_writer::end_tag('div'); // .coursebox
1252
        return $output;
1253
    }
1254
 
1255
    /**
1256
     * Returns HTML to print list of courses user is enrolled to for the frontpage
1257
     *
1258
     * Also lists remote courses or remote hosts if MNET authorisation is used
1259
     *
1260
     * @return string
1261
     */
1262
    public function frontpage_my_courses() {
1263
        global $USER, $CFG, $DB;
1264
 
1265
        if (!isloggedin() or isguestuser()) {
1266
            return '';
1267
        }
1268
 
1269
        $output = '';
1270
        $courses  = enrol_get_my_courses('summary, summaryformat');
1271
        $rhosts   = array();
1272
        $rcourses = array();
1273
        if (!empty($CFG->mnet_dispatcher_mode) && $CFG->mnet_dispatcher_mode==='strict') {
1274
            $rcourses = get_my_remotecourses($USER->id);
1275
            $rhosts   = get_my_remotehosts();
1276
        }
1277
 
1278
        if (!empty($courses) || !empty($rcourses) || !empty($rhosts)) {
1279
 
1280
            $chelper = new coursecat_helper();
1281
            $totalcount = count($courses);
1282
            if (count($courses) > $CFG->frontpagecourselimit) {
1283
                // There are more enrolled courses than we can display, display link to 'My courses'.
1284
                $courses = array_slice($courses, 0, $CFG->frontpagecourselimit, true);
1285
                $chelper->set_courses_display_options(array(
1286
                        'viewmoreurl' => new moodle_url('/my/courses.php'),
1287
                        'viewmoretext' => new lang_string('mycourses')
1288
                    ));
1289
            } else if (core_course_category::top()->is_uservisible()) {
1290
                // All enrolled courses are displayed, display link to 'All courses' if there are more courses in system.
1291
                $chelper->set_courses_display_options(array(
1292
                        'viewmoreurl' => new moodle_url('/course/index.php'),
1293
                        'viewmoretext' => new lang_string('fulllistofcourses')
1294
                    ));
1295
                $totalcount = $DB->count_records('course') - 1;
1296
            }
1297
            $chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_EXPANDED)->
1298
                    set_attributes(array('class' => 'frontpage-course-list-enrolled'));
1299
            $output .= $this->coursecat_courses($chelper, $courses, $totalcount);
1300
 
1301
            // MNET
1302
            if (!empty($rcourses)) {
1303
                // at the IDP, we know of all the remote courses
1304
                $output .= html_writer::start_tag('div', array('class' => 'courses'));
1305
                foreach ($rcourses as $course) {
1306
                    $output .= $this->frontpage_remote_course($course);
1307
                }
1308
                $output .= html_writer::end_tag('div'); // .courses
1309
            } elseif (!empty($rhosts)) {
1310
                // non-IDP, we know of all the remote servers, but not courses
1311
                $output .= html_writer::start_tag('div', array('class' => 'courses'));
1312
                foreach ($rhosts as $host) {
1313
                    $output .= $this->frontpage_remote_host($host);
1314
                }
1315
                $output .= html_writer::end_tag('div'); // .courses
1316
            }
1317
        }
1318
        return $output;
1319
    }
1320
 
1321
    /**
1322
     * Returns HTML to print list of available courses for the frontpage
1323
     *
1324
     * @return string
1325
     */
1326
    public function frontpage_available_courses() {
1327
        global $CFG;
1328
 
1329
        $chelper = new coursecat_helper();
1330
        $chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_EXPANDED)->
1331
                set_courses_display_options(array(
1332
                    'recursive' => true,
1333
                    'limit' => $CFG->frontpagecourselimit,
1334
                    'viewmoreurl' => new moodle_url('/course/index.php'),
1335
                    'viewmoretext' => new lang_string('fulllistofcourses')));
1336
 
1337
        $chelper->set_attributes(array('class' => 'frontpage-course-list-all'));
1338
        $courses = core_course_category::top()->get_courses($chelper->get_courses_display_options());
1339
        $totalcount = core_course_category::top()->get_courses_count($chelper->get_courses_display_options());
1340
        if (!$totalcount && !$this->page->user_is_editing() && has_capability('moodle/course:create', context_system::instance())) {
1341
            // Print link to create a new course, for the 1st available category.
1342
            return $this->add_new_course_button();
1343
        }
1344
        return $this->coursecat_courses($chelper, $courses, $totalcount);
1345
    }
1346
 
1347
    /**
1348
     * Returns HTML to the "add new course" button for the page
1349
     *
1350
     * @return string
1351
     */
1352
    public function add_new_course_button() {
1353
        global $CFG;
1354
        // Print link to create a new course, for the 1st available category.
1355
        $output = $this->container_start('buttons');
1356
        $url = new moodle_url('/course/edit.php', array('category' => $CFG->defaultrequestcategory, 'returnto' => 'topcat'));
1357
        $output .= $this->single_button($url, get_string('addnewcourse'), 'get');
1358
        $output .= $this->container_end('buttons');
1359
        return $output;
1360
    }
1361
 
1362
    /**
1363
     * Returns HTML to print tree with course categories and courses for the frontpage
1364
     *
1365
     * @return string
1366
     */
1367
    public function frontpage_combo_list() {
1368
        global $CFG;
1369
        // TODO MDL-10965 improve.
1370
        $tree = core_course_category::top();
1371
        if (!$tree->get_children_count()) {
1372
            return '';
1373
        }
1374
        $chelper = new coursecat_helper();
1375
        $chelper->set_subcat_depth($CFG->maxcategorydepth)->
1376
            set_categories_display_options(array(
1377
                'limit' => $CFG->coursesperpage,
1378
                'viewmoreurl' => new moodle_url('/course/index.php',
1379
                        array('browse' => 'categories', 'page' => 1))
1380
            ))->
1381
            set_courses_display_options(array(
1382
                'limit' => $CFG->coursesperpage,
1383
                'viewmoreurl' => new moodle_url('/course/index.php',
1384
                        array('browse' => 'courses', 'page' => 1))
1385
            ))->
1386
            set_attributes(array('class' => 'frontpage-category-combo'));
1387
        return $this->coursecat_tree($chelper, $tree);
1388
    }
1389
 
1390
    /**
1391
     * Returns HTML to print tree of course categories (with number of courses) for the frontpage
1392
     *
1393
     * @return string
1394
     */
1395
    public function frontpage_categories_list() {
1396
        global $CFG;
1397
        // TODO MDL-10965 improve.
1398
        $tree = core_course_category::top();
1399
        if (!$tree->get_children_count()) {
1400
            return '';
1401
        }
1402
        $chelper = new coursecat_helper();
1403
        $chelper->set_subcat_depth($CFG->maxcategorydepth)->
1404
                set_show_courses(self::COURSECAT_SHOW_COURSES_COUNT)->
1405
                set_categories_display_options(array(
1406
                    'limit' => $CFG->coursesperpage,
1407
                    'viewmoreurl' => new moodle_url('/course/index.php',
1408
                            array('browse' => 'categories', 'page' => 1))
1409
                ))->
1410
                set_attributes(array('class' => 'frontpage-category-names'));
1411
        return $this->coursecat_tree($chelper, $tree);
1412
    }
1413
 
1414
    /**
1415
     * @deprecated since Moodle 4.3 MDL-78744
1416
     */
1441 ariadna 1417
    #[\core\attribute\deprecated(null, since: '4.3', mdl: 'MDL-78744', final: true)]
1418
    public function render_activity_information() {
1419
        \core\deprecation::emit_deprecation([self::class, __FUNCTION__]);
1 efrain 1420
    }
1421
 
1422
    /**
1423
     * Renders the activity navigation.
1424
     *
1425
     * Defer to template.
1426
     *
1427
     * @param \core_course\output\activity_navigation $page
1428
     * @return string html for the page
1429
     */
1430
    public function render_activity_navigation(\core_course\output\activity_navigation $page) {
1431
        $data = $page->export_for_template($this->output);
1432
        return $this->output->render_from_template('core_course/activity_navigation', $data);
1433
    }
1434
 
1435
    /**
1436
     * Display waiting information about backup size during uploading backup process
1437
     * @param object $backupfile the backup stored_file
1438
     * @return $html string
1439
     */
1440
    public function sendingbackupinfo($backupfile) {
1441
        $sizeinfo = new stdClass();
1442
        $sizeinfo->total = number_format($backupfile->get_filesize() / 1000000, 2);
1443
        $html = html_writer::tag('div', get_string('sendingsize', 'hub', $sizeinfo),
1444
            array('class' => 'courseuploadtextinfo'));
1445
        return $html;
1446
    }
1447
 
1448
    /**
1449
     * Hub information (logo - name - description - link)
1450
     * @param object $hubinfo
1451
     * @return string html code
1452
     */
1453
    public function hubinfo($hubinfo) {
1454
        $screenshothtml = html_writer::empty_tag('img',
1455
            array('src' => $hubinfo['imgurl'], 'alt' => $hubinfo['name']));
1456
        $hubdescription = html_writer::tag('div', $screenshothtml,
1457
            array('class' => 'hubscreenshot'));
1458
 
1459
        $hubdescription .= html_writer::tag('a', $hubinfo['name'],
1460
            array('class' => 'hublink', 'href' => $hubinfo['url'],
1461
                'onclick' => 'this.target="_blank"'));
1462
 
1463
        $hubdescription .= html_writer::tag('div', format_text($hubinfo['description'], FORMAT_PLAIN),
1464
            array('class' => 'hubdescription'));
1465
        $hubdescription = html_writer::tag('div', $hubdescription, array('class' => 'hubinfo clearfix'));
1466
 
1467
        return $hubdescription;
1468
    }
1469
 
1470
    /**
1471
     * Output frontpage summary text and frontpage modules (stored as section 1 in site course)
1472
     *
1473
     * This may be disabled in settings
1474
     *
1475
     * @return string
1476
     */
1477
    public function frontpage_section1() {
1478
        global $SITE, $USER;
1479
 
1480
        $output = '';
1481
        $editing = $this->page->user_is_editing();
1482
 
1483
        if ($editing) {
1484
            // Make sure section with number 1 exists.
1485
            course_create_sections_if_missing($SITE, 1);
1486
        }
1487
 
1488
        $modinfo = get_fast_modinfo($SITE);
1489
        $section = $modinfo->get_section_info(1);
1490
 
1491
 
1492
        if (($section && (!empty($modinfo->sections[1]) or !empty($section->summary))) or $editing) {
1493
 
1494
            $format = course_get_format($SITE);
1495
            $frontpageclass = $format->get_output_classname('content\\frontpagesection');
1496
            $frontpagesection = new $frontpageclass($format, $section);
1497
 
1498
            // The course outputs works with format renderers, not with course renderers.
1499
            $renderer = $format->get_renderer($this->page);
1500
            $output .= $renderer->render($frontpagesection);
1501
        }
1502
 
1503
        return $output;
1504
    }
1505
 
1506
    /**
1507
     * Output news for the frontpage (extract from site-wide news forum)
1508
     *
1509
     * @param stdClass $forum record from db table 'forum' that represents the site news forum
1510
     * @return string
1511
     */
1512
    protected function frontpage_news($forum) {
1513
        global $CFG, $SITE, $SESSION, $USER;
1514
        require_once($CFG->dirroot .'/mod/forum/lib.php');
1515
 
1516
        $output = '';
1517
 
1518
        if (isloggedin()) {
1519
            $SESSION->fromdiscussion = $CFG->wwwroot;
1520
            $subtext = '';
1521
            if (\mod_forum\subscriptions::is_subscribed($USER->id, $forum)) {
1522
                if (!\mod_forum\subscriptions::is_forcesubscribed($forum)) {
1523
                    $subtext = get_string('unsubscribe', 'forum');
1524
                }
1525
            } else {
1526
                $subtext = get_string('subscribe', 'forum');
1527
            }
1528
            $suburl = new moodle_url('/mod/forum/subscribe.php', array('id' => $forum->id, 'sesskey' => sesskey()));
1441 ariadna 1529
            $output .= html_writer::tag('div', html_writer::link($suburl, $subtext), ['class' => 'subscribelink text-end']);
1 efrain 1530
        }
1531
 
1532
        $coursemodule = get_coursemodule_from_instance('forum', $forum->id);
1533
        $context = context_module::instance($coursemodule->id);
1534
 
1535
        $entityfactory = mod_forum\local\container::get_entity_factory();
1536
        $forumentity = $entityfactory->get_forum_from_stdclass($forum, $context, $coursemodule, $SITE);
1537
 
1538
        $rendererfactory = mod_forum\local\container::get_renderer_factory();
1539
        $discussionsrenderer = $rendererfactory->get_frontpage_news_discussion_list_renderer($forumentity);
1540
        $cm = \cm_info::create($coursemodule);
1541
        return $output . $discussionsrenderer->render($USER, $cm, null, null, 0, $SITE->newsitems);
1542
    }
1543
 
1544
    /**
1545
     * Renders part of frontpage with a skip link (i.e. "My courses", "Site news", etc.)
1546
     *
1547
     * @param string $skipdivid
1548
     * @param string $contentsdivid
1549
     * @param string $header Header of the part
1550
     * @param string $contents Contents of the part
1551
     * @return string
1552
     */
1553
    protected function frontpage_part($skipdivid, $contentsdivid, $header, $contents) {
1554
        if (strval($contents) === '') {
1555
            return '';
1556
        }
1557
        $output = html_writer::link('#' . $skipdivid,
1558
            get_string('skipa', 'access', core_text::strtolower(strip_tags($header))),
1559
            array('class' => 'skip-block skip aabtn'));
1560
 
1561
        // Wrap frontpage part in div container.
1562
        $output .= html_writer::start_tag('div', array('id' => $contentsdivid));
1563
        $output .= $this->heading($header);
1564
 
1565
        $output .= $contents;
1566
 
1567
        // End frontpage part div container.
1568
        $output .= html_writer::end_tag('div');
1569
 
1570
        $output .= html_writer::tag('span', '', array('class' => 'skip-block-to', 'id' => $skipdivid));
1571
        return $output;
1572
    }
1573
 
1574
    /**
1575
     * Outputs contents for frontpage as configured in $CFG->frontpage or $CFG->frontpageloggedin
1576
     *
1577
     * @return string
1578
     */
1579
    public function frontpage() {
1580
        global $CFG, $SITE;
1581
 
1582
        $output = '';
1583
 
1584
        if (isloggedin() and !isguestuser() and isset($CFG->frontpageloggedin)) {
1585
            $frontpagelayout = $CFG->frontpageloggedin;
1586
        } else {
1587
            $frontpagelayout = $CFG->frontpage;
1588
        }
1589
 
1590
        foreach (explode(',', $frontpagelayout) as $v) {
1591
            switch ($v) {
1592
                // Display the main part of the front page.
1593
                case FRONTPAGENEWS:
1594
                    if ($SITE->newsitems) {
1595
                        // Print forums only when needed.
1596
                        require_once($CFG->dirroot .'/mod/forum/lib.php');
1597
                        if (($newsforum = forum_get_course_forum($SITE->id, 'news')) &&
1598
                                ($forumcontents = $this->frontpage_news($newsforum))) {
1599
                            $newsforumcm = get_fast_modinfo($SITE)->instances['forum'][$newsforum->id];
1600
                            $output .= $this->frontpage_part('skipsitenews', 'site-news-forum',
1601
                                $newsforumcm->get_formatted_name(), $forumcontents);
1602
                        }
1603
                    }
1604
                    break;
1605
 
1606
                case FRONTPAGEENROLLEDCOURSELIST:
1607
                    $mycourseshtml = $this->frontpage_my_courses();
1608
                    if (!empty($mycourseshtml)) {
1609
                        $output .= $this->frontpage_part('skipmycourses', 'frontpage-course-list',
1610
                            get_string('mycourses'), $mycourseshtml);
1611
                    }
1612
                    break;
1613
 
1614
                case FRONTPAGEALLCOURSELIST:
1615
                    $availablecourseshtml = $this->frontpage_available_courses();
1616
                    $output .= $this->frontpage_part('skipavailablecourses', 'frontpage-available-course-list',
1617
                        get_string('availablecourses'), $availablecourseshtml);
1618
                    break;
1619
 
1620
                case FRONTPAGECATEGORYNAMES:
1621
                    $output .= $this->frontpage_part('skipcategories', 'frontpage-category-names',
1622
                        get_string('categories'), $this->frontpage_categories_list());
1623
                    break;
1624
 
1625
                case FRONTPAGECATEGORYCOMBO:
1626
                    $output .= $this->frontpage_part('skipcourses', 'frontpage-category-combo',
1627
                        get_string('courses'), $this->frontpage_combo_list());
1628
                    break;
1629
 
1630
                case FRONTPAGECOURSESEARCH:
1631
                    $output .= $this->box($this->course_search_form(''), 'd-flex justify-content-center');
1632
                    break;
1633
 
1634
            }
1635
            $output .= '<br />';
1636
        }
1637
 
1638
        return $output;
1639
    }
1640
}
1641
 
1642
/**
1643
 * Class storing display options and functions to help display course category and/or courses lists
1644
 *
1645
 * This is a wrapper for core_course_category objects that also stores display options
1646
 * and functions to retrieve sorted and paginated lists of categories/courses.
1647
 *
1648
 * If theme overrides methods in core_course_renderers that access this class
1649
 * it may as well not use this class at all or extend it.
1650
 *
1651
 * @package   core
1652
 * @copyright 2013 Marina Glancy
1653
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1654
 */
1655
class coursecat_helper {
1656
    /** @var string [none, collapsed, expanded] how (if) display courses list */
1657
    protected $showcourses = 10; /* core_course_renderer::COURSECAT_SHOW_COURSES_COLLAPSED */
1658
    /** @var int depth to expand subcategories in the tree (deeper subcategories will be loaded by AJAX or proceed to category page by clicking on category name) */
1659
    protected $subcatdepth = 1;
1660
    /** @var array options to display courses list */
1661
    protected $coursesdisplayoptions = array();
1662
    /** @var array options to display subcategories list */
1663
    protected $categoriesdisplayoptions = array();
1664
    /** @var array additional HTML attributes */
1665
    protected $attributes = array();
1666
    /** @var array search criteria if the list is a search result */
1667
    protected $searchcriteria = null;
1668
 
1669
    /**
1670
     * Sets how (if) to show the courses - none, collapsed, expanded, etc.
1671
     *
1672
     * @param int $showcourses SHOW_COURSES_NONE, SHOW_COURSES_COLLAPSED, SHOW_COURSES_EXPANDED, etc.
1673
     * @return coursecat_helper
1674
     */
1675
    public function set_show_courses($showcourses) {
1676
        $this->showcourses = $showcourses;
1677
        // Automatically set the options to preload summary and coursecontacts for core_course_category::get_courses()
1678
        // and core_course_category::search_courses().
1679
        $this->coursesdisplayoptions['summary'] = $showcourses >= core_course_renderer::COURSECAT_SHOW_COURSES_AUTO;
1680
        $this->coursesdisplayoptions['coursecontacts'] = $showcourses >= core_course_renderer::COURSECAT_SHOW_COURSES_EXPANDED;
1681
        $this->coursesdisplayoptions['customfields'] = $showcourses >= core_course_renderer::COURSECAT_SHOW_COURSES_COLLAPSED;
1682
        return $this;
1683
    }
1684
 
1685
    /**
1686
     * Returns how (if) to show the courses - none, collapsed, expanded, etc.
1687
     *
1688
     * @return int - COURSECAT_SHOW_COURSES_NONE, COURSECAT_SHOW_COURSES_COLLAPSED, COURSECAT_SHOW_COURSES_EXPANDED, etc.
1689
     */
1690
    public function get_show_courses() {
1691
        return $this->showcourses;
1692
    }
1693
 
1694
    /**
1695
     * Sets the maximum depth to expand subcategories in the tree
1696
     *
1697
     * deeper subcategories may be loaded by AJAX or proceed to category page by clicking on category name
1698
     *
1699
     * @param int $subcatdepth
1700
     * @return coursecat_helper
1701
     */
1702
    public function set_subcat_depth($subcatdepth) {
1703
        $this->subcatdepth = $subcatdepth;
1704
        return $this;
1705
    }
1706
 
1707
    /**
1708
     * Returns the maximum depth to expand subcategories in the tree
1709
     *
1710
     * deeper subcategories may be loaded by AJAX or proceed to category page by clicking on category name
1711
     *
1712
     * @return int
1713
     */
1714
    public function get_subcat_depth() {
1715
        return $this->subcatdepth;
1716
    }
1717
 
1718
    /**
1719
     * Sets options to display list of courses
1720
     *
1721
     * Options are later submitted as argument to core_course_category::get_courses() and/or core_course_category::search_courses()
1722
     *
1723
     * Options that core_course_category::get_courses() accept:
1724
     *    - recursive - return courses from subcategories as well. Use with care,
1725
     *      this may be a huge list!
1726
     *    - summary - preloads fields 'summary' and 'summaryformat'
1727
     *    - coursecontacts - preloads course contacts
1728
     *    - customfields - preloads custom fields data
1729
     *    - isenrolled - preloads indication whether this user is enrolled in the course
1730
     *    - sort - list of fields to sort. Example
1731
     *             array('idnumber' => 1, 'shortname' => 1, 'id' => -1)
1732
     *             will sort by idnumber asc, shortname asc and id desc.
1733
     *             Default: array('sortorder' => 1)
1734
     *             Only cached fields may be used for sorting!
1735
     *    - offset
1736
     *    - limit - maximum number of children to return, 0 or null for no limit
1737
     *
1738
     * Options summary and coursecontacts are filled automatically in the set_show_courses()
1739
     *
1740
     * Also renderer can set here any additional options it wants to pass between renderer functions.
1741
     *
1742
     * @param array $options
1743
     * @return coursecat_helper
1744
     */
1745
    public function set_courses_display_options($options) {
1746
        $this->coursesdisplayoptions = $options;
1747
        $this->set_show_courses($this->showcourses); // this will calculate special display options
1748
        return $this;
1749
    }
1750
 
1751
    /**
1752
     * Sets one option to display list of courses
1753
     *
1754
     * @see coursecat_helper::set_courses_display_options()
1755
     *
1756
     * @param string $key
1757
     * @param mixed $value
1758
     * @return coursecat_helper
1759
     */
1760
    public function set_courses_display_option($key, $value) {
1761
        $this->coursesdisplayoptions[$key] = $value;
1762
        return $this;
1763
    }
1764
 
1765
    /**
1766
     * Return the specified option to display list of courses
1767
     *
1768
     * @param string $optionname option name
1769
     * @param mixed $defaultvalue default value for option if it is not specified
1770
     * @return mixed
1771
     */
1772
    public function get_courses_display_option($optionname, $defaultvalue = null) {
1773
        if (array_key_exists($optionname, $this->coursesdisplayoptions)) {
1774
            return $this->coursesdisplayoptions[$optionname];
1775
        } else {
1776
            return $defaultvalue;
1777
        }
1778
    }
1779
 
1780
    /**
1781
     * Returns all options to display the courses
1782
     *
1783
     * This array is usually passed to {@link core_course_category::get_courses()} or
1784
     * {@link core_course_category::search_courses()}
1785
     *
1786
     * @return array
1787
     */
1788
    public function get_courses_display_options() {
1789
        return $this->coursesdisplayoptions;
1790
    }
1791
 
1792
    /**
1793
     * Sets options to display list of subcategories
1794
     *
1795
     * Options 'sort', 'offset' and 'limit' are passed to core_course_category::get_children().
1796
     * Any other options may be used by renderer functions
1797
     *
1798
     * @param array $options
1799
     * @return coursecat_helper
1800
     */
1801
    public function set_categories_display_options($options) {
1802
        $this->categoriesdisplayoptions = $options;
1803
        return $this;
1804
    }
1805
 
1806
    /**
1807
     * Return the specified option to display list of subcategories
1808
     *
1809
     * @param string $optionname option name
1810
     * @param mixed $defaultvalue default value for option if it is not specified
1811
     * @return mixed
1812
     */
1813
    public function get_categories_display_option($optionname, $defaultvalue = null) {
1814
        if (array_key_exists($optionname, $this->categoriesdisplayoptions)) {
1815
            return $this->categoriesdisplayoptions[$optionname];
1816
        } else {
1817
            return $defaultvalue;
1818
        }
1819
    }
1820
 
1821
    /**
1822
     * Returns all options to display list of subcategories
1823
     *
1824
     * This array is usually passed to {@link core_course_category::get_children()}
1825
     *
1826
     * @return array
1827
     */
1828
    public function get_categories_display_options() {
1829
        return $this->categoriesdisplayoptions;
1830
    }
1831
 
1832
    /**
1833
     * Sets additional general options to pass between renderer functions, usually HTML attributes
1834
     *
1835
     * @param array $attributes
1836
     * @return coursecat_helper
1837
     */
1838
    public function set_attributes($attributes) {
1839
        $this->attributes = $attributes;
1840
        return $this;
1841
    }
1842
 
1843
    /**
1844
     * Return all attributes and erases them so they are not applied again
1845
     *
1846
     * @param string $classname adds additional class name to the beginning of $attributes['class']
1847
     * @return array
1848
     */
1849
    public function get_and_erase_attributes($classname) {
1850
        $attributes = $this->attributes;
1851
        $this->attributes = array();
1852
        if (empty($attributes['class'])) {
1853
            $attributes['class'] = '';
1854
        }
1855
        $attributes['class'] = $classname . ' '. $attributes['class'];
1856
        return $attributes;
1857
    }
1858
 
1859
    /**
1860
     * Sets the search criteria if the course is a search result
1861
     *
1862
     * Search string will be used to highlight terms in course name and description
1863
     *
1864
     * @param array $searchcriteria
1865
     * @return coursecat_helper
1866
     */
1867
    public function set_search_criteria($searchcriteria) {
1868
        $this->searchcriteria = $searchcriteria;
1869
        return $this;
1870
    }
1871
 
1872
    /**
1873
     * Returns formatted and filtered description of the given category
1874
     *
1875
     * @param core_course_category $coursecat category
1876
     * @param stdClass|array $options format options, by default [noclean,overflowdiv],
1877
     *     if context is not specified it will be added automatically
1878
     * @return string|null
1879
     */
1880
    public function get_category_formatted_description($coursecat, $options = null) {
1881
        if ($coursecat->id && $coursecat->is_uservisible() && !empty($coursecat->description)) {
1882
            if (!isset($coursecat->descriptionformat)) {
1883
                $descriptionformat = FORMAT_MOODLE;
1884
            } else {
1885
                $descriptionformat = $coursecat->descriptionformat;
1886
            }
1887
            if ($options === null) {
1888
                $options = array('noclean' => true, 'overflowdiv' => true);
1889
            } else {
1890
                $options = (array)$options;
1891
            }
1892
            $context = context_coursecat::instance($coursecat->id);
1893
            if (!isset($options['context'])) {
1894
                $options['context'] = $context;
1895
            }
1896
            $text = file_rewrite_pluginfile_urls($coursecat->description,
1897
                    'pluginfile.php', $context->id, 'coursecat', 'description', null);
1898
            return format_text($text, $descriptionformat, $options);
1899
        }
1900
        return null;
1901
    }
1902
 
1903
    /**
1904
     * Returns given course's summary with proper embedded files urls and formatted
1905
     *
1906
     * @param core_course_list_element $course
1907
     * @param array|stdClass $options additional formatting options
1908
     * @return string
1909
     */
1910
    public function get_course_formatted_summary($course, $options = array()) {
1911
        global $CFG;
1912
        require_once($CFG->libdir. '/filelib.php');
1913
        if (!$course->has_summary()) {
1914
            return '';
1915
        }
1916
        $options = (array)$options;
1917
        $context = context_course::instance($course->id);
1918
        if (!isset($options['context'])) {
1919
            $options['context'] = $context;
1920
        }
1921
        $summary = file_rewrite_pluginfile_urls($course->summary, 'pluginfile.php', $context->id, 'course', 'summary', null);
1922
        $summary = format_text($summary, $course->summaryformat, $options);
1923
        if (!empty($this->searchcriteria['search'])) {
1924
            $summary = highlight($this->searchcriteria['search'], $summary);
1925
        }
1926
        return $summary;
1927
    }
1928
 
1929
    /**
1930
     * Returns course name as it is configured to appear in courses lists formatted to course context
1931
     *
1932
     * @param core_course_list_element $course
1933
     * @param array|stdClass $options additional formatting options
1934
     * @return string
1935
     */
1936
    public function get_course_formatted_name($course, $options = array()) {
1937
        $options = (array)$options;
1938
        if (!isset($options['context'])) {
1939
            $options['context'] = context_course::instance($course->id);
1940
        }
1941
        $name = format_string(get_course_display_name_for_list($course), true, $options);
1942
        if (!empty($this->searchcriteria['search'])) {
1943
            $name = highlight($this->searchcriteria['search'], $name);
1944
        }
1945
        return $name;
1946
    }
1947
}