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
declare(strict_types=1);
18
 
19
namespace core_reportbuilder\local\entities;
20
 
21
use context_course;
22
use context_helper;
23
use core_reportbuilder\local\filters\boolean_select;
24
use core_reportbuilder\local\filters\course_selector;
25
use core_reportbuilder\local\filters\date;
26
use core_reportbuilder\local\filters\select;
27
use core_reportbuilder\local\filters\text;
28
use core_reportbuilder\local\helpers\custom_fields;
29
use core_reportbuilder\local\helpers\format;
30
use core_reportbuilder\local\report\column;
31
use core_reportbuilder\local\report\filter;
32
use html_writer;
33
use lang_string;
34
use stdClass;
35
use theme_config;
36
 
37
defined('MOODLE_INTERNAL') || die();
38
 
39
global $CFG;
40
require_once($CFG->dirroot . '/course/lib.php');
41
 
42
/**
43
 * Course entity class implementation
44
 *
45
 * This entity defines all the course columns and filters to be used in any report.
46
 *
47
 * @package     core_reportbuilder
48
 * @copyright   2021 Sara Arjona <sara@moodle.com> based on Marina Glancy code.
49
 * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
50
 */
51
class course extends base {
52
 
53
    /**
54
     * Database tables that this entity uses
55
     *
56
     * @return string[]
57
     */
58
    protected function get_default_tables(): array {
59
        return [
60
            'course',
61
            'context',
62
            'tag_instance',
63
            'tag',
64
        ];
65
    }
66
 
67
    /**
68
     * The default title for this entity in the list of columns/filters in the report builder.
69
     *
70
     * @return lang_string
71
     */
72
    protected function get_default_entity_title(): lang_string {
73
        return new lang_string('entitycourse', 'core_reportbuilder');
74
    }
75
 
76
    /**
77
     * Get custom fields helper
78
     *
79
     * @return custom_fields
80
     */
81
    protected function get_custom_fields(): custom_fields {
82
        $customfields = new custom_fields($this->get_table_alias('course') . '.id', $this->get_entity_name(),
83
            'core_course', 'course');
84
        $customfields->add_joins($this->get_joins());
85
        return $customfields;
86
    }
87
 
88
    /**
89
     * Initialise the entity, adding all course and custom course fields
90
     *
91
     * @return base
92
     */
93
    public function initialise(): base {
94
        $customfields = $this->get_custom_fields();
95
 
96
        $columns = array_merge($this->get_all_columns(), $customfields->get_columns());
97
        foreach ($columns as $column) {
98
            $this->add_column($column);
99
        }
100
 
101
        $filters = array_merge($this->get_all_filters(), $customfields->get_filters());
102
        foreach ($filters as $filter) {
103
            $this
104
                ->add_condition($filter)
105
                ->add_filter($filter);
106
        }
107
 
108
        return $this;
109
    }
110
 
111
    /**
112
     * Return syntax for joining on the context table
113
     *
114
     * @return string
115
     */
116
    public function get_context_join(): string {
117
        $coursealias = $this->get_table_alias('course');
118
        $contextalias = $this->get_table_alias('context');
119
 
120
        return "LEFT JOIN {context} {$contextalias}
121
            ON {$contextalias}.contextlevel = " . CONTEXT_COURSE . "
122
           AND {$contextalias}.instanceid = {$coursealias}.id";
123
    }
124
 
125
    /**
126
     * Course fields.
127
     *
128
     * @return array
129
     */
130
    protected function get_course_fields(): array {
131
        return [
132
            'fullname' => new lang_string('fullnamecourse'),
133
            'shortname' => new lang_string('shortnamecourse'),
134
            'idnumber' => new lang_string('idnumbercourse'),
135
            'summary' => new lang_string('coursesummary'),
136
            'format' => new lang_string('format'),
137
            'startdate' => new lang_string('startdate'),
138
            'enddate' => new lang_string('enddate'),
139
            'visible' => new lang_string('coursevisibility'),
140
            'groupmode' => new lang_string('groupmode', 'group'),
141
            'groupmodeforce' => new lang_string('groupmodeforce', 'group'),
142
            'lang' => new lang_string('forcelanguage'),
143
            'calendartype' => new lang_string('forcecalendartype', 'calendar'),
144
            'theme' => new lang_string('theme'),
145
            'enablecompletion' => new lang_string('enablecompletion', 'completion'),
146
            'downloadcontent' => new lang_string('downloadcoursecontent', 'course'),
147
            'timecreated' => new lang_string('timecreated', 'core_reportbuilder'),
148
            'timemodified' => new lang_string('timemodified', 'core_reportbuilder'),
149
        ];
150
    }
151
 
152
    /**
153
     * Check if this field is sortable
154
     *
155
     * @param string $fieldname
156
     * @return bool
157
     */
158
    protected function is_sortable(string $fieldname): bool {
159
        // Some columns can't be sorted, like longtext or images.
160
        $nonsortable = [
161
            'summary',
162
        ];
163
 
164
        return !in_array($fieldname, $nonsortable);
165
    }
166
 
167
    /**
168
     * Return appropriate column type for given user field
169
     *
170
     * @param string $coursefield
171
     * @return int
172
     */
173
    protected function get_course_field_type(string $coursefield): int {
174
        switch ($coursefield) {
175
            case 'downloadcontent':
176
            case 'enablecompletion':
177
            case 'groupmodeforce':
178
            case 'visible':
179
                $fieldtype = column::TYPE_BOOLEAN;
180
                break;
181
            case 'startdate':
182
            case 'enddate':
183
            case 'timecreated':
184
            case 'timemodified':
185
                $fieldtype = column::TYPE_TIMESTAMP;
186
                break;
187
            case 'summary':
188
                $fieldtype = column::TYPE_LONGTEXT;
189
                break;
190
            case 'groupmode':
191
                $fieldtype = column::TYPE_INTEGER;
192
                break;
193
            case 'calendartype':
194
            case 'idnumber':
195
            case 'format':
196
            case 'fullname':
197
            case 'lang':
198
            case 'shortname':
199
            case 'theme':
200
            default:
201
                $fieldtype = column::TYPE_TEXT;
202
                break;
203
        }
204
 
205
        return $fieldtype;
206
    }
207
 
208
    /**
209
     * Return joins necessary for retrieving tags
210
     *
211
     * @return string[]
212
     */
213
    public function get_tag_joins(): array {
214
        return $this->get_tag_joins_for_entity('core', 'course', $this->get_table_alias('course') . '.id');
215
    }
216
 
217
    /**
218
     * Returns list of all available columns.
219
     *
220
     * These are all the columns available to use in any report that uses this entity.
221
     *
222
     * @return column[]
223
     */
224
    protected function get_all_columns(): array {
225
        global $DB;
226
 
227
        $coursefields = $this->get_course_fields();
228
        $tablealias = $this->get_table_alias('course');
229
        $contexttablealias = $this->get_table_alias('context');
230
 
231
        // Columns course full name with link, course short name with link and course id with link.
232
        $fields = [
233
            'coursefullnamewithlink' => 'fullname',
234
            'courseshortnamewithlink' => 'shortname',
235
            'courseidnumberewithlink' => 'idnumber',
236
        ];
237
        foreach ($fields as $key => $field) {
238
            $column = (new column(
239
                $key,
240
                new lang_string($key, 'core_reportbuilder'),
241
                $this->get_entity_name()
242
            ))
243
                ->add_joins($this->get_joins())
244
                ->set_type(column::TYPE_TEXT)
245
                ->add_fields("{$tablealias}.{$field} as $key, {$tablealias}.id")
246
                ->set_is_sortable(true)
247
                ->add_callback(static function(?string $value, stdClass $row): string {
248
                    if ($value === null) {
249
                        return '';
250
                    }
251
 
252
                    context_helper::preload_from_record($row);
253
 
254
                    return html_writer::link(course_get_url($row->id),
255
                        format_string($value, true, ['context' => context_course::instance($row->id)]));
256
                });
257
 
258
            // Join on the context table so that we can use it for formatting these columns later.
259
            if ($key === 'coursefullnamewithlink') {
260
                $column->add_join($this->get_context_join())
261
                    ->add_fields(context_helper::get_preload_record_columns_sql($contexttablealias));
262
            }
263
 
264
            $columns[] = $column;
265
        }
266
 
267
        foreach ($coursefields as $coursefield => $coursefieldlang) {
268
            $columntype = $this->get_course_field_type($coursefield);
269
 
270
            $columnfieldsql = "{$tablealias}.{$coursefield}";
271
            if ($columntype === column::TYPE_LONGTEXT && $DB->get_dbfamily() === 'oracle') {
272
                $columnfieldsql = $DB->sql_order_by_text($columnfieldsql, 1024);
273
            }
274
 
275
            $column = (new column(
276
                $coursefield,
277
                $coursefieldlang,
278
                $this->get_entity_name()
279
            ))
280
                ->add_joins($this->get_joins())
281
                ->set_type($columntype)
282
                ->add_field($columnfieldsql, $coursefield)
283
                ->add_callback([$this, 'format'], $coursefield)
284
                ->set_is_sortable($this->is_sortable($coursefield));
285
 
286
            // Join on the context table so that we can use it for formatting these columns later.
287
            if ($coursefield === 'summary' || $coursefield === 'shortname' || $coursefield === 'fullname') {
288
                $column->add_join($this->get_context_join())
289
                    ->add_field("{$tablealias}.id", 'courseid')
290
                    ->add_fields(context_helper::get_preload_record_columns_sql($contexttablealias));
291
            }
292
 
293
            $columns[] = $column;
294
        }
295
 
296
        return $columns;
297
    }
298
 
299
    /**
300
     * Returns list of all available filters
301
     *
302
     * @return array
303
     */
304
    protected function get_all_filters(): array {
305
        global $DB;
306
 
307
        $filters = [];
308
        $tablealias = $this->get_table_alias('course');
309
 
310
        $fields = $this->get_course_fields();
311
        foreach ($fields as $field => $name) {
312
            $filterfieldsql = "{$tablealias}.{$field}";
313
            if ($this->get_course_field_type($field) === column::TYPE_LONGTEXT) {
314
                $filterfieldsql = $DB->sql_cast_to_char($filterfieldsql);
315
            }
316
 
317
            $optionscallback = [static::class, 'get_options_for_' . $field];
318
            if (is_callable($optionscallback)) {
319
                $filterclass = select::class;
320
            } else if ($this->get_course_field_type($field) === column::TYPE_BOOLEAN) {
321
                $filterclass = boolean_select::class;
322
            } else if ($this->get_course_field_type($field) === column::TYPE_TIMESTAMP) {
323
                $filterclass = date::class;
324
            } else {
325
                $filterclass = text::class;
326
            }
327
 
328
            $filter = (new filter(
329
                $filterclass,
330
                $field,
331
                $name,
332
                $this->get_entity_name(),
333
                $filterfieldsql
334
            ))
335
                ->add_joins($this->get_joins());
336
 
337
            // Populate filter options by callback, if available.
338
            if (is_callable($optionscallback)) {
339
                $filter->set_options_callback($optionscallback);
340
            }
341
 
342
            $filters[] = $filter;
343
        }
344
 
345
        // We add our own custom course selector filter.
346
        $filters[] = (new filter(
347
            course_selector::class,
348
            'courseselector',
349
            new lang_string('courseselect', 'core_reportbuilder'),
350
            $this->get_entity_name(),
351
            "{$tablealias}.id"
352
        ))
353
            ->add_joins($this->get_joins());
354
 
355
        return $filters;
356
    }
357
 
358
    /**
359
     * Gets list of options if the filter supports it
360
     *
361
     * @param string $fieldname
362
     * @return null|array
363
     */
364
    protected function get_options_for(string $fieldname): ?array {
365
        static $cached = [];
366
        if (!array_key_exists($fieldname, $cached)) {
367
            $callable = [static::class, 'get_options_for_' . $fieldname];
368
            if (is_callable($callable)) {
369
                $cached[$fieldname] = $callable();
370
            } else {
371
                $cached[$fieldname] = null;
372
            }
373
        }
374
        return $cached[$fieldname];
375
    }
376
 
377
    /**
378
     * List of options for the field groupmode.
379
     *
380
     * @return array
381
     */
382
    public static function get_options_for_groupmode(): array {
383
        return [
384
            NOGROUPS => get_string('groupsnone', 'group'),
385
            SEPARATEGROUPS => get_string('groupsseparate', 'group'),
386
            VISIBLEGROUPS => get_string('groupsvisible', 'group'),
387
        ];
388
    }
389
 
390
    /**
391
     * List of options for the field format.
392
     *
393
     * @return array
394
     */
395
    public static function get_options_for_format(): array {
396
        global $CFG;
397
        require_once($CFG->dirroot.'/course/lib.php');
398
 
399
        $options = [];
400
 
401
        $courseformats = get_sorted_course_formats(true);
402
        foreach ($courseformats as $courseformat) {
403
            $options[$courseformat] = get_string('pluginname', "format_{$courseformat}");
404
        }
405
 
406
        return $options;
407
    }
408
 
409
    /**
410
     * List of options for the field theme.
411
     *
412
     * @return array
413
     */
414
    public static function get_options_for_theme(): array {
415
        return array_map(
416
            fn(theme_config $theme) => $theme->get_theme_name(),
417
            get_list_of_themes(),
418
        );
419
    }
420
 
421
    /**
422
     * List of options for the field lang.
423
     *
424
     * @return array
425
     */
426
    public static function get_options_for_lang(): array {
427
        return get_string_manager()->get_list_of_translations();
428
    }
429
 
430
    /**
431
     * List of options for the field.
432
     *
433
     * @return array
434
     */
435
    public static function get_options_for_calendartype(): array {
436
        return \core_calendar\type_factory::get_list_of_calendar_types();
437
    }
438
 
439
    /**
440
     * Formats the course field for display.
441
     *
442
     * @param mixed $value Current field value.
443
     * @param stdClass $row Complete row.
444
     * @param string $fieldname Name of the field to format.
445
     * @return string
446
     */
447
    public function format($value, stdClass $row, string $fieldname): string {
448
        if ($this->get_course_field_type($fieldname) === column::TYPE_TIMESTAMP) {
449
            return format::userdate($value, $row);
450
        }
451
 
452
        if ($this->get_course_field_type($fieldname) === column::TYPE_BOOLEAN) {
453
            return format::boolean_as_text($value);
454
        }
455
 
456
        // If the column has corresponding filter, determine the value from its options.
457
        $options = $this->get_options_for($fieldname);
458
        if ($options !== null && array_key_exists($value, $options)) {
459
            return $options[$value];
460
        }
461
 
462
        if (in_array($fieldname, ['fullname', 'shortname'])) {
463
            if (!$row->courseid) {
464
                return '';
465
            }
466
            context_helper::preload_from_record($row);
467
            $context = context_course::instance($row->courseid);
468
            return format_string($value, true, ['context' => $context->id, 'escape' => false]);
469
        }
470
 
471
        if (in_array($fieldname, ['summary'])) {
472
            if (!$row->courseid) {
473
                return '';
474
            }
475
            context_helper::preload_from_record($row);
476
            $context = context_course::instance($row->courseid);
477
            $summary = file_rewrite_pluginfile_urls($row->summary, 'pluginfile.php', $context->id, 'course', 'summary', null);
478
            return format_text($summary);
479
        }
480
 
481
        return s($value);
482
    }
483
}