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