Proyectos de Subversion Moodle

Rev

Ir a la última revisión | | 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
 * Course renderer.
19
 *
20
 * @package   theme_universe
21
 * @copyright 2023 Marcin Czaja (https://rosea.io)
22
 * @license   Commercial https://themeforest.net/licenses
23
 */
24
 
25
namespace theme_universe\output\core;
26
 
27
use moodle_url;
28
use html_writer;
29
use core_course_category;
30
use coursecat_helper;
31
use stdClass;
32
use core_course_list_element;
33
use single_select;
34
use lang_string;
35
use cm_info;
36
use core_text;
37
use completion_info;
38
use course_handler;
39
use context_course;
40
use theme_universe\util\course;
41
 
42
/**
43
 * @package    theme_universe
44
 * @copyright  2023 Marcin Czaja - Rosea Themes - rosea.io
45
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
46
 */
47
class course_renderer extends \core_course_renderer {
48
 
49
    /**
50
     * Renders html to display a course search form
51
     *
52
     * @param string $value default value to populate the search field
53
     * @return string
54
     */
55
    public function course_search_form($value = '') {
56
 
57
        $data = [
58
            'action' => \core_search\manager::get_course_search_url(),
59
            'btnclass' => 'btn-primary',
60
            'inputname' => 'q',
61
            'searchstring' => get_string('searchcourses'),
62
            'hiddenfields' => (object) ['name' => 'areaids', 'value' => 'core_course-course'],
63
            'query' => $value
64
        ];
65
 
66
        return $this->render_from_template('core/search_input_fw', $data);
67
    }
68
 
69
    /**
70
     * Renders the list of courses
71
     *
72
     * This is internal function, please use core_course_renderer::courses_list() or another public
73
     * method from outside of the class
74
     *
75
     * If list of courses is specified in $courses; the argument $chelper is only used
76
     * to retrieve display options and attributes, only methods get_show_courses(),
77
     * get_courses_display_option() and get_and_erase_attributes() are called.
78
     *
79
     * @param coursecat_helper $chelper various display options
80
     * @param array $courses the list of courses to display
81
     * @param int|null $totalcount total number of courses (affects display mode if it is AUTO or pagination if applicable),
82
     *     defaulted to count($courses)
83
     * @return string
84
     *
85
     * @throws \coding_exception
86
     * @throws \dml_exception
87
     * @throws \moodle_exception
88
     */
89
    protected function coursecat_courses(coursecat_helper $chelper, $courses, $totalcount = null) {
90
        global $CFG, $COURSE;
91
 
92
        $theme = \theme_config::load('universe');
93
 
94
        if ($totalcount === null) {
95
            $totalcount = count($courses);
96
        }
97
 
98
        if (!$totalcount) {
99
            // Courses count is cached during courses retrieval.
100
            return '';
101
        }
102
 
103
        if ($chelper->get_show_courses() == self::COURSECAT_SHOW_COURSES_AUTO) {
104
            // In 'auto' course display mode we analyse if number of courses is more or less than $CFG->courseswithsummarieslimit.
105
            if ($totalcount <= $CFG->courseswithsummarieslimit) {
106
                $chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_EXPANDED);
107
            } else {
108
                $chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_COLLAPSED);
109
            }
110
        }
111
 
112
        // Prepare content of paging bar if it is needed.
113
        $paginationurl = $chelper->get_courses_display_option('paginationurl');
114
        $paginationallowall = $chelper->get_courses_display_option('paginationallowall');
115
        if ($totalcount > count($courses)) {
116
            // There are more results that can fit on one page.
117
            if ($paginationurl) {
118
                // The option paginationurl was specified, display pagingbar.
119
                $perpage = $chelper->get_courses_display_option('limit', $CFG->coursesperpage);
120
                $page = $chelper->get_courses_display_option('offset') / $perpage;
121
                $pagingbar = $this->paging_bar(
122
                    $totalcount,
123
                    $page,
124
                    $perpage,
125
                    $paginationurl->out(false, array('perpage' => $perpage))
126
                );
127
                if ($paginationallowall) {
128
                    $pagingbar .= html_writer::tag('div', html_writer::link(
129
                        $paginationurl->out(false, array('perpage' => 'all')),
130
                        get_string('showall', '', $totalcount)
131
                    ), array('class' => 'paging paging-showall'));
132
                }
133
            }
134
        } else if (($totalcount > $CFG->coursesperpage) && $paginationurl && $paginationallowall) {
135
            // There are more than one page of results and we are in 'view all' mode, suggest to go back to paginated view mode.
136
            $pagingbar = html_writer::tag(
137
                'div',
138
                html_writer::link(
139
                    $paginationurl->out(
140
                        false,
141
                        array('perpage' => $CFG->coursesperpage)
142
                    ),
143
                    get_string('showperpage', '', $CFG->coursesperpage)
144
                ),
145
                array('class' => 'paging paging-showperpage')
146
            );
147
        }
148
 
149
        // Display list of courses.
150
        $attributes = $chelper->get_and_erase_attributes('courses');
151
        $content = html_writer::start_tag('div', $attributes);
152
 
153
        if (!empty($pagingbar)) {
154
            $content .= $pagingbar;
155
        }
156
 
157
        $coursecount = 1;
158
 
159
        if ($theme->settings->courselistview == 1) {
160
            $content .= html_writer::start_tag('div', array('class' => 'rui-course--list mt-2'));
161
        } else {
162
            $content .= html_writer::start_tag('div', array('class' => 'rui-course-card-deck mt-2'));
163
        }
164
 
165
        foreach ($courses as $course) {
166
            $content .= $this->coursecat_coursebox($chelper, $course);
167
 
168
            $coursecount++;
169
        }
170
 
171
        $content .= html_writer::end_tag('div');
172
 
173
        if (!empty($pagingbar)) {
174
            $content .= $pagingbar;
175
        }
176
 
177
        if (!empty($morelink)) {
178
            $content .= $morelink;
179
        }
180
 
181
        $content .= html_writer::end_tag('div'); // End courses.
182
 
183
        return $content;
184
    }
