Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
// This file is part of Moodle - http://moodle.org/
3
//
4
// Moodle is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// Moodle is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
 
17
/**
18
 * Class containing data for my overview block.
19
 *
20
 * @package    block_myoverview
21
 * @copyright  2017 Ryan Wyllie <ryan@moodle.com>
22
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23
 */
24
namespace block_myoverview\output;
25
defined('MOODLE_INTERNAL') || die();
26
 
27
use core_competency\url;
28
use renderable;
29
use renderer_base;
30
use templatable;
31
use stdClass;
32
 
33
require_once($CFG->dirroot . '/blocks/myoverview/lib.php');
34
 
35
/**
36
 * Class containing data for my overview block.
37
 *
38
 * @copyright  2018 Bas Brands <bas@moodle.com>
39
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
40
 */
41
class main implements renderable, templatable {
42
 
43
    /**
44
     * Store the grouping preference.
45
     *
46
     * @var string String matching the grouping constants defined in myoverview/lib.php
47
     */
48
    private $grouping;
49
 
50
    /**
51
     * Store the sort preference.
52
     *
53
     * @var string String matching the sort constants defined in myoverview/lib.php
54
     */
55
    private $sort;
56
 
57
    /**
58
     * Store the view preference.
59
     *
60
     * @var string String matching the view/display constants defined in myoverview/lib.php
61
     */
62
    private $view;
63
 
64
    /**
65
     * Store the paging preference.
66
     *
67
     * @var string String matching the paging constants defined in myoverview/lib.php
68
     */
69
    private $paging;
70
 
71
    /**
72
     * Store the display categories config setting.
73
     *
74
     * @var boolean
75
     */
76
    private $displaycategories;
77
 
78
    /**
79
     * Store the configuration values for the myoverview block.
80
     *
81
     * @var array Array of available layouts matching view/display constants defined in myoverview/lib.php
82
     */
83
    private $layouts;
84
 
85
    /**
86
     * Store a course grouping option setting
87
     *
88
     * @var boolean
89
     */
90
    private $displaygroupingallincludinghidden;
91
 
92
    /**
93
     * Store a course grouping option setting.
94
     *
95
     * @var boolean
96
     */
97
    private $displaygroupingall;
98
 
99
    /**
100
     * Store a course grouping option setting.
101
     *
102
     * @var boolean
103
     */
104
    private $displaygroupinginprogress;
105
 
106
    /**
107
     * Store a course grouping option setting.
108
     *
109
     * @var boolean
110
     */
111
    private $displaygroupingfuture;
112
 
113
    /**
114
     * Store a course grouping option setting.
115
     *
116
     * @var boolean
117
     */
118
    private $displaygroupingpast;
119
 
120
    /**
121
     * Store a course grouping option setting.
122
     *
123
     * @var boolean
124
     */
125
    private $displaygroupingfavourites;
126
 
127
    /**
128
     * Store a course grouping option setting.
129
     *
130
     * @var boolean
131
     */
132
    private $displaygroupinghidden;
133
 
134
    /**
135
     * Store a course grouping option setting.
136
     *
137
     * @var bool
138
     */
139
    private $displaygroupingcustomfield;
140
 
141
    /**
142
     * Store the custom field used by customfield grouping.
143
     *
144
     * @var string
145
     */
146
    private $customfiltergrouping;
147
 
148
    /**
149
     * Store the selected custom field value to group by.
150
     *
151
     * @var string
152
     */
153
    private $customfieldvalue;
154
 
155
    /** @var bool true if grouping selector should be shown, otherwise false. */
156
    protected $displaygroupingselector;
157
 
158
    /**
159
     * main constructor.
160
     * Initialize the user preferences
161
     *
162
     * @param string $grouping Grouping user preference
163
     * @param string $sort Sort user preference
164
     * @param string $view Display user preference
165
     * @param int $paging
166
     * @param string $customfieldvalue
167
     *
168
     * @throws \dml_exception
169
     */
170
    public function __construct($grouping, $sort, $view, $paging, $customfieldvalue = null) {
171
        global $CFG;
172
        // Get plugin config.
173
        $config = get_config('block_myoverview');
174
 
175
        // Build the course grouping option name to check if the given grouping is enabled afterwards.
176
        $groupingconfigname = 'displaygrouping'.$grouping;
177
        // Check the given grouping and remember it if it is enabled.
178
        if ($grouping && $config->$groupingconfigname == true) {
179
            $this->grouping = $grouping;
180
 
181
            // Otherwise fall back to another grouping in a reasonable order.
182
            // This is done to prevent one-time UI glitches in the case when a user has chosen a grouping option previously which
183
            // was then disabled by the admin in the meantime.
184
        } else {
185
            $this->grouping = $this->get_fallback_grouping($config);
186
        }
187
        unset ($groupingconfigname);
188
 
189
        // Remember which custom field value we were using, if grouping by custom field.
190
        $this->customfieldvalue = $customfieldvalue;
191
 
192
        // Check and remember the given sorting.
193
        if ($sort) {
194
            $this->sort = $sort;
195
        } else if ($CFG->courselistshortnames) {
196
            $this->sort = BLOCK_MYOVERVIEW_SORTING_SHORTNAME;
197
        } else {
198
            $this->sort = BLOCK_MYOVERVIEW_SORTING_TITLE;
199
        }
200
        // In case sorting remembered is shortname and display extended course names not checked,
201
        // we should revert sorting to title.
202
        if (!$CFG->courselistshortnames && $sort == BLOCK_MYOVERVIEW_SORTING_SHORTNAME) {
203
            $this->sort = BLOCK_MYOVERVIEW_SORTING_TITLE;
204
        }
205
 
206
        // Check and remember the given view.
207
        $this->view = $view ? $view : BLOCK_MYOVERVIEW_VIEW_CARD;
208
 
209
        // Check and remember the given page size, `null` indicates no page size set
210
        // while a `0` indicates a paging size of `All`.
211
        if (!is_null($paging) && $paging == BLOCK_MYOVERVIEW_PAGING_ALL) {
212
            $this->paging = BLOCK_MYOVERVIEW_PAGING_ALL;
213
        } else {
214
            $this->paging = $paging ? $paging : BLOCK_MYOVERVIEW_PAGING_12;
215
        }
216
 
217
        // Check and remember if the course categories should be shown or not.
218
        if (!$config->displaycategories) {
219
            $this->displaycategories = BLOCK_MYOVERVIEW_DISPLAY_CATEGORIES_OFF;
220
        } else {
221
            $this->displaycategories = BLOCK_MYOVERVIEW_DISPLAY_CATEGORIES_ON;
222
        }
223
 
224
        // Get and remember the available layouts.
225
        $this->set_available_layouts();
226
        $this->view = $view ? $view : reset($this->layouts);
227
 
228
        // Check and remember if the particular grouping options should be shown or not.
229
        $this->displaygroupingallincludinghidden = $config->displaygroupingallincludinghidden;
230
        $this->displaygroupingall = $config->displaygroupingall;
231
        $this->displaygroupinginprogress = $config->displaygroupinginprogress;
232
        $this->displaygroupingfuture = $config->displaygroupingfuture;
233
        $this->displaygroupingpast = $config->displaygroupingpast;
234
        $this->displaygroupingfavourites = $config->displaygroupingfavourites;
235
        $this->displaygroupinghidden = $config->displaygroupinghidden;
236
        $this->displaygroupingcustomfield = ($config->displaygroupingcustomfield && $config->customfiltergrouping);
237
        $this->customfiltergrouping = $config->customfiltergrouping;
238
 
239
        // Check and remember if the grouping selector should be shown at all or not.
240
        // It will be shown if more than 1 grouping option is enabled.
241
        $displaygroupingselectors = array($this->displaygroupingallincludinghidden,
242
                $this->displaygroupingall,
243
                $this->displaygroupinginprogress,
244
                $this->displaygroupingfuture,
245
                $this->displaygroupingpast,
246
                $this->displaygroupingfavourites,
247
                $this->displaygroupinghidden);
248
        $displaygroupingselectorscount = count(array_filter($displaygroupingselectors));
249
        if ($displaygroupingselectorscount > 1 || $this->displaygroupingcustomfield) {
250
            $this->displaygroupingselector = true;
251
        } else {
252
            $this->displaygroupingselector = false;
253
        }
254
        unset ($displaygroupingselectors, $displaygroupingselectorscount);
255
    }
256
    /**
257
     * Determine the most sensible fallback grouping to use (in cases where the stored selection
258
     * is no longer available).
259
     * @param object $config
260
     * @return string
261
     */
262
    private function get_fallback_grouping($config) {
263
        if ($config->displaygroupingall == true) {
264
            return BLOCK_MYOVERVIEW_GROUPING_ALL;
265
        }
266
        if ($config->displaygroupingallincludinghidden == true) {
267
            return BLOCK_MYOVERVIEW_GROUPING_ALLINCLUDINGHIDDEN;
268
        }
269
        if ($config->displaygroupinginprogress == true) {
270
            return BLOCK_MYOVERVIEW_GROUPING_INPROGRESS;
271
        }
272
        if ($config->displaygroupingfuture == true) {
273
            return BLOCK_MYOVERVIEW_GROUPING_FUTURE;
274
        }
275
        if ($config->displaygroupingpast == true) {
276
            return BLOCK_MYOVERVIEW_GROUPING_PAST;
277
        }
278
        if ($config->displaygroupingfavourites == true) {
279
            return BLOCK_MYOVERVIEW_GROUPING_FAVOURITES;
280
        }
281
        if ($config->displaygroupinghidden == true) {
282
            return BLOCK_MYOVERVIEW_GROUPING_HIDDEN;
283
        }
284
        if ($config->displaygroupingcustomfield == true) {
285
            return BLOCK_MYOVERVIEW_GROUPING_CUSTOMFIELD;
286
        }
287
        // In this case, no grouping option is enabled and the grouping is not needed at all.
288
        // But it's better not to leave $this->grouping unset for any unexpected case.
289
        return BLOCK_MYOVERVIEW_GROUPING_ALLINCLUDINGHIDDEN;
290
    }
291
 
292
    /**
293
     * Set the available layouts based on the config table settings,
294
     * if none are available, defaults to the cards view.
295
     *
296
     * @throws \dml_exception
297
     *
298
     */
299
    public function set_available_layouts() {
300
 
301
        if ($config = get_config('block_myoverview', 'layouts')) {
302
            $this->layouts = explode(',', $config);
303
        } else {
304
            $this->layouts = array(BLOCK_MYOVERVIEW_VIEW_CARD);
305
        }
306
    }
307
 
308
    /**
309
     * Get the user preferences as an array to figure out what has been selected.
310
     *
311
     * @return array $preferences Array with the pref as key and value set to true
312
     */
313
    public function get_preferences_as_booleans() {
314
        $preferences = [];
315
        $preferences[$this->sort] = true;
316
        $preferences[$this->grouping] = true;
317
        // Only use the user view/display preference if it is in available layouts.
318
        if (in_array($this->view, $this->layouts)) {
319
            $preferences[$this->view] = true;
320
        } else {
321
            $preferences[reset($this->layouts)] = true;
322
        }
323
 
324
        return $preferences;
325
    }
326
 
327
    /**
328
     * Format a layout into an object for export as a Context variable to template.
329
     *
330
     * @param string $layoutname
331
     *
332
     * @return \stdClass $layout an object representation of a layout
333
     * @throws \coding_exception
334
     */
335
    public function format_layout_for_export($layoutname) {
336
        $layout = new stdClass();
337
 
338
        $layout->id = $layoutname;
339
        $layout->name = get_string($layoutname, 'block_myoverview');
340
        $layout->active = $this->view == $layoutname ? true : false;
341
        $layout->arialabel = get_string('aria:' . $layoutname, 'block_myoverview');
342
 
343
        return $layout;
344
    }
345
 
346
    /**
347
     * Get the available layouts formatted for export.
348
     *
349
     * @return array an array of objects representing available layouts
350
     */
351
    public function get_formatted_available_layouts_for_export() {
352
 
353
        return array_map(array($this, 'format_layout_for_export'), $this->layouts);
354
 
355
    }
356
 
357
    /**
358
     * Get the list of values to add to the grouping dropdown
359
     * @return object[] containing name, value and active fields
360
     */
361
    public function get_customfield_values_for_export() {
362
        global $DB, $USER;
363
        if (!$this->displaygroupingcustomfield) {
364
            return [];
365
        }
366
 
367
        // Get the relevant customfield ID within the core_course/course component/area.
368
        $fieldid = $DB->get_field_sql("
369
            SELECT f.id
370
              FROM {customfield_field} f
371
              JOIN {customfield_category} c ON c.id = f.categoryid
372
             WHERE f.shortname = :shortname AND c.component = 'core_course' AND c.area = 'course'
373
        ", ['shortname' => $this->customfiltergrouping]);
374
        if (!$fieldid) {
375
            return [];
376
        }
377
        $courses = enrol_get_all_users_courses($USER->id, true);
378
        if (!$courses) {
379
            return [];
380
        }
381
        list($csql, $params) = $DB->get_in_or_equal(array_keys($courses), SQL_PARAMS_NAMED);
382
        $select = "instanceid $csql AND fieldid = :fieldid";
383
        $params['fieldid'] = $fieldid;
384
        $distinctablevalue = $DB->sql_compare_text('value');
385
        $values = $DB->get_records_select_menu('customfield_data', $select, $params, '',
386
            "DISTINCT $distinctablevalue, $distinctablevalue AS value2");
387
        \core_collator::asort($values, \core_collator::SORT_NATURAL);
388
        $values = array_filter($values);
389
        if (!$values) {
390
            return [];
391
        }
392
        $field = \core_customfield\field_controller::create($fieldid);
393
        $isvisible = $field->get_configdata_property('visibility') == \core_course\customfield\course_handler::VISIBLETOALL;
394
        // Only visible fields to everybody supporting course grouping will be displayed.
395
        if (!$field->supports_course_grouping() || !$isvisible) {
396
            return []; // The field shouldn't have been selectable in the global settings, but just skip it now.
397
        }
398
        $values = $field->course_grouping_format_values($values);
399
        $customfieldactive = ($this->grouping === BLOCK_MYOVERVIEW_GROUPING_CUSTOMFIELD);
400
        $ret = [];
401
        foreach ($values as $value => $name) {
402
            $ret[] = (object)[
403
                'name' => $name,
404
                'value' => $value,
405
                'active' => ($customfieldactive && ($this->customfieldvalue == $value)),
406
            ];
407
        }
408
        return $ret;
409
    }
410
 
411
    /**
412
     * Export this data so it can be used as the context for a mustache template.
413
     *
414
     * @param \renderer_base $output
415
     * @return array Context variables for the template
416
     * @throws \coding_exception
417
     *
418
     */
419
    public function export_for_template(renderer_base $output) {
420
        global $CFG, $USER;
421
 
422
        $nocoursesurl = $output->image_url('courses', 'block_myoverview')->out();
423
 
424
        $newcourseurl = '';
425
        $coursecat = \core_course_category::user_top();
426
        if ($coursecat && ($category = \core_course_category::get_nearest_editable_subcategory($coursecat, ['create']))) {
427
            $newcourseurl = new \moodle_url('/course/edit.php', ['category' => $category->id]);
428
        }
429
 
430
        $customfieldvalues = $this->get_customfield_values_for_export();
431
        $selectedcustomfield = '';
432
        if ($this->grouping == BLOCK_MYOVERVIEW_GROUPING_CUSTOMFIELD) {
433
            foreach ($customfieldvalues as $field) {
434
                if ($field->value == $this->customfieldvalue) {
435
                    $selectedcustomfield = $field->name;
436
                    break;
437
                }
438
            }
439
            // If the selected custom field value has not been found (possibly because the field has
440
            // been changed in the settings) find a suitable fallback.
441
            if (!$selectedcustomfield) {
442
                $this->grouping = $this->get_fallback_grouping(get_config('block_myoverview'));
443
                if ($this->grouping == BLOCK_MYOVERVIEW_GROUPING_CUSTOMFIELD) {
444
                    // If the fallback grouping is still customfield, then select the first field.
445
                    $firstfield = reset($customfieldvalues);
446
                    if ($firstfield) {
447
                        $selectedcustomfield = $firstfield->name;
448
                        $this->customfieldvalue = $firstfield->value;
449
                    }
450
                }
451
            }
452
        }
453
        $preferences = $this->get_preferences_as_booleans();
454
        $availablelayouts = $this->get_formatted_available_layouts_for_export();
455
        $sort = '';
456
        if ($this->sort == BLOCK_MYOVERVIEW_SORTING_SHORTNAME) {
457
            $sort = 'shortname';
458
        } else {
459
            $sort = $this->sort == BLOCK_MYOVERVIEW_SORTING_TITLE ? 'fullname' : 'ul.timeaccess desc';
460
        }
461
 
462
        $defaultvariables = [
463
            'totalcoursecount' => count(enrol_get_all_users_courses($USER->id, true)),
464
            'nocoursesimg' => $nocoursesurl,
465
            'newcourseurl' => $newcourseurl,
466
            'grouping' => $this->grouping,
467
            'sort' => $sort,
468
            // If the user preference display option is not available, default to first available layout.
469
            'view' => in_array($this->view, $this->layouts) ? $this->view : reset($this->layouts),
470
            'paging' => $this->paging,
471
            'layouts' => $availablelayouts,
472
            'displaycategories' => $this->displaycategories,
473
            'displaydropdown' => (count($availablelayouts) > 1) ? true : false,
474
            'displaygroupingallincludinghidden' => $this->displaygroupingallincludinghidden,
475
            'displaygroupingall' => $this->displaygroupingall,
476
            'displaygroupinginprogress' => $this->displaygroupinginprogress,
477
            'displaygroupingfuture' => $this->displaygroupingfuture,
478
            'displaygroupingpast' => $this->displaygroupingpast,
479
            'displaygroupingfavourites' => $this->displaygroupingfavourites,
480
            'displaygroupinghidden' => $this->displaygroupinghidden,
481
            'displaygroupingselector' => $this->displaygroupingselector,
482
            'displaygroupingcustomfield' => $this->displaygroupingcustomfield && $customfieldvalues,
483
            'customfieldname' => $this->customfiltergrouping,
484
            'customfieldvalue' => $this->customfieldvalue,
485
            'customfieldvalues' => $customfieldvalues,
486
            'selectedcustomfield' => $selectedcustomfield,
487
            'showsortbyshortname' => $CFG->courselistshortnames,
488
        ];
489
        return array_merge($defaultvariables, $preferences);
490
 
491
    }
492
 
493
    /**
494
     * Export this data so it can be used as the context for a mustache template.
495
     *
496
     * @param \renderer_base $output
497
     * @return array Context variables for the template
498
     * @throws \coding_exception
499
     *
500
     */
501
    public function export_for_zero_state_template(renderer_base $output) {
502
        global $CFG, $DB;
503
 
504
        $nocoursesimg = $output->image_url('courses', 'block_myoverview');
505
 
506
        $coursecat = \core_course_category::user_top();
507
        if ($coursecat) {
508
            $category = \core_course_category::get_nearest_editable_subcategory($coursecat, ['moodle/course:request']);
509
            if ($category && $category->can_request_course()) {
510
                // Add Request a course button.
511
                $button = new \single_button(
512
                    new \moodle_url('/course/request.php', ['category' => $category->id]),
513
                    get_string('requestcourse'),
514
                    'post',
515
                    \single_button::BUTTON_PRIMARY
516
                );
517
                return $this->generate_zero_state_data(
518
                    $nocoursesimg,
519
                    [$button->export_for_template($output)],
520
                    ['title' => 'zero_request_title', 'intro' => 'zero_request_intro']
521
                );
522
            }
523
 
524
            $totalcourses = $DB->count_records_select('course', 'category > 0');
525
            if (!$totalcourses && ($category = \core_course_category::get_nearest_editable_subcategory($coursecat, ['create']))) {
526
                // Add Quickstart guide and Create course buttons.
527
                $quickstarturl = $CFG->coursecreationguide;
528
                if ($quickstarturl) {
529
                    $quickstartbutton = new \single_button(
530
                        new \moodle_url($quickstarturl, ['lang' => current_language()]),
531
                        get_string('viewquickstart', 'block_myoverview'),
532
                        'get',
533
                    );
534
                    $buttons = [$quickstartbutton->export_for_template($output)];
535
                }
536
 
537
                $createbutton = new \single_button(
538
                    new \moodle_url('/course/edit.php', ['category' => $category->id]),
539
                    get_string('createcourse', 'block_myoverview'),
540
                    'post',
541
                    \single_button::BUTTON_PRIMARY
542
                );
543
                $buttons[] = $createbutton->export_for_template($output);
544
                return $this->generate_zero_state_data(
545
                    $nocoursesimg,
546
                    $buttons,
547
                    ['title' => 'zero_nocourses_title', 'intro' => 'zero_nocourses_intro']
548
                );
549
            }
550
 
551
            if ($categorytocreate = \core_course_category::get_nearest_editable_subcategory($coursecat, ['create'])) {
552
                $createbutton = new \single_button(
553
                    new \moodle_url('/course/edit.php', ['category' => $categorytocreate->id]),
554
                    get_string('createcourse', 'block_myoverview'),
555
                    'post',
556
                    \single_button::BUTTON_PRIMARY
557
                );
558
                $buttons = [$createbutton->export_for_template($output)];
559
                if ($categorytomanage = \core_course_category::get_nearest_editable_subcategory($coursecat, ['manage'])) {
560
                    // Add a Manage course button.
561
                    $managebutton = new \single_button(
562
                        new \moodle_url('/course/management.php', ['category' => $categorytomanage->id]),
563
                        get_string('managecourses')
564
                    );
565
                    $buttons[] = $managebutton->export_for_template($output);
566
                    return $this->generate_zero_state_data(
567
                        $nocoursesimg,
568
                        array_reverse($buttons),
569
                        ['title' => 'zero_default_title', 'intro' => 'zero_default_intro']
570
                    );
571
                }
572
                return $this->generate_zero_state_data(
573
                    $nocoursesimg,
574
                    $buttons,
575
                    ['title' => 'zero_default_title', 'intro' => 'zero_default_intro']
576
                );
577
            }
578
        }
579
 
580
        return $this->generate_zero_state_data(
581
            $nocoursesimg,
582
            [],
583
            ['title' => 'zero_default_title', 'intro' => 'zero_default_intro']
584
        );
585
    }
586
 
587
    /**
588
     * Generate the state zero data.
589
     *
590
     * @param \moodle_url $imageurl The URL to the image to show
591
     * @param string[] $buttons Exported {@see \single_button} instances
592
     * @param array $strings Title and intro strings for the zero state if needed.
593
     * @return array Context variables for the template
594
     */
595
    private function generate_zero_state_data(\moodle_url $imageurl, array $buttons, array $strings) {
596
        global $CFG;
597
        // Documentation data.
598
        $dochref = new \moodle_url($CFG->docroot, ['lang' => current_language()]);
599
        $quickstart = new \moodle_url($CFG->coursecreationguide, ['lang' => current_language()]);
600
        $docparams = [
601
            'quickhref' => $quickstart->out(),
602
            'quicktitle' => get_string('viewquickstart', 'block_myoverview'),
603
            'quicktarget' => '_blank',
604
            'dochref' => $dochref->out(),
605
            'doctitle' => get_string('documentation'),
606
            'doctarget' => $CFG->doctonewwindow ? '_blank' : '_self',
607
        ];
608
        return [
609
            'nocoursesimg' => $imageurl->out(),
610
            'title' => ($strings['title']) ? get_string($strings['title'], 'block_myoverview') : '',
611
            'intro' => ($strings['intro']) ? get_string($strings['intro'], 'block_myoverview', $docparams) : '',
612
            'buttons' => $buttons,
613
        ];
614
    }
615
}