MDL-75275 mod_data: Add ##actionsmenu## action tag
[moodle.git] / mod / data / classes / manager.php
blobff848719b7eafdb4b200b586829ff12a452aa531
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.
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/>.
17 namespace mod_data;
19 use cm_info;
20 use context_module;
21 use completion_info;
22 use data_field_base;
23 use mod_data\event\course_module_viewed;
24 use mod_data\event\template_viewed;
25 use mod_data\event\template_updated;
26 use core_component;
27 use stdClass;
29 /**
30 * Class manager for database activity
32 * @package mod_data
33 * @copyright 2022 Ferran Recio <ferran@moodle.com>
34 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
36 class manager {
38 /** Module name. */
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. */
56 private $path;
58 /** @var stdClass course_module record. */
59 private $instance;
61 /** @var context_module the current context. */
62 private $context;
64 /** @var cm_info course_modules record. */
65 private $cm;
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;
72 /**
73 * Class constructor.
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) {
79 global $CFG;
80 $this->cm = $cm;
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;
87 /**
88 * Create a manager instance from an instance record.
90 * @param stdClass $instance an activity record
91 * @return manager
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
104 * @return manager
106 public static function create_from_coursemodule($cm): self {
107 global $DB;
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
118 * @return manager
120 public static function create_from_data_record($record): self {
121 global $DB;
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 {
152 return $this->cm;
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) {
161 global $CFG;
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);
172 $event->trigger();
174 // Completion.
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,
187 'other' => [
188 'dataid' => $this->instance->id,
191 $event->add_record_snapshot(self::MODULE, $this->instance);
192 $event->trigger();
196 * Return if the database has fields.
198 * @return bool true if the database has fields
200 public function has_fields(): bool {
201 global $DB;
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 {
214 $result = [];
215 $fieldrecords = $this->get_field_records();
216 foreach ($fieldrecords as $fieldrecord) {
217 $result[$fieldrecord->id] = $this->get_field($fieldrecord);
219 return $result;
223 * Return the field records (the current data_fields records).
225 * @return stdClass[] an array of records
227 public function get_field_records() {
228 global $DB;
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);
252 return $newfield;
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 {
293 global $DB;
294 $record = (object)[
295 'id' => $this->instance->id,
297 foreach (self::TEMPLATES_LIST as $templatename => $templatefile) {
298 if (!isset($newtemplates->{$templatename})) {
299 continue;
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)) {
306 return false;
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,
316 'other' => array(
317 'dataid' => $this->instance->id,
320 $event->trigger();
322 return true;
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 {
346 global $USER;
348 $presets = [];
350 $fs = get_file_storage();
351 $files = $fs->get_area_files(DATA_PRESET_CONTEXT, DATA_PRESET_COMPONENT, DATA_PRESET_FILEAREA);
352 if (empty($files)) {
353 return $presets;
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) {
361 continue;
364 $preset = preset::create_from_storedfile($this, $file);
365 $presets[] = $preset;
368 return $presets;
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 {
377 $presets = [];
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;
387 return $presets;