185
 
186
    protected function coursecat_subcategories(coursecat_helper $chelper, $coursecat, $depth) {
187
        global $CFG;
188
        $subcategories = array();
189
        if (!$chelper->get_categories_display_option('nodisplay')) {
190
            $subcategories = $coursecat->get_children($chelper->get_categories_display_options());
191
        }
192
        $totalcount = $coursecat->get_children_count();
193
        if (!$totalcount) {
194
            // Note that we call core_course_category::get_children_count() AFTER core_course_category::get_children().
195
            // To avoid extra DB requests.
196
            // Categories count is cached during children categories retrieval.
197
            return '';
198
        }
199
 
200
        // Prepare content of paging bar or more link if it is needed.
201
        $paginationurl = $chelper->get_categories_display_option('paginationurl');
202
        $paginationallowall = $chelper->get_categories_display_option('paginationallowall');
203
        if ($totalcount > count($subcategories)) {
204
            if ($paginationurl) {
205
                // The option 'paginationurl was specified, display pagingbar.
206
                $perpage = $chelper->get_categories_display_option('limit', $CFG->coursesperpage);
207
                $page = $chelper->get_categories_display_option('offset') / $perpage;
208
                $pagingbar = $this->paging_bar(
209
                    $totalcount,
210
                    $page,
211
                    $perpage,
212
                    $paginationurl->out(false, array('perpage' => $perpage))
213
                );
214
                if ($paginationallowall) {
215
                    $pagingbar .= html_writer::tag('div', html_writer::link(
216
                        $paginationurl->out(false, array('perpage' => 'all')),
217
                        get_string('showall', '', $totalcount)
218
                    ), array('class' => 'paging paging-showall'));
219
                }
220
            } else if ($viewmoreurl = $chelper->get_categories_display_option('viewmoreurl')) {
221
                // The option 'viewmoreurl' was specified, display more link (if it is link to category view page, add category id).
222
                if ($viewmoreurl->compare(new moodle_url('/course/index.php'), URL_MATCH_BASE)) {
223
                    $viewmoreurl->param('categoryid', $coursecat->id);
224
                }
225
                $viewmoretext = $chelper->get_categories_display_option('viewmoretext', new lang_string('viewmore'));
226
                $morelink = html_writer::tag(
227
                    'div',
228
                    html_writer::link($viewmoreurl, $viewmoretext, array('class' => 'btn btn-secondary w-100')),
229
                    array('class' => 'paging paging-morelink wrapper-fw')
230
                );
231
            }
232
        } else if (($totalcount > $CFG->coursesperpage) && $paginationurl && $paginationallowall) {
233
            // There are more than one page of results and we are in 'view all' mode, suggest to go back to paginated view mode.
234
            $pagingbar = html_writer::tag('div', html_writer::link(
235
                $paginationurl->out(false, array('perpage' => $CFG->coursesperpage)),
236
                get_string('showperpage', '', $CFG->coursesperpage)
237
            ), array('class' => 'paging paging-showperpage'));
238
        }
239
 
240
        // Display list of subcategories.
241
        $content = html_writer::start_tag('div', array('class' => 'subcategories'));
242
 
243
        if (!empty($pagingbar)) {
244
            $content .= $pagingbar;
245
        }
246
 
247
        foreach ($subcategories as $subcategory) {
248
            $content .= $this->coursecat_category($chelper, $subcategory, $depth);
249
        }
250
 
251
        if (!empty($pagingbar)) {
252
            $content .= $pagingbar;
253
        }
254
        if (!empty($morelink)) {
255
            $content .= $morelink;
256
        }
257
 
258
        $content .= html_writer::end_tag('div');
259
        return $content;
260
    }
