mirror of
https://github.com/moodle/moodle.git
synced 2025-02-24 20:13:14 +01:00
This has been generated running the following Sniffs, all them part of the Moodle's CodeSniffer standard: - PSR12.Functions.ReturnTypeDeclaration - PSR12.Functions.NullableTypeDeclaration - moodle.Methods.MethodDeclarationSpacing - Squiz.Whitespace.ScopeKeywordSpacing All them are, exclusively, about correct spacing, so the changes are, all them, only white space changes. Only exceptions to the above are 3 changes what were setting the return type in a new line, and, when that happens, the closing parenthesis (bracket) has to go to the same line than the colon.
502 lines
16 KiB
PHP
502 lines
16 KiB
PHP
<?php
|
|
// This file is part of Moodle - http://moodle.org/
|
|
//
|
|
// Moodle is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Moodle is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
namespace mod_data;
|
|
|
|
use cm_info;
|
|
use context_module;
|
|
use completion_info;
|
|
use data_field_base;
|
|
use mod_data_renderer;
|
|
use mod_data\event\course_module_viewed;
|
|
use mod_data\event\template_viewed;
|
|
use mod_data\event\template_updated;
|
|
use moodle_page;
|
|
use core_component;
|
|
use stdClass;
|
|
|
|
/**
|
|
* Class manager for database activity
|
|
*
|
|
* @package mod_data
|
|
* @copyright 2022 Ferran Recio <ferran@moodle.com>
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
class manager {
|
|
|
|
/** Module name. */
|
|
const MODULE = 'data';
|
|
|
|
/** The plugin name. */
|
|
const PLUGINNAME = 'mod_data';
|
|
|
|
/** Template list with their files required to save the information of a preset. */
|
|
const TEMPLATES_LIST = [
|
|
'listtemplate' => 'listtemplate.html',
|
|
'singletemplate' => 'singletemplate.html',
|
|
'asearchtemplate' => 'asearchtemplate.html',
|
|
'addtemplate' => 'addtemplate.html',
|
|
'rsstemplate' => 'rsstemplate.html',
|
|
'csstemplate' => 'csstemplate.css',
|
|
'jstemplate' => 'jstemplate.js',
|
|
'listtemplateheader' => 'listtemplateheader.html',
|
|
'listtemplatefooter' => 'listtemplatefooter.html',
|
|
'rsstitletemplate' => 'rsstitletemplate.html',
|
|
];
|
|
|
|
/** @var string plugin path. */
|
|
public $path;
|
|
|
|
/** @var stdClass course_module record. */
|
|
private $instance;
|
|
|
|
/** @var context_module the current context. */
|
|
private $context;
|
|
|
|
/** @var cm_info course_modules record. */
|
|
private $cm;
|
|
|
|
/** @var array the current data_fields records.
|
|
* Do not access this attribute directly, use $this->get_field_records instead
|
|
*/
|
|
private $_fieldrecords = null;
|
|
|
|
/**
|
|
* Class constructor.
|
|
*
|
|
* @param cm_info $cm course module info object
|
|
* @param stdClass $instance activity instance object.
|
|
*/
|
|
public function __construct(cm_info $cm, stdClass $instance) {
|
|
global $CFG;
|
|
$this->cm = $cm;
|
|
$this->instance = $instance;
|
|
$this->context = context_module::instance($cm->id);
|
|
$this->instance->cmidnumber = $cm->idnumber;
|
|
$this->path = $CFG->dirroot . '/mod/' . self::MODULE;
|
|
}
|
|
|
|
/**
|
|
* Create a manager instance from an instance record.
|
|
*
|
|
* @param stdClass $instance an activity record
|
|
* @return manager
|
|
*/
|
|
public static function create_from_instance(stdClass $instance): self {
|
|
$cm = get_coursemodule_from_instance(self::MODULE, $instance->id);
|
|
// Ensure that $this->cm is a cm_info object.
|
|
$cm = cm_info::create($cm);
|
|
return new self($cm, $instance);
|
|
}
|
|
|
|
/**
|
|
* Create a manager instance from a course_modules record.
|
|
*
|
|
* @param stdClass|cm_info $cm an activity record
|
|
* @return manager
|
|
*/
|
|
public static function create_from_coursemodule($cm): self {
|
|
global $DB;
|
|
// Ensure that $this->cm is a cm_info object.
|
|
$cm = cm_info::create($cm);
|
|
$instance = $DB->get_record(self::MODULE, ['id' => $cm->instance], '*', MUST_EXIST);
|
|
return new self($cm, $instance);
|
|
}
|
|
|
|
/**
|
|
* Create a manager instance from a data_record entry.
|
|
*
|
|
* @param stdClass $record the data_record record
|
|
* @return manager
|
|
*/
|
|
public static function create_from_data_record($record): self {
|
|
global $DB;
|
|
$instance = $DB->get_record(self::MODULE, ['id' => $record->dataid], '*', MUST_EXIST);
|
|
$cm = get_coursemodule_from_instance(self::MODULE, $instance->id);
|
|
$cm = cm_info::create($cm);
|
|
return new self($cm, $instance);
|
|
}
|
|
|
|
/**
|
|
* Return the current context.
|
|
*
|
|
* @return context_module
|
|
*/
|
|
public function get_context(): context_module {
|
|
return $this->context;
|
|
}
|
|
|
|
/**
|
|
* Return the current instance.
|
|
*
|
|
* @return stdClass the instance record
|
|
*/
|
|
public function get_instance(): stdClass {
|
|
return $this->instance;
|
|
}
|
|
|
|
/**
|
|
* Return the current cm_info.
|
|
*
|
|
* @return cm_info the course module
|
|
*/
|
|
public function get_coursemodule(): cm_info {
|
|
return $this->cm;
|
|
}
|
|
|
|
/**
|
|
* Return the current module renderer.
|
|
*
|
|
* @param moodle_page|null $page the current page
|
|
* @return mod_data_renderer the module renderer
|
|
*/
|
|
public function get_renderer(?moodle_page $page = null): mod_data_renderer {
|
|
global $PAGE;
|
|
$page = $page ?? $PAGE;
|
|
return $page->get_renderer(self::PLUGINNAME);
|
|
}
|
|
|
|
/**
|
|
* Trigger module viewed event and set the module viewed for completion.
|
|
*
|
|
* @param stdClass $course course object
|
|
*/
|
|
public function set_module_viewed(stdClass $course) {
|
|
global $CFG;
|
|
require_once($CFG->libdir . '/completionlib.php');
|
|
|
|
// Trigger module viewed event.
|
|
$event = course_module_viewed::create([
|
|
'objectid' => $this->instance->id,
|
|
'context' => $this->context,
|
|
]);
|
|
$event->add_record_snapshot('course', $course);
|
|
$event->add_record_snapshot('course_modules', $this->cm);
|
|
$event->add_record_snapshot(self::MODULE, $this->instance);
|
|
$event->trigger();
|
|
|
|
// Completion.
|
|
$completion = new completion_info($course);
|
|
$completion->set_module_viewed($this->cm);
|
|
}
|
|
|
|
/**
|
|
* Trigger module template viewed event.
|
|
*/
|
|
public function set_template_viewed() {
|
|
// Trigger an event for viewing templates.
|
|
$event = template_viewed::create([
|
|
'context' => $this->context,
|
|
'courseid' => $this->cm->course,
|
|
'other' => [
|
|
'dataid' => $this->instance->id,
|
|
],
|
|
]);
|
|
$event->add_record_snapshot(self::MODULE, $this->instance);
|
|
$event->trigger();
|
|
}
|
|
|
|
/**
|
|
* Return if the database has records.
|
|
*
|
|
* @return bool true if the database has records
|
|
*/
|
|
public function has_records(): bool {
|
|
global $DB;
|
|
|
|
return $DB->record_exists('data_records', ['dataid' => $this->instance->id]);
|
|
}
|
|
|
|
/**
|
|
* Return if the database has fields.
|
|
*
|
|
* @return bool true if the database has fields
|
|
*/
|
|
public function has_fields(): bool {
|
|
global $DB;
|
|
if ($this->_fieldrecords === null) {
|
|
return $DB->record_exists('data_fields', ['dataid' => $this->instance->id]);
|
|
}
|
|
return !empty($this->_fieldrecords);
|
|
}
|
|
|
|
/**
|
|
* Return the database fields.
|
|
*
|
|
* @return data_field_base[] the field instances.
|
|
*/
|
|
public function get_fields(): array {
|
|
$result = [];
|
|
$fieldrecords = $this->get_field_records();
|
|
foreach ($fieldrecords as $fieldrecord) {
|
|
$result[$fieldrecord->id] = $this->get_field($fieldrecord);
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Return the field records (the current data_fields records).
|
|
*
|
|
* @return stdClass[] an array of records
|
|
*/
|
|
public function get_field_records() {
|
|
global $DB;
|
|
if ($this->_fieldrecords === null) {
|
|
$this->_fieldrecords = $DB->get_records('data_fields', ['dataid' => $this->instance->id], 'id');
|
|
}
|
|
return $this->_fieldrecords;
|
|
}
|
|
|
|
/**
|
|
* Return a specific field instance from a field record.
|
|
*
|
|
* @param stdClass $fieldrecord the fieldrecord to convert
|
|
* @return data_field_base the data field class instance
|
|
*/
|
|
public function get_field(stdClass $fieldrecord): data_field_base {
|
|
global $CFG; // Some old field plugins require $CFG to be in the scope.
|
|
$filepath = "{$this->path}/field/{$fieldrecord->type}/field.class.php";
|
|
$classname = "data_field_{$fieldrecord->type}";
|
|
if (!file_exists($filepath)) {
|
|
return new data_field_base($fieldrecord, $this->instance, $this->cm);
|
|
}
|
|
require_once($filepath);
|
|
if (!class_exists($classname)) {
|
|
return new data_field_base($fieldrecord, $this->instance, $this->cm);
|
|
}
|
|
$newfield = new $classname($fieldrecord, $this->instance, $this->cm);
|
|
return $newfield;
|
|
}
|
|
|
|
/**
|
|
* Return a specific template.
|
|
*
|
|
* NOTE: this method returns a default template if the module template is empty.
|
|
* However, it won't update the template database field.
|
|
*
|
|
* Some possible options:
|
|
* - search: string with the current searching text.
|
|
* - page: integer repesenting the current pagination page numbre (if any)
|
|
* - baseurl: a moodle_url object to the current page.
|
|
*
|
|
* @param string $templatename
|
|
* @param array $options extra display options array
|
|
* @return template the template instance
|
|
*/
|
|
public function get_template(string $templatename, array $options = []): template {
|
|
if ($templatename === 'single') {
|
|
$templatename = 'singletemplate';
|
|
}
|
|
$instance = $this->instance;
|
|
$templatecontent = $instance->{$templatename} ?? '';
|
|
if (empty($templatecontent)) {
|
|
$templatecontent = data_generate_default_template($instance, $templatename, 0, false, false);
|
|
}
|
|
$options['templatename'] = $templatename;
|
|
// Some templates have extra options.
|
|
$options = array_merge($options, template::get_default_display_options($templatename));
|
|
|
|
return new template($this, $templatecontent, $options);
|
|
}
|
|
|
|
/** Check if the user can manage templates on the current context.
|
|
*
|
|
* @param int $userid the user id to check ($USER->id if null).
|
|
* @return bool if the user can manage templates on current context.
|
|
*/
|
|
public function can_manage_templates(?int $userid = null): bool {
|
|
global $USER;
|
|
if (!$userid) {
|
|
$userid = $USER->id;
|
|
}
|
|
return has_capability('mod/data:managetemplates', $this->context, $userid);
|
|
}
|
|
|
|
/** Check if the user can export entries on the current context.
|
|
*
|
|
* @param int $userid the user id to check ($USER->id if null).
|
|
* @return bool if the user can export entries on current context.
|
|
*/
|
|
public function can_export_entries(?int $userid = null): bool {
|
|
global $USER, $DB;
|
|
|
|
if (!$userid) {
|
|
$userid = $USER->id;
|
|
}
|
|
|
|
// Exportallentries and exportentry are basically the same capability.
|
|
return has_capability('mod/data:exportallentries', $this->context) ||
|
|
has_capability('mod/data:exportentry', $this->context) ||
|
|
(has_capability('mod/data:exportownentry', $this->context) &&
|
|
$DB->record_exists('data_records', ['userid' => $userid, 'dataid' => $this->instance->id]));
|
|
}
|
|
|
|
/**
|
|
* Update the database templates.
|
|
*
|
|
* @param stdClass $newtemplates an object with all the new templates
|
|
* @return bool if updated successfully.
|
|
*/
|
|
public function update_templates(stdClass $newtemplates): bool {
|
|
global $DB;
|
|
$record = (object)[
|
|
'id' => $this->instance->id,
|
|
];
|
|
foreach (self::TEMPLATES_LIST as $templatename => $templatefile) {
|
|
if (!isset($newtemplates->{$templatename})) {
|
|
continue;
|
|
}
|
|
$record->{$templatename} = $newtemplates->{$templatename};
|
|
}
|
|
|
|
// The add entry form cannot repeat tags.
|
|
if (isset($record->addtemplate) && !data_tags_check($this->instance->id, $record->addtemplate)) {
|
|
return false;
|
|
}
|
|
|
|
$DB->update_record(self::MODULE, $record);
|
|
$this->instance = $DB->get_record(self::MODULE, ['id' => $this->cm->instance], '*', MUST_EXIST);
|
|
|
|
// Trigger an event for saving the templates.
|
|
$event = template_updated::create(array(
|
|
'context' => $this->context,
|
|
'courseid' => $this->cm->course,
|
|
'other' => array(
|
|
'dataid' => $this->instance->id,
|
|
)
|
|
));
|
|
$event->trigger();
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Reset all templates.
|
|
*
|
|
* @return bool if the reset is done or not
|
|
*/
|
|
public function reset_all_templates(): bool {
|
|
$newtemplates = new stdClass();
|
|
foreach (self::TEMPLATES_LIST as $templatename => $templatefile) {
|
|
$newtemplates->{$templatename} = '';
|
|
}
|
|
return $this->update_templates($newtemplates);
|
|
}
|
|
|
|
/**
|
|
* Reset all templates related to a specific template.
|
|
*
|
|
* @param string $templatename the template name
|
|
* @return bool if the reset is done or not
|
|
*/
|
|
public function reset_template(string $templatename): bool {
|
|
$newtemplates = new stdClass();
|
|
// Reset the template to default.
|
|
$newtemplates->{$templatename} = '';
|
|
if ($templatename == 'listtemplate') {
|
|
$newtemplates->listtemplateheader = '';
|
|
$newtemplates->listtemplatefooter = '';
|
|
}
|
|
if ($templatename == 'rsstemplate') {
|
|
$newtemplates->rsstitletemplate = '';
|
|
}
|
|
return $this->update_templates($newtemplates);
|
|
}
|
|
|
|
/** Check if the user can view a specific preset.
|
|
*
|
|
* @param preset $preset the preset instance.
|
|
* @param int $userid the user id to check ($USER->id if null).
|
|
* @return bool if the user can view the preset.
|
|
*/
|
|
public function can_view_preset(preset $preset, ?int $userid = null): bool {
|
|
global $USER;
|
|
if (!$userid) {
|
|
$userid = $USER->id;
|
|
}
|
|
$presetuserid = $preset->get_userid();
|
|
if ($presetuserid && $presetuserid != $userid) {
|
|
return has_capability('mod/data:viewalluserpresets', $this->context, $userid);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Returns an array of all the available presets.
|
|
*
|
|
* @return array A list with the datapreset plugins and the presets saved by users.
|
|
*/
|
|
public function get_available_presets(): array {
|
|
// First load the datapreset plugins that exist within the modules preset dir.
|
|
$pluginpresets = static::get_available_plugin_presets();
|
|
|
|
// Then find the presets that people have saved.
|
|
$savedpresets = static::get_available_saved_presets();
|
|
|
|
return array_merge($pluginpresets, $savedpresets);
|
|
}
|
|
|
|
/**
|
|
* Returns an array of all the presets that users have saved to the site.
|
|
*
|
|
* @return array A list with the preset saved by the users.
|
|
*/
|
|
public function get_available_saved_presets(): array {
|
|
global $USER;
|
|
|
|
$presets = [];
|
|
|
|
$fs = get_file_storage();
|
|
$files = $fs->get_area_files(DATA_PRESET_CONTEXT, DATA_PRESET_COMPONENT, DATA_PRESET_FILEAREA);
|
|
if (empty($files)) {
|
|
return $presets;
|
|
}
|
|
$canviewall = has_capability('mod/data:viewalluserpresets', $this->get_context());
|
|
foreach ($files as $file) {
|
|
$isnotdirectory = ($file->is_directory() && $file->get_filepath() == '/') || !$file->is_directory();
|
|
$userid = $file->get_userid();
|
|
$cannotviewfile = !$canviewall && $userid != $USER->id;
|
|
if ($isnotdirectory || $cannotviewfile) {
|
|
continue;
|
|
}
|
|
|
|
$preset = preset::create_from_storedfile($this, $file);
|
|
$presets[] = $preset;
|
|
}
|
|
|
|
return $presets;
|
|
}
|
|
|
|
/**
|
|
* Returns an array of all the available plugin presets.
|
|
*
|
|
* @return array A list with the datapreset plugins.
|
|
*/
|
|
public static function get_available_plugin_presets(): array {
|
|
$presets = [];
|
|
|
|
$dirs = core_component::get_plugin_list('datapreset');
|
|
foreach ($dirs as $dir => $fulldir) {
|
|
if (preset::is_directory_a_preset($fulldir)) {
|
|
$preset = preset::create_from_plugin(null, $dir);
|
|
$presets[] = $preset;
|
|
}
|
|
}
|
|
|
|
return $presets;
|
|
}
|
|
}
|