2 // This file is part of Moodle - http://moodle.org/
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.
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.
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/>.
23 use mod_data\event\course_module_viewed
;
24 use mod_data\event\template_viewed
;
25 use mod_data\event\template_updated
;
30 * Class manager for database activity
33 * @copyright 2022 Ferran Recio <ferran@moodle.com>
34 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
39 const MODULE
= 'data';
41 /** Template list with their files required to save the information of a preset. */
42 const TEMPLATES_LIST
= [
43 'listtemplate' => 'listtemplate.html',
44 'singletemplate' => 'singletemplate.html',
45 'asearchtemplate' => 'asearchtemplate.html',
46 'addtemplate' => 'addtemplate.html',
47 'rsstemplate' => 'rsstemplate.html',
48 'csstemplate' => 'csstemplate.css',
49 'jstemplate' => 'jstemplate.js',
50 'listtemplateheader' => 'listtemplateheader.html',
51 'listtemplatefooter' => 'listtemplatefooter.html',
52 'rsstitletemplate' => 'rsstitletemplate.html',
55 /** @var string plugin path. */
58 /** @var stdClass course_module record. */
61 /** @var context_module the current context. */
64 /** @var cm_info course_modules record. */
67 /** @var array the current data_fields records.
68 * Do not access this attribute directly, use $this->get_field_records instead
70 private $_fieldrecords = null;
75 * @param cm_info $cm course module info object
76 * @param stdClass $instance activity instance object.
78 public function __construct(cm_info
$cm, stdClass
$instance) {
81 $this->instance
= $instance;
82 $this->context
= context_module
::instance($cm->id
);
83 $this->instance
->cmidnumber
= $cm->idnumber
;
84 $this->path
= $CFG->dirroot
. '/mod/' . self
::MODULE
;
88 * Create a manager instance from an instance record.
90 * @param stdClass $instance an activity record
93 public static function create_from_instance(stdClass
$instance): self
{
94 $cm = get_coursemodule_from_instance(self
::MODULE
, $instance->id
);
95 // Ensure that $this->cm is a cm_info object.
96 $cm = cm_info
::create($cm);
97 return new self($cm, $instance);
101 * Create a manager instance from a course_modules record.
103 * @param stdClass|cm_info $cm an activity record
106 public static function create_from_coursemodule($cm): self
{
108 // Ensure that $this->cm is a cm_info object.
109 $cm = cm_info
::create($cm);
110 $instance = $DB->get_record(self
::MODULE
, ['id' => $cm->instance
], '*', MUST_EXIST
);
111 return new self($cm, $instance);
115 * Create a manager instance from a data_record entry.
117 * @param stdClass $record the data_record record
120 public static function create_from_data_record($record): self
{
122 $instance = $DB->get_record(self
::MODULE
, ['id' => $record->dataid
], '*', MUST_EXIST
);
123 $cm = get_coursemodule_from_instance(self
::MODULE
, $instance->id
);
124 $cm = cm_info
::create($cm);
125 return new self($cm, $instance);
129 * Return the current context.
131 * @return context_module
133 public function get_context(): context_module
{
134 return $this->context
;
138 * Return the current instance.
140 * @return stdClass the instance record
142 public function get_instance(): stdClass
{
143 return $this->instance
;
147 * Return the current cm_info.
149 * @return cm_info the course module
151 public function get_coursemodule(): cm_info
{
156 * Trigger module viewed event and set the module viewed for completion.
158 * @param stdClass $course course object
160 public function set_module_viewed(stdClass
$course) {
162 require_once($CFG->libdir
. '/completionlib.php');
164 // Trigger module viewed event.
165 $event = course_module_viewed
::create([
166 'objectid' => $this->instance
->id
,
167 'context' => $this->context
,
169 $event->add_record_snapshot('course', $course);
170 $event->add_record_snapshot('course_modules', $this->cm
);
171 $event->add_record_snapshot(self
::MODULE
, $this->instance
);
175 $completion = new completion_info($course);
176 $completion->set_module_viewed($this->cm
);
180 * Trigger module template viewed event.
182 public function set_template_viewed() {
183 // Trigger an event for viewing templates.
184 $event = template_viewed
::create([
185 'context' => $this->context
,
186 'courseid' => $this->cm
->course
,
188 'dataid' => $this->instance
->id
,
191 $event->add_record_snapshot(self
::MODULE
, $this->instance
);
196 * Return if the database has fields.
198 * @return bool true if the database has fields
200 public function has_fields(): bool {
202 if ($this->_fieldrecords
=== null) {
203 return $DB->record_exists('data_fields', ['dataid' => $this->instance
->id
]);
205 return !empty($this->_fieldrecords
);
209 * Return the database fields.
211 * @return data_field_base[] the field instances.
213 public function get_fields(): array {
215 $fieldrecords = $this->get_field_records();
216 foreach ($fieldrecords as $fieldrecord) {
217 $result[$fieldrecord->id
] = $this->get_field($fieldrecord);
223 * Return the field records (the current data_fields records).
225 * @return stdClass[] an array of records
227 public function get_field_records() {
229 if ($this->_fieldrecords
=== null) {
230 $this->_fieldrecords
= $DB->get_records('data_fields', ['dataid' => $this->instance
->id
]);
232 return $this->_fieldrecords
;
236 * Return a specific field instance from a field record.
238 * @param stdClass $fieldrecord the fieldrecord to convert
239 * @return data_field_base the data field class instance
241 public function get_field(stdClass
$fieldrecord): data_field_base
{
242 $filepath = "{$this->path}/field/{$fieldrecord->type}/field.class.php";
243 $classname = "data_field_{$fieldrecord->type}";
244 if (!file_exists($filepath)) {
245 return new data_field_base($fieldrecord, $this->instance
, $this->cm
);
247 require_once($filepath);
248 if (!class_exists($classname)) {
249 return new data_field_base($fieldrecord, $this->instance
, $this->cm
);
251 $newfield = new $classname($fieldrecord, $this->instance
, $this->cm
);
256 * Return a specific template.
258 * NOTE: this method returns a default template if the module template is empty.
259 * However, it won't update the template database field.
261 * @param string $templatename
262 * @param array $options extra display options array
263 * @return template the template instance
265 public function get_template(string $templatename, array $options = []): template
{
266 if ($templatename === 'single') {
267 $templatename = 'singletemplate';
269 $instance = $this->instance
;
270 $templatecontent = $instance->{$templatename} ??
'';
271 if (empty($templatecontent)) {
272 $templatecontent = data_generate_default_template($instance, $templatename, 0, false, false);
274 // Some templates have extra options.
275 if ($templatename === 'singletemplate') {
276 $options['comments'] = true;
277 $options['ratings'] = true;
279 if ($templatename === 'listtemplate') {
280 // The "Show more" button should be only displayed in the listtemplate.
281 $options['showmore'] = true;
283 return new template($this, $templatecontent, $options);
287 * Update the database templates.
289 * @param stdClass $newtemplates an object with all the new templates
290 * @return bool if updated successfully.
292 public function update_templates(stdClass
$newtemplates): bool {
295 'id' => $this->instance
->id
,
297 foreach (self
::TEMPLATES_LIST
as $templatename => $templatefile) {
298 if (!isset($newtemplates->{$templatename})) {
301 $record->{$templatename} = $newtemplates->{$templatename};
304 // The add entry form cannot repeat tags.
305 if (isset($record->addtemplate
) && !data_tags_check($this->instance
->id
, $record->addtemplate
)) {
309 $DB->update_record(self
::MODULE
, $record);
310 $this->instance
= $DB->get_record(self
::MODULE
, ['id' => $this->cm
->instance
], '*', MUST_EXIST
);
312 // Trigger an event for saving the templates.
313 $event = template_updated
::create(array(
314 'context' => $this->context
,
315 'courseid' => $this->cm
->course
,
317 'dataid' => $this->instance
->id
,
326 * Returns an array of all the available presets.
328 * @return array A list with the datapreset plugins and the presets saved by users.
330 public function get_available_presets(): array {
331 // First load the datapreset plugins that exist within the modules preset dir.
332 $pluginpresets = static::get_available_plugin_presets();
334 // Then find the presets that people have saved.
335 $savedpresets = static::get_available_saved_presets();
337 return array_merge($pluginpresets, $savedpresets);
341 * Returns an array of all the presets that users have saved to the site.
343 * @return array A list with the preset saved by the users.
345 public function get_available_saved_presets(): array {
350 $fs = get_file_storage();
351 $files = $fs->get_area_files(DATA_PRESET_CONTEXT
, DATA_PRESET_COMPONENT
, DATA_PRESET_FILEAREA
);
355 $canviewall = has_capability('mod/data:viewalluserpresets', $this->get_context());
356 foreach ($files as $file) {
357 $isnotdirectory = ($file->is_directory() && $file->get_filepath() == '/') ||
!$file->is_directory();
358 $userid = $file->get_userid();
359 $cannotviewfile = !$canviewall && $userid != $USER->id
;
360 if ($isnotdirectory ||
$cannotviewfile) {
364 $preset = preset
::create_from_storedfile($this, $file);
365 $presets[] = $preset;
372 * Returns an array of all the available plugin presets.
374 * @return array A list with the datapreset plugins.
376 public static function get_available_plugin_presets(): array {
379 $dirs = core_component
::get_plugin_list('datapreset');
380 foreach ($dirs as $dir => $fulldir) {
381 if (preset
::is_directory_a_preset($fulldir)) {
382 $preset = preset
::create_from_plugin(null, $dir);
383 $presets[] = $preset;