261
 
262
    /**
263
     * Returns HTML to display course content (summary, course contacts and optionally category name)
264
     *
265
     * This method is called from coursecat_coursebox() and may be re-used in AJAX
266
     *
267
     * @param coursecat_helper $chelper various display options
268
     * @param stdClass|core_course_list_element $course
269
     *
270
     * @return string
271
     *
272
     * @throws \coding_exception
273
     * @throws \dml_exception
274
     * @throws \moodle_exception
275
     */
276
    protected function coursecat_coursebox_content(coursecat_helper $chelper, $course) {
277
        global $DB, $COURSE;
278
        $theme = \theme_config::load('universe');
279
 
280
        if ($course instanceof stdClass) {
281
            $course = new core_course_list_element($course);
282
        }
283
 
284
        $cccteachers = $theme->settings->cccteachers;
285
        $cccsummary = $theme->settings->cccsummary;
286
        $coursecarddesclimit = $theme->settings->coursecarddesclimit;
287
        $courseprogressbar = $theme->settings->courseprogressbar;
288
 
289
        $courseutil = new course($course);
290
 
291
        $coursecontacts = $courseutil->get_course_contacts();
292
 
293
        $courseenrolmenticons = $courseutil->get_enrolment_icons();
294
        $courseenrolmenticons = !empty($courseenrolmenticons) ? $this->render_enrolment_icons($courseenrolmenticons) : false;
295
 
296
        $courseprogress = $courseutil->get_progress();
297
        $hasprogress = $courseprogress != null;
298
 
299
        if ($theme->settings->courselangbadge == 1) {
300
            $forcedlanguage = strval($course->lang);
301
        } else {
302
            $forcedlanguage = null;
303
        }
304
 
305
        if ($theme->settings->showcustomfields == 1) {
306
            $showcustomfields = true;
307
        } else {
308
            $showcustomfields = false;
309
        }
310
 
311
        $data = [
312
            'id' => $course->id,
313
            'fullname' => $chelper->get_course_formatted_name($course),
314
            'visible' => $course->visible,
315
            'image' => $courseutil->get_summary_image(),
316
            'summary' => $courseutil->get_summary($chelper),
317
            'category' => $courseutil->get_category(),
318
            'customfields' => $courseutil->course_get_taux(),
319
            'showcustomfields' => $showcustomfields,
320
            'hasprogress' => $hasprogress,
321
            'progress' => (int) $courseprogress,
322
            'hasenrolmenticons' => $courseenrolmenticons != false,
323
            'enrolmenticons' => $courseenrolmenticons,
324
            'hascontacts' => !empty($coursecontacts),
325
            'contacts' => $coursecontacts,
326
            'courseprogressbar' => $courseprogressbar,
327
            'cccteachers' => $cccteachers,
328
            'cccsummary' => $cccsummary,
329
            'coursecarddesclimit' => $coursecarddesclimit,
330
            'forcedlanguage' => $forcedlanguage
331
        ];
332
 
333
        if ($theme->settings->courselistview == 1) {
334
            return $this->render_from_template('theme_universe/custom_courselist', $data);
335
        } else {
336
            return $this->render_from_template('theme_universe/custom_coursecard', $data);
337
        }
338
    }
339
 
340
    /**
341
     * Displays one course in the list of courses.
342
     *
343
     * This is an internal function, to display an information about just one course
344
     * please use core_course_renderer::course_info_box()
345
     *
346
     * @param coursecat_helper $chelper various display options
347
     * @param core_course_list_element|stdClass $course
348
     * @param string $additionalclasses additional classes to add to the main <div> tag (usually
349
     *    depend on the course position in list - first/last/even/odd)
350
     * @return string
351
     */
352
    protected function coursecat_coursebox(coursecat_helper $chelper, $course, $additionalclasses = '') {
353
        if (!isset($this->strings->summary)) {
354
            $this->strings->summary = get_string('summary');
355
        }
356
 
357
        if ($chelper->get_show_courses() <= self::COURSECAT_SHOW_COURSES_COUNT) {
358
            return '';
359
        }
360
 
361
        if ($course instanceof stdClass) {
362
            $course = new core_course_list_element($course);
363
        }
364
 
365
        return $this->coursecat_coursebox_content($chelper, $course);
366
    }
