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
namespace mod_data;
18
 
19
use cm_info;
20
use context_module;
21
use completion_info;
22
use data_field_base;
23
use mod_data_renderer;
24
use mod_data\event\course_module_viewed;
25
use mod_data\event\template_viewed;
26
use mod_data\event\template_updated;
27
use moodle_page;
28
use core_component;
29
use stdClass;
30
 
31
/**
32
 * Class manager for database activity
33
 *
34
 * @package    mod_data
35
 * @copyright  2022 Ferran Recio <ferran@moodle.com>
36
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
37
 */
38
class manager {
39
 
40
    /** Module name. */
41
    const MODULE = 'data';
42
 
43
    /** The plugin name. */
44
    const PLUGINNAME = 'mod_data';
45
 
46
    /** Template list with their files required to save the information of a preset. */
47
    const TEMPLATES_LIST = [
48
        'listtemplate' => 'listtemplate.html',
49
        'singletemplate' => 'singletemplate.html',
50
        'asearchtemplate' => 'asearchtemplate.html',
51
        'addtemplate' => 'addtemplate.html',
52
        'rsstemplate' => 'rsstemplate.html',
53
        'csstemplate' => 'csstemplate.css',
54
        'jstemplate' => 'jstemplate.js',
55
        'listtemplateheader' => 'listtemplateheader.html',
56
        'listtemplatefooter' => 'listtemplatefooter.html',
57
        'rsstitletemplate' => 'rsstitletemplate.html',
58
    ];
59
 
60
    /** @var string plugin path. */
61
    public $path;
62
 
63
    /** @var stdClass course_module record. */
64
    private $instance;
65
 
66
    /** @var context_module the current context. */
67
    private $context;
68
 
69
    /** @var cm_info course_modules record. */
70
    private $cm;
71
 
72
    /** @var array the current data_fields records.
73
     * Do not access this attribute directly, use $this->get_field_records instead
74
     */
75
    private $_fieldrecords = null;
76
 
77
    /**
78
     * Class constructor.
79
     *
80
     * @param cm_info $cm course module info object
81
     * @param stdClass $instance activity instance object.
82
     */
83
    public function __construct(cm_info $cm, stdClass $instance) {
84
        global $CFG;
85
        $this->cm = $cm;
86
        $this->instance = $instance;
87
        $this->context = context_module::instance($cm->id);
88
        $this->instance->cmidnumber = $cm->idnumber;
89
        $this->path = $CFG->dirroot . '/mod/' . self::MODULE;
90
    }
91
 
92
    /**
93
     * Create a manager instance from an instance record.
94
     *
95
     * @param stdClass $instance an activity record
96
     * @return manager
97
     */
98
    public static function create_from_instance(stdClass $instance): self {
99
        $cm = get_coursemodule_from_instance(self::MODULE, $instance->id);
100
        // Ensure that $this->cm is a cm_info object.
101
        $cm = cm_info::create($cm);
102
        return new self($cm, $instance);
103
    }
104
 
105
    /**
106
     * Create a manager instance from a course_modules record.
107
     *
108
     * @param stdClass|cm_info $cm an activity record
109
     * @return manager
110
     */
111
    public static function create_from_coursemodule($cm): self {
112
        global $DB;
113
        // Ensure that $this->cm is a cm_info object.
114
        $cm = cm_info::create($cm);
115
        $instance = $DB->get_record(self::MODULE, ['id' => $cm->instance], '*', MUST_EXIST);
116
        return new self($cm, $instance);
117
    }
118
 
119
    /**
120
     * Create a manager instance from a data_record entry.
121
     *
122
     * @param stdClass $record the data_record record
123
     * @return manager
124
     */
125
    public static function create_from_data_record($record): self {
126
        global $DB;
127
        $instance = $DB->get_record(self::MODULE, ['id' => $record->dataid], '*', MUST_EXIST);
128
        $cm = get_coursemodule_from_instance(self::MODULE, $instance->id);
129
        $cm = cm_info::create($cm);
130
        return new self($cm, $instance);
131
    }
132
 
133
    /**
134
     * Return the current context.
135
     *
136
     * @return context_module
137
     */
138
    public function get_context(): context_module {
139
        return $this->context;
140
    }
141
 
142
    /**
143
     * Return the current instance.
144
     *
145
     * @return stdClass the instance record
146
     */
147
    public function get_instance(): stdClass {
148
        return $this->instance;
149
    }
150
 
151
    /**
152
     * Return the current cm_info.
153
     *
154
     * @return cm_info the course module
155
     */
156
    public function get_coursemodule(): cm_info {
157
        return $this->cm;
158
    }
159
 
160
    /**
161
     * Return the current module renderer.
162
     *
163
     * @param moodle_page|null $page the current page
164
     * @return mod_data_renderer the module renderer
165
     */
166
    public function get_renderer(?moodle_page $page = null): mod_data_renderer {
167
        global $PAGE;
168
        $page = $page ?? $PAGE;
169
        return $page->get_renderer(self::PLUGINNAME);
170
    }
171
 
172
    /**
173
     * Trigger module viewed event and set the module viewed for completion.
174
     *
175
     * @param stdClass $course course object
176
     */
177
    public function set_module_viewed(stdClass $course) {
178
        global $CFG;
179
        require_once($CFG->libdir . '/completionlib.php');
180
 
181
        // Trigger module viewed event.
182
        $event = course_module_viewed::create([
183
            'objectid' => $this->instance->id,
184
            'context' => $this->context,
185
        ]);
186
        $event->add_record_snapshot('course', $course);
187
        $event->add_record_snapshot('course_modules', $this->cm);
188
        $event->add_record_snapshot(self::MODULE, $this->instance);
189
        $event->trigger();
190
 
191
        // Completion.
192
        $completion = new completion_info($course);
193
        $completion->set_module_viewed($this->cm);
194
    }
195
 
196
    /**
197
     * Trigger module template viewed event.
198
     */
199
    public function set_template_viewed() {
200
        // Trigger an event for viewing templates.
201
        $event = template_viewed::create([
202
            'context' => $this->context,
203
            'courseid' => $this->cm->course,
204
            'other' => [
205
                'dataid' => $this->instance->id,
206
            ],
207
        ]);
208
        $event->add_record_snapshot(self::MODULE, $this->instance);
209
        $event->trigger();
210
    }
211
 
212
    /**
213
     * Return if the database has records.
214
     *
215
     * @return bool true if the database has records
216
     */
217
    public function has_records(): bool {
218
        global $DB;
219
 
220
        return $DB->record_exists('data_records', ['dataid' => $this->instance->id]);
221
    }
222
 
223
    /**
224
     * Return if the database has fields.
225
     *
226
     * @return bool true if the database has fields
227
     */
228
    public function has_fields(): bool {
229
        global $DB;
230
        if ($this->_fieldrecords === null) {
231
            return $DB->record_exists('data_fields', ['dataid' => $this->instance->id]);
232
        }
233
        return !empty($this->_fieldrecords);
234
    }
235
 
236
    /**
237
     * Return the database fields.
238
     *
239
     * @return data_field_base[] the field instances.
240
     */
241
    public function get_fields(): array {
242
        $result = [];
243
        $fieldrecords = $this->get_field_records();
244
        foreach ($fieldrecords as $fieldrecord) {
245
            $result[$fieldrecord->id] = $this->get_field($fieldrecord);
246
        }
247
        return $result;
248
    }
249
 
250
    /**
251
     * Return the field records (the current data_fields records).
252
     *
253
     * @return stdClass[] an array of records
254
     */
255
    public function get_field_records() {
256
        global $DB;
257
        if ($this->_fieldrecords === null) {
258
            $this->_fieldrecords = $DB->get_records('data_fields', ['dataid' => $this->instance->id], 'id');
259
        }
260
        return $this->_fieldrecords;
261
    }
262
 
263
    /**
264
     * Return a specific field instance from a field record.
265
     *
266
     * @param stdClass $fieldrecord the fieldrecord to convert
267
     * @return data_field_base the data field class instance
268
     */
269
    public function get_field(stdClass $fieldrecord): data_field_base {
270
        global $CFG; // Some old field plugins require $CFG to be in the  scope.
271
        $filepath = "{$this->path}/field/{$fieldrecord->type}/field.class.php";
272
        $classname = "data_field_{$fieldrecord->type}";
273
        if (!file_exists($filepath)) {
274
            return new data_field_base($fieldrecord, $this->instance, $this->cm);
275
        }
276
        require_once($filepath);
277
        if (!class_exists($classname)) {
278
            return new data_field_base($fieldrecord, $this->instance, $this->cm);
279
        }
280
        $newfield = new $classname($fieldrecord, $this->instance, $this->cm);
281
        return $newfield;
282
    }
283
 
284
    /**
285
     * Return a specific template.
286
     *
287
     * NOTE: this method returns a default template if the module template is empty.
288
     * However, it won't update the template database field.
289
     *
290
     * Some possible options:
291
     * - search: string with the current searching text.
292
     * - page: integer repesenting the current pagination page numbre (if any)
293
     * - baseurl: a moodle_url object to the current page.
294
     *
295
     * @param string $templatename
296
     * @param array $options extra display options array
297
     * @return template the template instance
298
     */
299
    public function get_template(string $templatename, array $options = []): template {
300
        if ($templatename === 'single') {
301
            $templatename = 'singletemplate';
302
        }
303
        $instance = $this->instance;
304
        $templatecontent = $instance->{$templatename} ?? '';
305
        if (empty($templatecontent)) {
306
            $templatecontent = data_generate_default_template($instance, $templatename, 0, false, false);
307
        }
308
        $options['templatename'] = $templatename;
309
        // Some templates have extra options.
310
        $options = array_merge($options, template::get_default_display_options($templatename));
311
 
312
        return new template($this, $templatecontent, $options);
313
    }
314
 
315
    /** Check if the user can manage templates on the current context.
316
     *
317
     * @param int $userid the user id to check ($USER->id if null).
318
     * @return bool if the user can manage templates on current context.
319
     */
320
    public function can_manage_templates(?int $userid = null): bool {
321
        global $USER;
322
        if (!$userid) {
323
            $userid = $USER->id;
324
        }
325
        return has_capability('mod/data:managetemplates', $this->context, $userid);
326
    }
327
 
328
    /** Check if the user can export entries on the current context.
329
     *
330
     * @param int $userid the user id to check ($USER->id if null).
331
     * @return bool if the user can export entries on current context.
332
     */
333
    public function can_export_entries(?int $userid = null): bool {
334
        global $USER, $DB;
335
 
336
        if (!$userid) {
337
            $userid = $USER->id;
338
        }
339
 
340
        // Exportallentries and exportentry are basically the same capability.
341
        return has_capability('mod/data:exportallentries', $this->context) ||
342
                has_capability('mod/data:exportentry', $this->context) ||
343
                (has_capability('mod/data:exportownentry', $this->context) &&
344
                $DB->record_exists('data_records', ['userid' => $userid, 'dataid' => $this->instance->id]));
345
    }
346
 
347
    /**
348
     * Update the database templates.
349
     *
350
     * @param stdClass $newtemplates an object with all the new templates
351
     * @return bool if updated successfully.
352
     */
353
    public function update_templates(stdClass $newtemplates): bool {
354
        global $DB;
355
        $record = (object)[
356
            'id' => $this->instance->id,
357
        ];
358
        foreach (self::TEMPLATES_LIST as $templatename => $templatefile) {
359
            if (!isset($newtemplates->{$templatename})) {
360
                continue;
361
            }
362
            $record->{$templatename} = $newtemplates->{$templatename};
363
        }
364
 
365
        // The add entry form cannot repeat tags.
366
        if (isset($record->addtemplate) && !data_tags_check($this->instance->id, $record->addtemplate)) {
367
                return false;
368
        }
369
 
370
        $DB->update_record(self::MODULE, $record);
371
        $this->instance = $DB->get_record(self::MODULE, ['id' => $this->cm->instance], '*', MUST_EXIST);
372
 
373
        // Trigger an event for saving the templates.
374
        $event = template_updated::create(array(
375
            'context' => $this->context,
376
            'courseid' => $this->cm->course,
377
            'other' => array(
378
                'dataid' => $this->instance->id,
379
            )
380
        ));
381
        $event->trigger();
382
 
383
        return true;
384
    }
385
 
386
    /**
387
     * Reset all templates.
388
     *
389
     * @return bool if the reset is done or not
390
     */
391
    public function reset_all_templates(): bool {
392
        $newtemplates = new stdClass();
393
        foreach (self::TEMPLATES_LIST as $templatename => $templatefile) {
394
            $newtemplates->{$templatename} = '';
395
        }
396
        return $this->update_templates($newtemplates);
397
    }
398
 
399
    /**
400
     * Reset all templates related to a specific template.
401
     *
402
     * @param string $templatename the template name
403
     * @return bool if the reset is done or not
404
     */
405
    public function reset_template(string $templatename): bool {
406
        $newtemplates = new stdClass();
407
        // Reset the template to default.
408
        $newtemplates->{$templatename} = '';
409
        if ($templatename == 'listtemplate') {
410
            $newtemplates->listtemplateheader = '';
411
            $newtemplates->listtemplatefooter = '';
412
        }
413
        if ($templatename == 'rsstemplate') {
414
            $newtemplates->rsstitletemplate = '';
415
        }
416
        return $this->update_templates($newtemplates);
417
    }
418
 
419
    /** Check if the user can view a specific preset.
420
     *
421
     * @param preset $preset the preset instance.
422
     * @param int $userid the user id to check ($USER->id if null).
423
     * @return bool if the user can view the preset.
424
     */
425
    public function can_view_preset(preset $preset, ?int $userid = null): bool {
426
        global $USER;
427
        if (!$userid) {
428
            $userid = $USER->id;
429
        }
430
        $presetuserid = $preset->get_userid();
431
        if ($presetuserid && $presetuserid != $userid) {
432
            return has_capability('mod/data:viewalluserpresets', $this->context, $userid);
433
        }
434
        return true;
435
    }
436
 
437
    /**
438
     * Returns an array of all the available presets.
439
     *
440
     * @return array A list with the datapreset plugins and the presets saved by users.
441
     */
442
    public function get_available_presets(): array {
443
        // First load the datapreset plugins that exist within the modules preset dir.
444
        $pluginpresets = static::get_available_plugin_presets();
445
 
446
        // Then find the presets that people have saved.
447
        $savedpresets = static::get_available_saved_presets();
448
 
449
        return array_merge($pluginpresets, $savedpresets);
450
    }
451
 
452
    /**
453
     * Returns an array of all the presets that users have saved to the site.
454
     *
455
     * @return array A list with the preset saved by the users.
456
     */
457
    public function get_available_saved_presets(): array {
458
        global $USER;
459
 
460
        $presets = [];
461
 
462
        $fs = get_file_storage();
463
        $files = $fs->get_area_files(DATA_PRESET_CONTEXT, DATA_PRESET_COMPONENT, DATA_PRESET_FILEAREA);
464
        if (empty($files)) {
465
            return $presets;
466
        }
467
        $canviewall = has_capability('mod/data:viewalluserpresets', $this->get_context());
468
        foreach ($files as $file) {
469
            $isnotdirectory = ($file->is_directory() && $file->get_filepath() == '/') || !$file->is_directory();
470
            $userid = $file->get_userid();
471
            $cannotviewfile = !$canviewall && $userid != $USER->id;
472
            if ($isnotdirectory || $cannotviewfile) {
473
                continue;
474
            }
475
 
476
            $preset = preset::create_from_storedfile($this, $file);
477
            $presets[] = $preset;
478
        }
479
 
480
        return $presets;
481
    }
482
 
483
    /**
484
     * Returns an array of all the available plugin presets.
485
     *
486
     * @return array A list with the datapreset plugins.
487
     */
488
    public static function get_available_plugin_presets(): array {
489
        $presets = [];
490
 
491
        $dirs = core_component::get_plugin_list('datapreset');
492
        foreach ($dirs as $dir => $fulldir) {
493
            if (preset::is_directory_a_preset($fulldir)) {
494
                $preset = preset::create_from_plugin(null, $dir);
495
                $presets[] = $preset;
496
            }
497
        }
498
 
499
        return $presets;
500
    }
501
}