367
 
368
    /**
369
     * Returns HTML to display a tree of subcategories and courses in the given category
370
     *
371
     * @param coursecat_helper $chelper various display options
372
     * @param core_course_category $coursecat top category (this category's name and description will NOT be added to the tree)
373
     * @return string
374
     */
375
    protected function coursecat_tree(coursecat_helper $chelper, $coursecat) {
376
        // Reset the category expanded flag for this course category tree first.
377
        $this->categoryexpandedonload = false;
378
        $categorycontent = $this->coursecat_category_content($chelper, $coursecat, 1);
379
        if (empty($categorycontent)) {
380
            return '';
381
        }
382
 
383
        // Start content generation.
384
        $content = '';
385
        $attributes = $chelper->get_and_erase_attributes('course_category_tree clearfix');
386
        $content .= html_writer::start_tag('div', $attributes);
387
 
388
        if ($coursecat->get_children_count()) {
389
            $classes = array(
390
                'collapseexpand', 'aabtn'
391
            );
392
 
393
            // Check if the category content contains subcategories with children's content loaded.
394
            if ($this->categoryexpandedonload) {
395
                $classes[] = 'collapse-all';
396
                $linkname = get_string('collapseall');
397
            } else {
398
                $linkname = get_string('expandall');
399
            }
400
 
401
            // Only show the collapse/expand if there are children to expand.
402
            $content .= html_writer::start_tag('div', array('class' => 'wrapper-fw collapsible-actions position-relative'));
403
            $content .= html_writer::link('#', $linkname, array('class' => implode(' ', $classes)));
404
            $content .= html_writer::end_tag('div');
405
            $this->page->requires->strings_for_js(array('collapseall', 'expandall'), 'moodle');
406
        }
407
 
408
        $content .= html_writer::tag('div', $categorycontent, array('class' => 'content'));
409
 
410
        $content .= html_writer::end_tag('div'); // End course_category_tree.
411
 
412
        return $content;
413
    }
414
 
415
    /**
416
     * Returns HTML to display a course category as a part of a tree
417
     *
418
     * This is an internal function, to display a particular category and all its contents
419
     * use {@link core_course_renderer::course_category()}
420
     *
421
     * @param coursecat_helper $chelper various display options
422
     * @param core_course_category $coursecat
423
     * @param int $depth depth of this category in the current tree
424
     * @return string
425
     */
426
    protected function coursecat_category(coursecat_helper $chelper, $coursecat, $depth) {
427
        // Open category tag.
428
        $classes = array('category');
429
        if (empty($coursecat->visible)) {
430
            $classes[] = 'dimmed_category';
431
        }
432
        if ($chelper->get_subcat_depth() > 0 && $depth >= $chelper->get_subcat_depth()) {
433
            // Do not load content.
434
            $categorycontent = '';
435
            $classes[] = 'notloaded';
436
            if (
437
                $coursecat->get_children_count() ||
438
                ($chelper->get_show_courses() >= self::COURSECAT_SHOW_COURSES_COLLAPSED && $coursecat->get_courses_count())
439
            ) {
440
                $classes[] = 'with_children';
441
                $classes[] = 'collapsed';
442
            }
443
        } else {
444
            // Load category content.
445
            $categorycontent = $this->coursecat_category_content($chelper, $coursecat, $depth);
446
            $classes[] = 'loaded';
447
            if (!empty($categorycontent)) {
448
                $classes[] = 'with_children';
449
                // Category content loaded with children.
450
                $this->categoryexpandedonload = true;
451
            }
452
        }
453
 
454
        // Make sure JS file to expand category content is included.
455
        $this->coursecat_include_js();
456
 
457
        $content = html_writer::start_tag('div', array(
458
            'class' => join(' ', $classes),
459
            'data-categoryid' => $coursecat->id,
460
            'data-depth' => $depth,
461
            'data-showcourses' => $chelper->get_show_courses(),
462
            'data-type' => self::COURSECAT_TYPE_CATEGORY,
463
        ));
464
 
465
        // Category name.
466
        $categoryname = html_writer::link(
467
            new moodle_url(
468
                '/course/index.php',
469
                array('categoryid' => $coursecat->id)
470
            ),
471
            $coursecat->get_formatted_name(),
472
            array('class' => 'rui-category-link')
473
        );
474
 
475
        $icon = '<svg class="mr-1" width="18" height="18" fill="none" viewBox="0 0 24 24">
476
        <path stroke="currentColor"
477
        stroke-linecap="round"
478
        stroke-linejoin="round"
479
        stroke-width="2"
480
        d="M19.25 5.75C19.25 5.19772 18.8023 4.75 18.25 4.75H14C12.8954 4.75
481
        12 5.64543 12 6.75V19.25L12.8284 18.4216C13.5786 17.6714 14.596 17.25 15.6569
482
        17.25H18.25C18.8023 17.25 19.25 16.8023 19.25 16.25V5.75Z"></path>
483
        <path stroke="currentColor"
484
        stroke-linecap="round"
485
        stroke-linejoin="round"
486
        stroke-width="2"
487
        d="M4.75 5.75C4.75 5.19772 5.19772 4.75 5.75 4.75H10C11.1046 4.75 12
488
        5.64543 12 6.75V19.25L11.1716 18.4216C10.4214
489
        17.6714 9.40401 17.25 8.34315 17.25H5.75C5.19772 17.25 4.75 16.8023
490
        4.75 16.25V5.75Z"></path>
491
        </svg>';
492
 
493
        $a = $coursecat->get_courses_count();
494
        $b = $coursecat->get_children_count();
495
        $coursesum = $a + $b;
496
 
497
        $coursescount = $icon . $coursesum;
498
 
499
        $content .= html_writer::start_tag('div', array('class' => 'wrapper-fw info'));
500
        $content .= html_writer::start_tag('h5', array('class' => 'categoryname aabtn'));
501
        $content .= $categoryname;
502
        $acontent = '';
503
        if ($viewmoreurl = $chelper->get_courses_display_option('viewmoreurl')) {
504
            // The option for 'View more' link was specified, display more link.
505
            $viewmoretext = $chelper->get_courses_display_option('viewmoretext', new \lang_string('viewmore'));
506
            // The option 'viewmoreurl' was specified, display more link (if it is link to category view page, add category id).
507
            if ($viewmoreurl->compare(new moodle_url('/course/index.php'), URL_MATCH_BASE)) {
508
                $viewmoreurl->param('categoryid', $coursecat->id);
509
            }
510
            $acontent .= html_writer::tag('div', html_writer::link(
511
                $viewmoreurl,
512
                $viewmoretext,
513
                array('class' => 'btn btn-sm btn-light ml-2')
514
            ));
515
        }
516
        $content .= '<div class="rui-number-of-courses d-inline-flex">' .
517
            html_writer::tag('span', $coursescount, array('class' => 'badge badge-sq badge-primary')) . $acontent . '</div>';
518
 
519
        $content .= html_writer::end_tag('h5');
520
 
521
        $content .= html_writer::end_tag('div'); // End .info.
522
 
523
        // Add category content to the output.
524
        $content .= html_writer::tag('div', $categorycontent, array('class' => 'content'));
525
 
526
        $content .= html_writer::end_tag('div'); // End .category.
527
 
528
        // Return the course category tree HTML.
529
        return $content;
530
    }
531
 
532
    /**
533
     * Returns the first course's summary issue
534
     *
535
     * @param \core_course_list_element $course
536
     * @param string $courselink
537
     *
538
     * @return string
539
     */
540
    public static function get_course_summary_image($course, $courselink) {
541
        global $CFG;
542
 
543
        $contentimage = '';
544
        foreach ($course->get_course_overviewfiles() as $file) {
545
            $isimage = $file->is_valid_image();
546
            $url = moodle_url::make_file_url(
547
                "$CFG->wwwroot/pluginfile.php",
548
                '/' . $file->get_contextid() . '/' . $file->get_component() . '/' .
549
                    $file->get_filearea() . $file->get_filepath() . $file->get_filename(),
550
                !$isimage
551
            );
552
            if ($isimage) {
553
                $contentimage .= '<a href="' . $courselink . '">
554
                    <figure class="rui-course-card-img-top" style="background-image: url(' . $url . ');"></figure>
555
                    </a>';
556
                break;
557
            }
558
        }
559
 
560
        if (empty($contentimage)) {
561
            $contentimage = '';
562
        }
563
 
564
        return $contentimage;
565
    }
566
 
567
    /**
568
     * Returns enrolment icons
569
     *
570
     * @param array $icons
571
     *
572
     * @return array
573
     */
574
    protected function render_enrolment_icons(array $icons): array {
575
        $data = [];
576
 
577
        foreach ($icons as $icon) {
578
            $data[] = $this->render($icon);
579
        }
580
 
581
        return $data;
582
    }
583
}