first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-09-30 18:11:26 -04:00
commit e592ca6823
27270 changed files with 5002257 additions and 0 deletions
@@ -0,0 +1,46 @@
<?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/>.
/**
* Activity base class.
*
* @package mod_data
* @copyright 2017 onwards Ankit Agarwal <ankit.agrr@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\analytics\indicator;
defined('MOODLE_INTERNAL') || die();
/**
* Activity base class.
*
* @package mod_data
* @copyright 2017 onwards Ankit Agarwal <ankit.agrr@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class activity_base extends \core_analytics\local\indicator\community_of_inquiry_activity {
/**
* Returns the name of the field that controls activity availability.
*
* @return null|string
*/
protected function get_timeclose_field() {
return 'timeavailableto';
}
}
@@ -0,0 +1,56 @@
<?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/>.
/**
* Cognitive depth indicator - data.
*
* @package mod_data
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\analytics\indicator;
defined('MOODLE_INTERNAL') || die();
/**
* Cognitive depth indicator - data.
*
* @package mod_data
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class cognitive_depth extends activity_base {
/**
* Returns the name.
*
* If there is a corresponding '_help' string this will be shown as well.
*
* @return \lang_string
*/
public static function get_name(): \lang_string {
return new \lang_string('indicator:cognitivedepth', 'mod_data');
}
public function get_indicator_type() {
return self::INDICATOR_COGNITIVE;
}
public function get_cognitive_depth_level(\cm_info $cm) {
return self::COGNITIVE_LEVEL_2;
}
}
@@ -0,0 +1,56 @@
<?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/>.
/**
* Social breadth indicator - data.
*
* @package mod_data
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\analytics\indicator;
defined('MOODLE_INTERNAL') || die();
/**
* Social breadth indicator - data.
*
* @package mod_data
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class social_breadth extends activity_base {
/**
* Returns the name.
*
* If there is a corresponding '_help' string this will be shown as well.
*
* @return \lang_string
*/
public static function get_name(): \lang_string {
return new \lang_string('indicator:socialbreadth', 'mod_data');
}
public function get_indicator_type() {
return self::INDICATOR_SOCIAL;
}
public function get_social_breadth_level(\cm_info $cm) {
return self::SOCIAL_LEVEL_1;
}
}
@@ -0,0 +1,86 @@
<?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/>.
declare(strict_types=1);
namespace mod_data\completion;
use core_completion\activity_custom_completion;
/**
* Activity custom completion subclass for the data activity.
*
* Class for defining mod_data's custom completion rules and fetching the completion statuses
* of the custom completion rules for a given data instance and a user.
*
* @package mod_data
* @copyright Simey Lameze <simey@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class custom_completion extends activity_custom_completion {
/**
* Fetches the completion state for a given completion rule.
*
* @param string $rule The completion rule.
* @return int The completion state.
*/
public function get_state(string $rule): int {
global $DB;
$this->validate_rule($rule);
$userentries = $DB->count_records('data_records', ['dataid' => $this->cm->instance, 'userid' => $this->userid]);
$completionentries = $this->cm->customdata['customcompletionrules']['completionentries'];
return ($completionentries <= $userentries) ? COMPLETION_COMPLETE : COMPLETION_INCOMPLETE;
}
/**
* Fetch the list of custom completion rules that this module defines.
*
* @return array
*/
public static function get_defined_custom_rules(): array {
return ['completionentries'];
}
/**
* Returns an associative array of the descriptions of custom completion rules.
*
* @return array
*/
public function get_custom_rule_descriptions(): array {
$entries = $this->cm->customdata['customcompletionrules']['completionentries'] ?? 0;
return [
'completionentries' => get_string('completiondetail:entries', 'data', $entries),
];
}
/**
* Returns an array of all completion rules, in the order they should be displayed to users.
*
* @return array
*/
public function get_sort_order(): array {
return [
'completionview',
'completionentries',
'completionusegrade',
'completionpassgrade',
];
}
}
+70
View File
@@ -0,0 +1,70 @@
<?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/>.
/**
* Contains the class for fetching the important dates in mod_data for a given module instance and a user.
*
* @package mod_data
* @copyright 2021 Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
declare(strict_types=1);
namespace mod_data;
use core\activity_dates;
/**
* Class for fetching the important dates in mod_data for a given module instance and a user.
*
* @copyright 2021 Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class dates extends activity_dates {
/**
* Returns a list of important dates in mod_data
*
* @return array
*/
protected function get_dates(): array {
$timeopen = $this->cm->customdata['timeavailablefrom'] ?? null;
$timeclose = $this->cm->customdata['timeavailableto'] ?? null;
$now = time();
$dates = [];
if ($timeopen) {
$openlabelid = $timeopen > $now ? 'activitydate:opens' : 'activitydate:opened';
$dates[] = [
'dataid' => 'timeavailablefrom',
'label' => get_string($openlabelid, 'course'),
'timestamp' => (int) $timeopen,
];
}
if ($timeclose) {
$closelabelid = $timeclose > $now ? 'activitydate:closes' : 'activitydate:closed';
$dates[] = [
'dataid' => 'timeavailableto',
'label' => get_string($closelabelid, 'course'),
'timestamp' => (int) $timeclose,
];
}
return $dates;
}
}
@@ -0,0 +1,55 @@
<?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/>.
/**
* The mod_data comment created event.
*
* @package mod_data
* @copyright 2013 Rajesh Taneja <rajesh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_data comment created event class.
*
* @package mod_data
* @since Moodle 2.7
* @copyright 2013 Rajesh Taneja <rajesh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class comment_created extends \core\event\comment_created {
/**
* Get URL related to the action.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/data/view.php', array('id' => $this->contextinstanceid));
}
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' added the comment with id '$this->objectid' to the database activity with " .
"course module id '$this->contextinstanceid'.";
}
}
@@ -0,0 +1,55 @@
<?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/>.
/**
* The mod_data comment deleted event.
*
* @package mod_data
* @copyright 2013 Rajesh Taneja <rajesh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_data comment deleted event class.
*
* @package mod_data
* @since Moodle 2.7
* @copyright 2013 Rajesh Taneja <rajesh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class comment_deleted extends \core\event\comment_deleted {
/**
* Get URL related to the action.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/data/view.php', array('id' => $this->contextinstanceid));
}
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' deleted the comment with id '$this->objectid' from the database activity with " .
"course module id '$this->contextinstanceid'.";
}
}
@@ -0,0 +1,41 @@
<?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/>.
/**
* The mod_data instance list viewed event.
*
* @package mod_data
* @since Moodle 2.7
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_data instance list viewed event class.
*
* @package mod_data
* @since Moodle 2.7
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class course_module_instance_list_viewed extends \core\event\course_module_instance_list_viewed {
// No code required here as the parent class handles it all.
}
@@ -0,0 +1,54 @@
<?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/>.
/**
* The mod_data course module viewed event.
*
* @package mod_data
* @since Moodle 2.7
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_data course module viewed event class.
*
* @package mod_data
* @since Moodle 2.7
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class course_module_viewed extends \core\event\course_module_viewed {
/**
* Init method.
*
* @return void
*/
protected function init() {
$this->data['objecttable'] = 'data';
$this->data['crud'] = 'r';
$this->data['edulevel'] = self::LEVEL_PARTICIPATING;
}
public static function get_objectid_mapping() {
return array('db' => 'data', 'restore' => 'data');
}
}
+114
View File
@@ -0,0 +1,114 @@
<?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/>.
/**
* The mod_data field created event.
*
* @package mod_data
* @since Moodle 2.7
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_data field created event class.
*
* @property-read array $other {
* Extra information about event.
*
* - string fieldname: the name of the field.
* - int dataid: the id of the data activity.
* }
*
* @package mod_data
* @since Moodle 2.7
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class field_created extends \core\event\base {
/**
* Init method.
*
* @return void
*/
protected function init() {
$this->data['objecttable'] = 'data_fields';
$this->data['crud'] = 'c';
$this->data['edulevel'] = self::LEVEL_TEACHING;
}
/**
* Return localised event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventfieldcreated', 'mod_data');
}
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' created the field with id '$this->objectid' for the data activity " .
"with course module id '$this->contextinstanceid'.";
}
/**
* Get URL related to the action.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/data/field.php', array('d' => $this->other['dataid']));
}
/**
* Custom validation.
*
* @throws \coding_exception when validation does not pass.
* @return void
*/
protected function validate_data() {
parent::validate_data();
if (!isset($this->other['fieldname'])) {
throw new \coding_exception('The \'fieldname\' value must be set in other.');
}
if (!isset($this->other['dataid'])) {
throw new \coding_exception('The \'dataid\' value must be set in other.');
}
}
public static function get_objectid_mapping() {
return array('db' => 'data_fields', 'restore' => 'data_field');
}
public static function get_other_mapping() {
$othermapped = array();
$othermapped['dataid'] = array('db' => 'data', 'restore' => 'data');
return $othermapped;
}
}
+113
View File
@@ -0,0 +1,113 @@
<?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/>.
/**
* The mod_data field deleted event.
*
* @package mod_data
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_data field deleted event class.
*
* @property-read array $other {
* Extra information about event.
*
* - string fieldname: the name of the field.
* - int dataid: the id of the data activity.
* }
*
* @package mod_data
* @since Moodle 2.7
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class field_deleted extends \core\event\base {
/**
* Init method.
*
* @return void
*/
protected function init() {
$this->data['objecttable'] = 'data_fields';
$this->data['crud'] = 'd';
$this->data['edulevel'] = self::LEVEL_TEACHING;
}
/**
* Return localised event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventfielddeleted', 'mod_data');
}
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' deleted the field with id '$this->objectid' in the data activity " .
"with course module id '$this->contextinstanceid'.";
}
/**
* Get URL related to the action.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/data/field.php', array('d' => $this->other['dataid']));
}
/**
* Custom validation.
*
* @throws \coding_exception when validation does not pass.
* @return void
*/
protected function validate_data() {
parent::validate_data();
if (!isset($this->other['fieldname'])) {
throw new \coding_exception('The \'fieldname\' value must be set in other.');
}
if (!isset($this->other['dataid'])) {
throw new \coding_exception('The \'dataid\' value must be set in other.');
}
}
public static function get_objectid_mapping() {
return array('db' => 'data_fields', 'restore' => 'data_field');
}
public static function get_other_mapping() {
$othermapped = array();
$othermapped['dataid'] = array('db' => 'data', 'restore' => 'data');
return $othermapped;
}
}
+113
View File
@@ -0,0 +1,113 @@
<?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/>.
/**
* The mod_data field updated event.
*
* @package mod_data
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_data field updated event class.
*
* @property-read array $other {
* Extra information about event.
*
* - string fieldname: the name of the field.
* - int dataid: the id of the data activity.
* }
*
* @package mod_data
* @since Moodle 2.7
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class field_updated extends \core\event\base {
/**
* Init method.
*
* @return void
*/
protected function init() {
$this->data['objecttable'] = 'data_fields';
$this->data['crud'] = 'u';
$this->data['edulevel'] = self::LEVEL_TEACHING;
}
/**
* Return localised event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventfieldupdated', 'mod_data');
}
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' updated the field with id '$this->objectid' in the data activity " .
"with course module id '$this->contextinstanceid'.";
}
/**
* Get URL related to the action.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/data/field.php', array('d' => $this->other['dataid']));
}
/**
* Custom validation.
*
* @throws \coding_exception when validation does not pass.
* @return void
*/
protected function validate_data() {
parent::validate_data();
if (!isset($this->other['fieldname'])) {
throw new \coding_exception('The \'fieldname\' value must be set in other.');
}
if (!isset($this->other['dataid'])) {
throw new \coding_exception('The \'dataid\' value must be set in other.');
}
}
public static function get_objectid_mapping() {
return array('db' => 'data_fields', 'restore' => 'data_field');
}
public static function get_other_mapping() {
$othermapped = array();
$othermapped['dataid'] = array('db' => 'data', 'restore' => 'data');
return $othermapped;
}
}
+108
View File
@@ -0,0 +1,108 @@
<?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/>.
/**
* The mod_data record created event.
*
* @package mod_data
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_data record created event class.
*
* @property-read array $other {
* Extra information about event.
*
* - int dataid: the id of the data activity.
* }
*
* @package mod_data
* @since Moodle 2.7
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class record_created extends \core\event\base {
/**
* Init method.
*
* @return void
*/
protected function init() {
$this->data['objecttable'] = 'data_records';
$this->data['crud'] = 'c';
$this->data['edulevel'] = self::LEVEL_PARTICIPATING;
}
/**
* Return localised event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventrecordcreated', 'mod_data');
}
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' created the data record with id '$this->objectid' for the data activity " .
"with course module id '$this->contextinstanceid'.";
}
/**
* Get URL related to the action.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/data/view.php', array('d' => $this->other['dataid'], 'rid' => $this->objectid));
}
/**
* Custom validation.
*
* @throws \coding_exception when validation does not pass.
* @return void
*/
protected function validate_data() {
parent::validate_data();
if (!isset($this->other['dataid'])) {
throw new \coding_exception('The \'dataid\' value must be set in other.');
}
}
public static function get_objectid_mapping() {
return array('db' => 'data_records', 'restore' => 'data_record');
}
public static function get_other_mapping() {
$othermapped = array();
$othermapped['dataid'] = array('db' => 'data', 'restore' => 'data');
return $othermapped;
}
}
+108
View File
@@ -0,0 +1,108 @@
<?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/>.
/**
* The mod_data record deleted event.
*
* @package mod_data
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_data record deleted event class.
*
* @property-read array $other {
* Extra information about event.
*
* - int dataid: the id of the data activity.
* }
*
* @package mod_data
* @since Moodle 2.7
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class record_deleted extends \core\event\base {
/**
* Init method.
*
* @return void
*/
protected function init() {
$this->data['objecttable'] = 'data_records';
$this->data['crud'] = 'd';
$this->data['edulevel'] = self::LEVEL_PARTICIPATING;
}
/**
* Return localised event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventrecorddeleted', 'mod_data');
}
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' deleted the data record with id '$this->objectid' in the data activity " .
"with course module id '$this->contextinstanceid'.";
}
/**
* Get URL related to the action.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/data/view.php', array('d' => $this->other['dataid']));
}
/**
* Custom validation.
*
* @throws \coding_exception when validation does not pass.
* @return void
*/
protected function validate_data() {
parent::validate_data();
if (!isset($this->other['dataid'])) {
throw new \coding_exception('The \'dataid\' value must be set in other.');
}
}
public static function get_objectid_mapping() {
return array('db' => 'data_records', 'restore' => 'data_record');
}
public static function get_other_mapping() {
$othermapped = array();
$othermapped['dataid'] = array('db' => 'data', 'restore' => 'data');
return $othermapped;
}
}
+108
View File
@@ -0,0 +1,108 @@
<?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/>.
/**
* The mod_data record updated event.
*
* @package mod_data
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_data record updated event class.
*
* @property-read array $other {
* Extra information about event.
*
* - int dataid: the id of the data activity.
* }
*
* @package mod_data
* @since Moodle 2.7
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class record_updated extends \core\event\base {
/**
* Init method.
*
* @return void
*/
protected function init() {
$this->data['objecttable'] = 'data_records';
$this->data['crud'] = 'u';
$this->data['edulevel'] = self::LEVEL_PARTICIPATING;
}
/**
* Return localised event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventrecordupdated', 'mod_data');
}
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' updated the data record with id '$this->objectid' in the data activity " .
"with course module id '$this->contextinstanceid'.";
}
/**
* Get URL related to the action.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/data/view.php', array('d' => $this->other['dataid'], 'rid' => $this->objectid));
}
/**
* Custom validation.
*
* @throws \coding_exception when validation does not pass.
* @return void
*/
protected function validate_data() {
parent::validate_data();
if (!isset($this->other['dataid'])) {
throw new \coding_exception('The \'dataid\' value must be set in other.');
}
}
public static function get_objectid_mapping() {
return array('db' => 'data_records', 'restore' => 'data_record');
}
public static function get_other_mapping() {
$othermapped = array();
$othermapped['dataid'] = array('db' => 'data', 'restore' => 'data');
return $othermapped;
}
}
+104
View File
@@ -0,0 +1,104 @@
<?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/>.
/**
* The mod_data template updated event.
*
* @package mod_data
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_data template updated event class.
*
* @property-read array $other {
* Extra information about event.
*
* - int dataid: the id of the data activity.
* }
*
* @package mod_data
* @since Moodle 2.7
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class template_updated extends \core\event\base {
/**
* Init method.
*
* @return void
*/
protected function init() {
$this->data['crud'] = 'u';
$this->data['edulevel'] = self::LEVEL_OTHER;
}
/**
* Return localised event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventtemplateupdated', 'mod_data');
}
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' updated the template for the data activity with course module " .
"id '$this->contextinstanceid'.";
}
/**
* Get URL related to the action.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/data/templates.php', array('d' => $this->other['dataid']));
}
/**
* Custom validation.
*
* @throws \coding_exception when validation does not pass.
* @return void
*/
protected function validate_data() {
parent::validate_data();
if (!isset($this->other['dataid'])) {
throw new \coding_exception('The \'dataid\' value must be set in other.');
}
}
public static function get_other_mapping() {
$othermapped = array();
$othermapped['dataid'] = array('db' => 'data', 'restore' => 'data');
return $othermapped;
}
}
+103
View File
@@ -0,0 +1,103 @@
<?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/>.
/**
* The mod_data template viewed event.
*
* @package mod_data
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_data template viewed event class.
*
* @property-read array $other {
* Extra information about event.
*
* - int dataid the id of the data activity.
* }
*
* @package mod_data
* @since Moodle 2.7
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class template_viewed extends \core\event\base {
/**
* Init method.
*
* @return void
*/
protected function init() {
$this->data['crud'] = 'r';
$this->data['edulevel'] = self::LEVEL_OTHER;
}
/**
* Return localised event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventtemplateviewed', 'mod_data');
}
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' viewed the template for the data activity with course module " .
"id '$this->contextinstanceid'.";
}
/**
* Get URL related to the action.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/data/templates.php', array('d' => $this->other['dataid']));
}
/**
* Custom validation.
*
* @throws \coding_exception when validation does not pass.
* @return void
*/
protected function validate_data() {
parent::validate_data();
if (!isset($this->other['dataid'])) {
throw new \coding_exception('The \'dataid\' value must be set in other.');
}
}
public static function get_other_mapping() {
$othermapped = array();
$othermapped['dataid'] = array('db' => 'data', 'restore' => 'data');
return $othermapped;
}
}
File diff suppressed because it is too large Load Diff
+106
View File
@@ -0,0 +1,106 @@
<?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/>.
/**
* Class for exporting content associated to a record.
*
* @package mod_data
* @copyright 2017 Juan Leyva <juan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\external;
defined('MOODLE_INTERNAL') || die();
use core\external\exporter;
use renderer_base;
use core_external\external_files;
use core_external\util as external_util;
/**
* Class for exporting content associated to a record.
*
* @copyright 2017 Juan Leyva <juan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class content_exporter extends exporter {
protected static function define_properties() {
return array(
'id' => array(
'type' => PARAM_INT,
'description' => 'Content id.',
),
'fieldid' => array(
'type' => PARAM_INT,
'description' => 'The field type of the content.',
'default' => 0,
),
'recordid' => array(
'type' => PARAM_INT,
'description' => 'The record this content belongs to.',
'default' => 0,
),
'content' => array(
'type' => PARAM_RAW,
'description' => 'Contents.',
'null' => NULL_ALLOWED,
),
'content1' => array(
'type' => PARAM_RAW,
'description' => 'Contents.',
'null' => NULL_ALLOWED,
),
'content2' => array(
'type' => PARAM_RAW,
'description' => 'Contents.',
'null' => NULL_ALLOWED,
),
'content3' => array(
'type' => PARAM_RAW,
'description' => 'Contents.',
'null' => NULL_ALLOWED,
),
'content4' => array(
'type' => PARAM_RAW,
'description' => 'Contents.',
'null' => NULL_ALLOWED,
),
);
}
protected static function define_related() {
return array(
'context' => 'context',
);
}
protected static function define_other_properties() {
return array(
'files' => array(
'type' => external_files::get_properties_for_exporter(),
'multiple' => true,
'optional' => true,
),
);
}
protected function get_other_values(renderer_base $output) {
$values = ['files' => external_util::get_area_files($this->related['context']->id, 'mod_data', 'content', $this->data->id)];
return $values;
}
}
+248
View File
@@ -0,0 +1,248 @@
<?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/>.
/**
* Class for exporting partial database data.
*
* @package mod_data
* @copyright 2017 Juan Leyva <juan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\external;
defined('MOODLE_INTERNAL') || die();
use core\external\exporter;
use renderer_base;
use core_external\external_files;
use core_external\util as external_util;
/**
* Class for exporting partial database data (some fields are only viewable by admins).
*
* @copyright 2017 Juan Leyva <juan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class database_summary_exporter extends exporter {
protected static function define_properties() {
return array(
'id' => array(
'type' => PARAM_INT,
'description' => 'Database id'),
'course' => array(
'type' => PARAM_INT,
'description' => 'Course id'),
'name' => array(
'type' => PARAM_RAW,
'description' => 'Database name'),
'intro' => array(
'type' => PARAM_RAW,
'description' => 'The Database intro',
),
'introformat' => array(
'choices' => array(FORMAT_HTML, FORMAT_MOODLE, FORMAT_PLAIN, FORMAT_MARKDOWN),
'type' => PARAM_INT,
'default' => FORMAT_MOODLE
),
'lang' => array(
'type' => PARAM_LANG,
'description' => 'Forced activity language',
'null' => NULL_ALLOWED,
),
'comments' => array(
'type' => PARAM_BOOL,
'description' => 'comments enabled',
),
'timeavailablefrom' => array(
'type' => PARAM_INT,
'description' => 'timeavailablefrom field',
),
'timeavailableto' => array(
'type' => PARAM_INT,
'description' => 'timeavailableto field',
),
'timeviewfrom' => array(
'type' => PARAM_INT,
'description' => 'timeviewfrom field',
),
'timeviewto' => array(
'type' => PARAM_INT,
'description' => 'timeviewto field',
),
'requiredentries' => array(
'type' => PARAM_INT,
'description' => 'requiredentries field',
),
'requiredentriestoview' => array(
'type' => PARAM_INT,
'description' => 'requiredentriestoview field',
),
'maxentries' => array(
'type' => PARAM_INT,
'description' => 'maxentries field',
),
'rssarticles' => array(
'type' => PARAM_INT,
'description' => 'rssarticles field',
),
'singletemplate' => array(
'type' => PARAM_RAW,
'description' => 'singletemplate field',
'null' => NULL_ALLOWED,
),
'listtemplate' => array(
'type' => PARAM_RAW,
'description' => 'listtemplate field',
'null' => NULL_ALLOWED,
),
'listtemplateheader' => array(
'type' => PARAM_RAW,
'description' => 'listtemplateheader field',
'null' => NULL_ALLOWED,
),
'listtemplatefooter' => array(
'type' => PARAM_RAW,
'description' => 'listtemplatefooter field',
'null' => NULL_ALLOWED,
),
'addtemplate' => array(
'type' => PARAM_RAW,
'description' => 'addtemplate field',
'null' => NULL_ALLOWED,
),
'rsstemplate' => array(
'type' => PARAM_RAW,
'description' => 'rsstemplate field',
'null' => NULL_ALLOWED,
),
'rsstitletemplate' => array(
'type' => PARAM_RAW,
'description' => 'rsstitletemplate field',
'null' => NULL_ALLOWED,
),
'csstemplate' => array(
'type' => PARAM_RAW,
'description' => 'csstemplate field',
'null' => NULL_ALLOWED,
),
'jstemplate' => array(
'type' => PARAM_RAW,
'description' => 'jstemplate field',
'null' => NULL_ALLOWED,
),
'asearchtemplate' => array(
'type' => PARAM_RAW,
'description' => 'asearchtemplate field',
'null' => NULL_ALLOWED,
),
'approval' => array(
'type' => PARAM_BOOL,
'description' => 'approval field',
),
'manageapproved' => array(
'type' => PARAM_BOOL,
'description' => 'manageapproved field',
),
'scale' => array(
'type' => PARAM_INT,
'description' => 'scale field',
'optional' => true,
),
'assessed' => array(
'type' => PARAM_INT,
'description' => 'assessed field',
'optional' => true,
),
'assesstimestart' => array(
'type' => PARAM_INT,
'description' => 'assesstimestart field',
'optional' => true,
),
'assesstimefinish' => array(
'type' => PARAM_INT,
'description' => 'assesstimefinish field',
'optional' => true,
),
'defaultsort' => array(
'type' => PARAM_INT,
'description' => 'defaultsort field',
),
'defaultsortdir' => array(
'type' => PARAM_INT,
'description' => 'defaultsortdir field',
),
'editany' => array(
'type' => PARAM_BOOL,
'description' => 'editany field (not used any more)',
'optional' => true,
),
'notification' => array(
'type' => PARAM_INT,
'description' => 'notification field (not used any more)',
'optional' => true,
),
'timemodified' => array(
'type' => PARAM_INT,
'description' => 'Time modified',
'optional' => true,
),
);
}
protected static function define_related() {
return array(
'context' => 'context'
);
}
protected static function define_other_properties() {
return array(
'coursemodule' => array(
'type' => PARAM_INT
),
'introfiles' => array(
'type' => external_files::get_properties_for_exporter(),
'multiple' => true,
'optional' => true,
),
);
}
protected function get_other_values(renderer_base $output) {
$context = $this->related['context'];
$values = array(
'coursemodule' => $context->instanceid,
'introfiles' => external_util::get_area_files($context->id, 'mod_data', 'intro', false, false),
);
return $values;
}
/**
* Get the formatting parameters for the intro.
*
* @return array
*/
protected function get_format_parameters_for_intro() {
return [
'component' => 'mod_data',
'filearea' => 'intro',
'options' => array('noclean' => true),
];
}
}
+123
View File
@@ -0,0 +1,123 @@
<?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\external;
use core\notification;
use core_external\external_api;
use core_external\external_function_parameters;
use core_external\external_multiple_structure;
use core_external\external_single_structure;
use core_external\external_value;
use core_external\external_warnings;
use mod_data\manager;
use mod_data\preset;
/**
* This is the external method for deleting a saved preset.
*
* @package mod_data
* @since Moodle 4.1
* @copyright 2022 Amaia Anabitarte <amaia@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class delete_saved_preset extends external_api {
/**
* Parameters.
*
* @return external_function_parameters
*/
public static function execute_parameters(): external_function_parameters {
return new external_function_parameters([
'dataid' => new external_value(PARAM_INT, 'Id of the data activity', VALUE_REQUIRED),
'presetnames' => new external_multiple_structure(
new external_value(PARAM_TEXT, 'The preset name to delete', VALUE_REQUIRED)
)
]);
}
/**
* Delete saved preset from the file system.
*
* @param int $dataid Id of the data activity to check context and permissions.
* @param array $presetnames List of saved preset names to delete.
* @return array True if the content has been deleted; false and the warning, otherwise.
*/
public static function execute(int $dataid, array $presetnames): array {
global $DB;
$result = false;
$warnings = [];
$params = self::validate_parameters(self::execute_parameters(), ['dataid' => $dataid, 'presetnames' => $presetnames]);
$instance = $DB->get_record('data', ['id' => $params['dataid']], '*', MUST_EXIST);
$manager = manager::create_from_instance($instance);
foreach ($params['presetnames'] as $presetname) {
try {
$preset = preset::create_from_instance($manager, $presetname);
if ($preset->can_manage()) {
if ($preset->delete()) {
notification::success(get_string('presetdeleted', 'mod_data'));
$result = true;
} else {
// An error ocurred while deleting the preset.
$warnings[] = [
'item' => $presetname,
'warningcode' => 'failedpresetdelete',
'message' => get_string('failedpresetdelete', 'mod_data')
];
notification::error(get_string('failedpresetdelete', 'mod_data'));
}
} else {
// The user has no permission to delete the preset.
$warnings[] = [
'item' => $presetname,
'warningcode' => 'cannotdeletepreset',
'message' => get_string('cannotdeletepreset', 'mod_data')
];
notification::error(get_string('cannotdeletepreset', 'mod_data'));
}
} catch (\moodle_exception $e) {
// The saved preset has not been deleted.
$warnings[] = [
'item' => $presetname,
'warningcode' => 'exception',
'message' => $e->getMessage()
];
notification::error($e->getMessage());
}
}
return [
'result' => $result,
'warnings' => $warnings
];
}
/**
* Return.
*
* @return external_single_structure
*/
public static function execute_returns(): external_single_structure {
return new external_single_structure([
'result' => new external_value(PARAM_BOOL, 'The processing result'),
'warnings' => new external_warnings()
]);
}
}
+85
View File
@@ -0,0 +1,85 @@
<?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/>.
/**
* Class for exporting field data.
*
* @package mod_data
* @copyright 2017 Juan Leyva <juan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\external;
defined('MOODLE_INTERNAL') || die();
use core\external\exporter;
/**
* Class for exporting field data.
*
* @copyright 2017 Juan Leyva <juan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class field_exporter extends exporter {
protected static function define_properties() {
$properties = array(
'id' => array(
'type' => PARAM_INT,
'description' => 'Field id.',
),
'dataid' => array(
'type' => PARAM_INT,
'description' => 'The field type of the content.',
'default' => 0,
),
'type' => array(
'type' => PARAM_PLUGIN,
'description' => 'The field type.',
),
'name' => array(
'type' => PARAM_TEXT,
'description' => 'The field name.',
),
'description' => array(
'type' => PARAM_RAW,
'description' => 'The field description.',
),
'required' => array(
'type' => PARAM_BOOL,
'description' => 'Whether is a field required or not.',
'default' => 0,
),
);
// Field possible parameters.
for ($i = 1; $i <= 10; $i++) {
$properties["param$i"] = array(
'type' => PARAM_RAW,
'description' => 'Field parameters',
'null' => NULL_ALLOWED,
);
}
return $properties;
}
protected static function define_related() {
// Context is required for text formatting.
return array(
'context' => 'context',
);
}
}
+97
View File
@@ -0,0 +1,97 @@
<?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\external;
use core\notification;
use core_external\external_api;
use core_external\external_function_parameters;
use core_external\external_single_structure;
use core_external\external_value;
use core_external\external_warnings;
use mod_data\local\importer\preset_importer;
use mod_data\manager;
/**
* This is the external method for deleting a saved preset.
*
* @package mod_data
* @since Moodle 4.1
* @copyright 2022 Amaia Anabitarte <amaia@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class get_mapping_information extends external_api {
/**
* Parameters.
*
* @return external_function_parameters
*/
public static function execute_parameters(): external_function_parameters {
return new external_function_parameters([
'cmid' => new external_value(PARAM_INT, 'Id of the data activity', VALUE_REQUIRED),
'importedpreset' => new external_value(PARAM_TEXT, 'Preset to be imported'),
]);
}
/**
* Get importing information for the given database course module.
*
* @param int $cmid Id of the course module where to import the preset.
* @param string $importedpreset Plugin or saved preset to be imported.
* @return array Information needed to decide whether to show the dialogue or not.
*/
public static function execute(int $cmid, string $importedpreset): array {
$params = self::validate_parameters(
self::execute_parameters(),
['cmid' => $cmid, 'importedpreset' => $importedpreset]
);
try {
// Let's get the manager.
list($course, $cm) = get_course_and_cm_from_cmid($params['cmid'], manager::MODULE);
$manager = manager::create_from_coursemodule($cm);
$importer = preset_importer::create_from_plugin_or_directory($manager, $params['importedpreset']);
$result['data'] = $importer->get_mapping_information();
} catch (\moodle_exception $e) {
$result['warnings'][] = [
'item' => $importedpreset,
'warningcode' => 'exception',
'message' => $e->getMessage()
];
notification::error($e->getMessage());
}
return $result;
}
/**
* Return.
*
* @return external_single_structure
*/
public static function execute_returns(): external_single_structure {
return new external_single_structure([
'data' => new external_single_structure([
'needsmapping' => new external_value(PARAM_BOOL, 'Whether the importing needs mapping or not'),
'presetname' => new external_value(PARAM_TEXT, 'Name of the applied preset'),
'fieldstocreate' => new external_value(PARAM_TEXT, 'List of field names to create'),
'fieldstoremove' => new external_value(PARAM_TEXT, 'List of field names to remove'),
], 'Information to import if everything went fine', VALUE_OPTIONAL),
'warnings' => new external_warnings(),
]);
}
}
+143
View File
@@ -0,0 +1,143 @@
<?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/>.
/**
* Class for exporting record data.
*
* @package mod_data
* @copyright 2017 Juan Leyva <juan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\external;
defined('MOODLE_INTERNAL') || die();
use core\external\exporter;
use renderer_base;
use core_user;
use core_tag\external\tag_item_exporter;
/**
* Class for exporting record data.
*
* @copyright 2017 Juan Leyva <juan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class record_exporter extends exporter {
protected static function define_properties() {
return array(
'id' => array(
'type' => PARAM_INT,
'description' => 'Record id.',
),
'userid' => array(
'type' => PARAM_INT,
'description' => 'The id of the user who created the record.',
'default' => 0,
),
'groupid' => array(
'type' => PARAM_INT,
'description' => 'The group id this record belongs to (0 for no groups).',
'default' => 0,
),
'dataid' => array(
'type' => PARAM_INT,
'description' => 'The database id this record belongs to.',
'default' => 0,
),
'timecreated' => array(
'type' => PARAM_INT,
'description' => 'Time the record was created.',
'default' => 0,
),
'timemodified' => array(
'type' => PARAM_INT,
'description' => 'Last time the record was modified.',
'default' => 0,
),
'approved' => array(
'type' => PARAM_BOOL,
'description' => 'Whether the entry has been approved (if the database is configured in that way).',
'default' => 0,
),
);
}
protected static function define_related() {
return array(
'database' => 'stdClass',
'user' => 'stdClass?',
'context' => 'context',
'contents' => 'stdClass[]?',
);
}
protected static function define_other_properties() {
return array(
'canmanageentry' => array(
'type' => PARAM_BOOL,
'description' => 'Whether the current user can manage this entry',
),
'fullname' => array(
'type' => PARAM_TEXT,
'description' => 'The user who created the entry fullname.',
'optional' => true,
),
'contents' => array(
'type' => content_exporter::read_properties_definition(),
'description' => 'The record contents.',
'multiple' => true,
'optional' => true,
),
'tags' => array(
'type' => tag_item_exporter::read_properties_definition(),
'description' => 'Tags.',
'multiple' => true,
'optional' => true,
),
);
}
protected function get_other_values(renderer_base $output) {
global $PAGE;
$values = array(
'canmanageentry' => data_user_can_manage_entry($this->data, $this->related['database'], $this->related['context']),
);
if (!empty($this->related['user']) and !empty($this->related['user']->id)) {
$values['fullname'] = fullname($this->related['user']);
} else if ($this->data->userid) {
$user = core_user::get_user($this->data->userid);
$values['fullname'] = fullname($user);
}
if (!empty($this->related['contents'])) {
$contents = [];
foreach ($this->related['contents'] as $content) {
$related = array('context' => $this->related['context']);
$exporter = new content_exporter($content, $related);
$contents[] = $exporter->export($PAGE->get_renderer('core'));
}
$values['contents'] = $contents;
}
$values['tags'] = \core_tag\external\util::get_item_tags('mod_data', 'data_records', $this->data->id);
return $values;
}
}
+115
View File
@@ -0,0 +1,115 @@
<?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\form;
use context;
use moodle_exception;
use moodle_url;
use core_form\dynamic_form;
/**
* Import presets form.
*
* @package mod_data
* @copyright 2022 Laurent David <laurent.david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class import_presets extends dynamic_form {
/**
* Process the form submission
*
* @return array
* @throws moodle_exception
*/
public function process_dynamic_submission(): array {
global $CFG;
$filepath = $this->save_temp_file('importfile');
$context = $this->get_context_for_dynamic_submission();
$returnurl = new moodle_url('/mod/data/preset.php', [
'id' => $context->instanceid,
'action' => 'importzip',
'filepath' => str_replace($CFG->tempdir, '', $filepath)
]);
return [
'result' => true,
'url' => $returnurl->out(false),
];
}
/**
* Get context
*
* @return context
*/
protected function get_context_for_dynamic_submission(): context {
$cmid = $this->optional_param('cmid', null, PARAM_INT);
$cm = get_coursemodule_from_id('data', $cmid);
$context = \context_module::instance($cm->id);
return $context;
}
/**
* Set data
*
* @return void
*/
public function set_data_for_dynamic_submission(): void {
$data = (object) [
'cmid' => $this->optional_param('cmid', 0, PARAM_INT),
];
$this->set_data($data);
}
/**
* Has access ?
*
* @return void
* @throws moodle_exception
*/
protected function check_access_for_dynamic_submission(): void {
if (!has_capability('mod/data:managetemplates', $this->get_context_for_dynamic_submission())) {
throw new moodle_exception('importpresetmissingcapability', 'data');
}
}
/**
* Get page URL
*
* @return moodle_url
*/
protected function get_page_url_for_dynamic_submission(): moodle_url {
$cmid = $this->optional_param('cmid', null, PARAM_INT);
return new moodle_url('/mod/data/preset.php', ['id' => $cmid]);
}
/**
* Form definition
*
* @return void
*/
protected function definition() {
$mform = $this->_form;
$mform->addElement('html', \html_writer::div(get_string('importpreset_desc', 'mod_data'), 'py-3'));
$mform->addElement('hidden', 'cmid');
$mform->setType('cmid', PARAM_INT);
$mform->addElement('filepicker', 'importfile', get_string('choosepreset', 'mod_data'), null,
['accepted_types' => '.zip']);
$mform->addRule('importfile', null, 'required');
}
}
+239
View File
@@ -0,0 +1,239 @@
<?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\form;
use context;
use core\notification;
use moodle_exception;
use moodle_url;
use core_form\dynamic_form;
use mod_data\manager;
use mod_data\preset;
/**
* Save database as preset form.
*
* @package mod_data
* @copyright 2021 Mihail Geshoski <paulh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class save_as_preset extends dynamic_form {
/**
* Form definition
*/
protected function definition() {
$this->_form->addElement('hidden', 'd');
$this->_form->setType('d', PARAM_INT);
$this->_form->addElement('hidden', 'action', 'save');
$this->_form->setType('action', PARAM_ALPHANUM);
$this->_form->addElement('hidden', 'oldpresetname', '');
$this->_form->setType('oldpresetname', PARAM_FILE);
$this->_form->addElement('text', 'name', get_string('name'), ['size' => 60]);
$this->_form->setType('name', PARAM_FILE);
$this->_form->addRule('name', null, 'required');
// Overwrite checkbox will be hidden by default. It will only appear if there is an error when saving the preset.
$this->_form->addElement('checkbox', 'overwrite', '', get_string('overrwritedesc', 'data'), ['class' => 'hidden']);
$this->_form->addElement('textarea', 'description', get_string('description'), ['rows' => 5, 'cols' => 60]);
$this->_form->setType('name', PARAM_TEXT);
}
/**
* Return form context
*
* @return context
*/
protected function get_context_for_dynamic_submission(): context {
global $DB;
$d = $this->optional_param('d', null, PARAM_INT);
$data = $DB->get_record('data', array('id' => $d), '*', MUST_EXIST);
$course = $DB->get_record('course', array('id' => $data->course), '*', MUST_EXIST);
$cm = get_coursemodule_from_instance('data', $data->id, $course->id, null, MUST_EXIST);
return \context_module::instance($cm->id, MUST_EXIST);
}
/**
* Perform some validation.
*
* @param array $formdata
* @param array $files
* @return array
*/
public function validation($formdata, $files): array {
$errors = parent::validation($formdata, $files);
$context = $this->get_context_for_dynamic_submission();
$cm = get_coursemodule_from_id('', $context->instanceid, 0, false, MUST_EXIST);
$manager = manager::create_from_coursemodule($cm);
if (!empty($formdata['overwrite'])) {
$presets = $manager->get_available_presets();
$selectedpreset = new \stdClass();
foreach ($presets as $preset) {
if ($preset->name == $formdata['name']) {
$selectedpreset = $preset;
break;
}
}
if (!$selectedpreset instanceof preset || !$selectedpreset->can_manage()) {
$errors['name'] = get_string('cannotoverwritepreset', 'data');
}
} else if ($formdata['action'] == 'saveaspreset' || $formdata['oldpresetname'] != $formdata['name']) {
// If the preset exists when a new preset is saved or name has changed, then we need to throw an error.
$sitepresets = $manager->get_available_saved_presets();
$usercandelete = false;
foreach ($sitepresets as $preset) {
if ($formdata['name'] == $preset->name) {
if ($preset->can_manage()) {
$errors['name'] = get_string('errorpresetexists', 'data');
$usercandelete = true;
} else {
$errors['name'] = get_string('errorpresetexistsbutnotoverwrite', 'data');
}
break;
}
}
// If there are some errors, the checkbox should be displayed, to let users overwrite the preset.
if (!empty($errors) && $usercandelete) {
$this->_form->getElement('overwrite')->removeAttribute('class');
}
}
return $errors;
}
/**
* Check if current user has access to this form, otherwise throw exception.
*
* @return void
* @throws moodle_exception
*/
protected function check_access_for_dynamic_submission(): void {
global $DB;
if (!has_capability('mod/data:managetemplates', $this->get_context_for_dynamic_submission())) {
throw new moodle_exception('saveaspresetmissingcapability', 'data');
}
$action = $this->optional_param('action', '', PARAM_ALPHANUM);
if ($action == 'saveaspreset') {
// For saving it as a new preset, some fields need to be created; otherwise, an exception will be raised.
$instanceid = $this->optional_param('d', null, PARAM_INT);
$hasfields = $DB->record_exists('data_fields', ['dataid' => $instanceid]);
if (!$hasfields) {
throw new moodle_exception('nofieldindatabase', 'data');
}
}
}
/**
* Process the form submission, used if form was submitted via AJAX.
*
* @return array
*/
public function process_dynamic_submission(): array {
global $DB, $CFG;
require_once($CFG->dirroot . '/mod/data/lib.php');
$formdata = $this->get_data();
$result = false;
$errors = [];
$data = $DB->get_record('data', array('id' => $formdata->d), '*', MUST_EXIST);
$course = $DB->get_record('course', array('id' => $data->course), '*', MUST_EXIST);
$cm = get_coursemodule_from_instance('data', $data->id, $course->id, null, MUST_EXIST);
$context = \context_module::instance($cm->id, MUST_EXIST);
try {
$manager = manager::create_from_instance($data);
if (!empty($formdata->overwrite)) {
$presets = $manager->get_available_presets();
$selectedpreset = new \stdClass();
foreach ($presets as $preset) {
if ($preset->name == $formdata->name) {
$selectedpreset = $preset;
break;
}
}
if ($selectedpreset instanceof preset && $selectedpreset->can_manage()) {
$selectedpreset->delete();
}
}
$presetname = $formdata->name;
if (!empty($formdata->oldpresetname)) {
$presetname = $formdata->oldpresetname;
}
$preset = preset::create_from_instance($manager, $presetname, $formdata->description);
if (!empty($formdata->oldpresetname)) {
// Update the name and the description, to save the new data.
$preset->name = $formdata->name;
$preset->description = $formdata->description;
}
$result = $preset->save();
if ($result) {
// Add notification in the session to be shown when the page is reloaded on the JS side.
$previewurl = new moodle_url(
'/mod/data/preset.php',
['id' => $cm->id, 'fullname' => $preset->get_fullname(), 'action' => 'preview']
);
notification::success(get_string('savesuccess', 'mod_data', (object)['url' => $previewurl->out()]));
}
} catch (\Exception $exception) {
$errors[] = $exception->getMessage();
}
return [
'result' => $result,
'errors' => $errors,
];
}
/**
* Load in existing data as form defaults.
*
* @return void
*/
public function set_data_for_dynamic_submission(): void {
$data = (object)[
'd' => $this->optional_param('d', 0, PARAM_INT),
'action' => $this->optional_param('action', '', PARAM_ALPHANUM),
'oldpresetname' => $this->optional_param('presetname', '', PARAM_FILE),
'name' => $this->optional_param('presetname', '', PARAM_FILE),
'description' => $this->optional_param('presetdescription', '', PARAM_TEXT),
];
$this->set_data($data);
}
/**
* Returns url to set in $PAGE->set_url() when form is being rendered or submitted via AJAX
*
* @return moodle_url
*/
protected function get_page_url_for_dynamic_submission(): moodle_url {
$d = $this->optional_param('d', null, PARAM_INT);
return new moodle_url('/user/field.php', ['d' => $d]);
}
}
@@ -0,0 +1,81 @@
<?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\local\exporter;
use coding_exception;
use csv_export_writer;
/**
* CSV entries exporter for mod_data.
*
* @package mod_data
* @copyright 2023 ISB Bayern
* @author Philipp Memmel
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class csv_entries_exporter extends entries_exporter {
/** @var string[] Possible delimiter names. Only used internally to check if a valid delimiter name
* has been specified.
*/
private const POSSIBLE_DELIMITER_NAMES = ['comma', 'tab', 'semicolon', 'colon', 'cfg'];
/**
* @var string name of the delimiter to use for the csv export. Possible values:
* 'comma', 'tab', 'semicolon', 'colon' or 'cfg'.
*/
private string $delimitername = 'comma';
/**
* Returns the csv data exported by the csv_export_writer for further handling.
*
* @see \mod_data\local\exporter\entries_exporter::get_data_file_content()
*/
public function get_data_file_content(): string {
global $CFG;
require_once($CFG->libdir . '/csvlib.class.php');
return csv_export_writer::print_array($this->exportdata, $this->delimitername, '"', true);
}
/**
* Returns the file extension of this entries exporter.
*
* @see \mod_data\local\exporter\entries_exporter::get_export_data_file_extension()
*/
public function get_export_data_file_extension(): string {
return 'csv';
}
/**
* Setter for the delimiter name which should be used in this csv_entries_exporter object.
*
* Calling this setter is optional, the delimiter name defaults to 'comma'.
*
* @param string $delimitername one of 'comma', 'tab', 'semicolon', 'colon' or 'cfg'
* @return void
* @throws coding_exception if a wrong delimiter name has been specified
*/
public function set_delimiter_name(string $delimitername): void {
if (!in_array($delimitername, self::POSSIBLE_DELIMITER_NAMES)) {
throw new coding_exception('Wrong delimiter type',
'Please choose on of the following delimiters: '
. '\"comma\", \"tab\", \"semicolon\", \"colon\", \"cfg\"');
}
$this->delimitername = $delimitername;
}
}
@@ -0,0 +1,277 @@
<?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\local\exporter;
use file_serving_exception;
use moodle_exception;
use zip_archive;
/**
* Exporter class for exporting data and - if needed - files as well in a zip archive.
*
* @package mod_data
* @copyright 2023 ISB Bayern
* @author Philipp Memmel
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class entries_exporter {
/** @var int Tracks the currently edited row of the export data file. */
private int $currentrow;
/**
* @var array The data structure containing the data for exporting. It's a 2-dimensional array of
* rows and columns.
*/
protected array $exportdata;
/** @var string Name of the export file name without extension. */
protected string $exportfilename;
/** @var zip_archive The zip archive object we store all the files in, if we need to export files as well. */
private zip_archive $ziparchive;
/** @var bool Tracks the state if the zip archive already has been closed. */
private bool $isziparchiveclosed;
/** @var string full path of the zip archive. */
private string $zipfilepath;
/** @var array Array to store all filenames in the zip archive for export. */
private array $filenamesinzip;
/**
* Creates an entries_exporter object.
*
* This object can be used to export data to different formats including files. If files are added,
* everything will be bundled up in a zip archive.
*/
public function __construct() {
$this->currentrow = 0;
$this->exportdata = [];
$this->exportfilename = 'Exportfile';
$this->filenamesinzip = [];
$this->isziparchiveclosed = true;
}
/**
* Adds a row (array of strings) to the export data.
*
* @param array $row the row to add, $row has to be a plain array of strings
* @return void
*/
public function add_row(array $row): void {
$this->exportdata[] = $row;
$this->currentrow++;
}
/**
* Adds a data string (so the content for a "cell") to the current row.
*
* @param string $cellcontent the content to add to the current row
* @return void
*/
public function add_to_current_row(string $cellcontent): void {
$this->exportdata[$this->currentrow][] = $cellcontent;
}
/**
* Signal the entries_exporter to finish the current row and jump to the next row.
*
* @return void
*/
public function next_row(): void {
$this->currentrow++;
}
/**
* Sets the name of the export file.
*
* Only use the basename without path and without extension here.
*
* @param string $exportfilename name of the file without path and extension
* @return void
*/
public function set_export_file_name(string $exportfilename): void {
$this->exportfilename = $exportfilename;
}
/**
* The entries_exporter will prepare a data file from the rows and columns being added.
* Overwrite this method to generate the data file as string.
*
* @return string the data file as a string
*/
abstract protected function get_data_file_content(): string;
/**
* Overwrite the method to return the file extension your data file will have, for example
* <code>return 'csv';</code> for a csv file entries_exporter.
*
* @return string the file extension of the data file your entries_exporter is using
*/
abstract protected function get_export_data_file_extension(): string;
/**
* Returns the count of currently stored records (rows excluding header row).
*
* @return int the count of records/rows
*/
public function get_records_count(): int {
// The attribute $this->exportdata also contains a header. If only one row is present, this
// usually is the header, so record count should be 0.
if (count($this->exportdata) <= 1) {
return 0;
}
return count($this->exportdata) - 1;
}
/**
* Use this method to add a file which should be exported to the entries_exporter.
*
* @param string $filename the name of the file which should be added
* @param string $filecontent the content of the file as a string
* @param string $zipsubdir the subdirectory in the zip archive. Defaults to 'files/'.
* @return void
* @throws moodle_exception if there is an error adding the file to the zip archive
*/
public function add_file_from_string(string $filename, string $filecontent, string $zipsubdir = 'files/'): void {
if (empty($this->filenamesinzip)) {
// No files added yet, so we need to create a zip archive.
$this->create_zip_archive();
}
if (!str_ends_with($zipsubdir, '/')) {
$zipsubdir .= '/';
}
$zipfilename = $zipsubdir . $filename;
$this->filenamesinzip[] = $zipfilename;
$this->ziparchive->add_file_from_string($zipfilename, $filecontent);
}
/**
* Sends the generated export file.
*
* Care: By default this function finishes the current PHP request and directly serves the file to the user as download.
*
* @param bool $sendtouser true if the file should be sent directly to the user, if false the file content will be returned
* as string
* @return string|null file content as string if $sendtouser is true
* @throws moodle_exception if there is an issue adding the data file
* @throws file_serving_exception if the file could not be served properly
*/
public function send_file(bool $sendtouser = true): null|string {
if (empty($this->filenamesinzip)) {
if ($sendtouser) {
send_file($this->get_data_file_content(),
$this->exportfilename . '.' . $this->get_export_data_file_extension(),
null, 0, true, true);
return null;
} else {
return $this->get_data_file_content();
}
}
$this->add_file_from_string($this->exportfilename . '.' . $this->get_export_data_file_extension(),
$this->get_data_file_content(), '/');
$this->finish_zip_archive();
if ($this->isziparchiveclosed) {
if ($sendtouser) {
send_file($this->zipfilepath, $this->exportfilename . '.zip', null, 0, false, true);
return null;
} else {
return file_get_contents($this->zipfilepath);
}
} else {
throw new file_serving_exception('Could not serve zip file, it could not be closed properly.');
}
}
/**
* Checks if a file with the given name has already been added to the file export bundle.
*
* Care: Filenames are compared to all files in the specified zip subdirectory which
* defaults to 'files/'.
*
* @param string $filename the filename containing the zip path of the file to check
* @param string $zipsubdir The subdirectory in which the filename should be looked for,
* defaults to 'files/'
* @return bool true if file with the given name already exists, false otherwise
*/
public function file_exists(string $filename, string $zipsubdir = 'files/'): bool {
if (!str_ends_with($zipsubdir, '/')) {
$zipsubdir .= '/';
}
if (empty($filename)) {
return false;
}
return in_array($zipsubdir . $filename, $this->filenamesinzip, true);
}
/**
* Creates a unique filename based on the given filename.
*
* This method adds "_1", "_2", ... to the given file name until the newly generated filename
* is not equal to any of the already saved ones in the export file bundle.
*
* @param string $filename the filename based on which a unique filename should be generated
* @return string the unique filename
*/
public function create_unique_filename(string $filename): string {
if (!$this->file_exists($filename)) {
return $filename;
}
$extension = pathinfo($filename, PATHINFO_EXTENSION);
$filenamewithoutextension = empty($extension)
? $filename
: substr($filename, 0,strlen($filename) - strlen($extension) - 1);
$filenamewithoutextension = $filenamewithoutextension . '_1';
$i = 1;
$filename = empty($extension) ? $filenamewithoutextension : $filenamewithoutextension . '.' . $extension;
while ($this->file_exists($filename)) {
// In case we have already a file ending with '_XX' where XX is an ascending number, we have to
// remove '_XX' first before adding '_YY' again where YY is the successor of XX.
$filenamewithoutextension = preg_replace('/_' . $i . '$/', '_' . ($i + 1), $filenamewithoutextension);
$filename = empty($extension) ? $filenamewithoutextension : $filenamewithoutextension . '.' . $extension;
$i++;
}
return $filename;
}
/**
* Prepares the zip archive.
*
* @return void
*/
private function create_zip_archive(): void {
$tmpdir = make_request_directory();
$this->zipfilepath = $tmpdir . '/' . $this->exportfilename . '.zip';
$this->ziparchive = new zip_archive();
$this->isziparchiveclosed = !$this->ziparchive->open($this->zipfilepath);
}
/**
* Closes the zip archive.
*
* @return void
*/
private function finish_zip_archive(): void {
if (!$this->isziparchiveclosed) {
$this->isziparchiveclosed = $this->ziparchive->close();
}
}
}
@@ -0,0 +1,65 @@
<?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\local\exporter;
use MoodleODSWorkbook;
use MoodleODSWriter;
/**
* ODS entries exporter for mod_data.
*
* @package mod_data
* @copyright 2023 ISB Bayern
* @author Philipp Memmel
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class ods_entries_exporter extends entries_exporter {
/**
* Returns the file extension of this entries exporter.
*
* @see \mod_data\local\exporter\entries_exporter::get_export_data_file_extension()
*/
public function get_export_data_file_extension(): string {
return 'ods';
}
/**
* Returns the ods data exported by the ODS library for further handling.
*
* @see \mod_data\local\exporter\entries_exporter::get_data_file_content()
*/
public function get_data_file_content(): string {
global $CFG;
require_once("$CFG->libdir/odslib.class.php");
$filearg = '-';
$workbook = new MoodleODSWorkbook($filearg);
$worksheet = [];
$worksheet[0] = $workbook->add_worksheet('');
$rowno = 0;
foreach ($this->exportdata as $row) {
$colno = 0;
foreach ($row as $col) {
$worksheet[0]->write($rowno, $colno, $col);
$colno++;
}
$rowno++;
}
$writer = new MoodleODSWriter($worksheet);
return $writer->get_file_content();
}
}
+141
View File
@@ -0,0 +1,141 @@
<?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\local\exporter;
use context;
use context_system;
/**
* Utility class for exporting data from a mod_data instance.
*
* @package mod_data
* @copyright 2023 ISB Bayern
* @author Philipp Memmel
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class utils {
/**
* Exports the data of the mod_data instance to an entries_exporter object which then can export it to a file format.
*
* @param int $dataid
* @param array $fields
* @param array $selectedfields
* @param entries_exporter $exporter the entries_exporter object used
* @param int $currentgroup group ID of the current group. This is used for
* exporting data while maintaining group divisions.
* @param context|null $context the context in which the operation is performed (for capability checks)
* @param bool $userdetails whether to include the details of the record author
* @param bool $time whether to include time created/modified
* @param bool $approval whether to include approval status
* @param bool $tags whether to include tags
* @param bool $includefiles whether files should be exported as well
* @return void
*/
public static function data_exportdata(int $dataid, array $fields, array $selectedfields, entries_exporter $exporter,
int $currentgroup = 0, context $context = null, bool $userdetails = false, bool $time = false, bool $approval = false,
bool $tags = false, bool $includefiles = true): void {
global $DB;
if (is_null($context)) {
$context = context_system::instance();
}
// Exporting user data needs special permission.
$userdetails = $userdetails && has_capability('mod/data:exportuserinfo', $context);
// Populate the header in first row of export.
$header = [];
foreach ($fields as $key => $field) {
if (!in_array($field->field->id, $selectedfields)) {
// Ignore values we aren't exporting.
unset($fields[$key]);
} else {
$header[] = $field->field->name;
}
}
if ($tags) {
$header[] = get_string('tags', 'data');
}
if ($userdetails) {
$header[] = get_string('user');
$header[] = get_string('username');
$header[] = get_string('email');
}
if ($time) {
$header[] = get_string('timeadded', 'data');
$header[] = get_string('timemodified', 'data');
}
if ($approval) {
$header[] = get_string('approved', 'data');
}
$exporter->add_row($header);
$datarecords = $DB->get_records('data_records', array('dataid' => $dataid));
ksort($datarecords);
$line = 1;
foreach ($datarecords as $record) {
// Get content indexed by fieldid.
if ($currentgroup) {
$select = 'SELECT c.fieldid, c.content, c.content1, c.content2, c.content3, c.content4 FROM {data_content} c, '
. '{data_records} r WHERE c.recordid = ? AND r.id = c.recordid AND r.groupid = ?';
$where = array($record->id, $currentgroup);
} else {
$select = 'SELECT fieldid, content, content1, content2, content3, content4 FROM {data_content} WHERE recordid = ?';
$where = array($record->id);
}
if ($content = $DB->get_records_sql($select, $where)) {
foreach ($fields as $field) {
$contents = '';
if (isset($content[$field->field->id])) {
$contents = $field->export_text_value($content[$field->field->id]);
if (!empty($contents) && $field->file_export_supported() && $includefiles
&& !is_null($field->export_file_value($record))) {
// For exporting overwrite the content of the column with a unique
// filename, even it is not exactly the name of the file in the
// mod_data instance content. But it's more important to match the name
// of the exported file.
$contents = $exporter->create_unique_filename($contents);
$exporter->add_file_from_string($contents, $field->export_file_value($record));
}
}
// Just be double sure.
$contents = !empty($contents) ? $contents : '';
$exporter->add_to_current_row($contents);
}
if ($tags) {
$itemtags = \core_tag_tag::get_item_tags_array('mod_data', 'data_records', $record->id);
$exporter->add_to_current_row(implode(', ', $itemtags));
}
if ($userdetails) { // Add user details to the export data.
$userdata = get_complete_user_data('id', $record->userid);
$exporter->add_to_current_row(fullname($userdata));
$exporter->add_to_current_row($userdata->username);
$exporter->add_to_current_row($userdata->email);
}
if ($time) { // Add time added / modified.
$exporter->add_to_current_row(userdate($record->timecreated));
$exporter->add_to_current_row(userdate($record->timemodified));
}
if ($approval) { // Add approval status.
$exporter->add_to_current_row((int) $record->approved);
}
}
$exporter->next_row();
}
}
}
@@ -0,0 +1,209 @@
<?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\local\importer;
use context_module;
use core_php_time_limit;
use core_tag_tag;
use core_user;
use csv_import_reader;
use moodle_exception;
use stdClass;
/**
* CSV entries_importer class for importing data and - if needed - files as well from a zip archive.
*
* @package mod_data
* @copyright 2023 ISB Bayern
* @author Philipp Memmel
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class csv_entries_importer extends entries_importer {
/** @var array Log entries for successfully added records. */
private array $addedrecordsmessages = [];
/**
* Declares the entries_importer to use a csv file as data file.
*
* @see entries_importer::get_import_data_file_extension()
*/
public function get_import_data_file_extension(): string {
return 'csv';
}
/**
* Import records for a data instance from csv data.
*
* @param stdClass $cm Course module of the data instance.
* @param stdClass $data The data instance.
* @param string $encoding The encoding of csv data.
* @param string $fielddelimiter The delimiter of the csv data.
*
* @throws moodle_exception
*/
public function import_csv(stdClass $cm, stdClass $data, string $encoding, string $fielddelimiter): void {
global $CFG, $DB;
// Large files are likely to take their time and memory. Let PHP know
// that we'll take longer, and that the process should be recycled soon
// to free up memory.
core_php_time_limit::raise();
raise_memory_limit(MEMORY_HUGE);
$iid = csv_import_reader::get_new_iid('moddata');
$cir = new csv_import_reader($iid, 'moddata');
$context = context_module::instance($cm->id);
$readcount = $cir->load_csv_content($this->get_data_file_content(), $encoding, $fielddelimiter);
if (empty($readcount)) {
throw new \moodle_exception('csvfailed', 'data', "{$CFG->wwwroot}/mod/data/edit.php?d={$data->id}");
} else {
if (!$fieldnames = $cir->get_columns()) {
throw new \moodle_exception('cannotreadtmpfile', 'error');
}
// Check the fieldnames are valid.
$rawfields = $DB->get_records('data_fields', ['dataid' => $data->id], '', 'name, id, type');
$fields = [];
$errorfield = '';
$usernamestring = get_string('username');
$safetoskipfields = [get_string('user'), get_string('email'),
get_string('timeadded', 'data'), get_string('timemodified', 'data'),
get_string('approved', 'data'), get_string('tags', 'data')];
$userfieldid = null;
foreach ($fieldnames as $id => $name) {
if (!isset($rawfields[$name])) {
if ($name == $usernamestring) {
$userfieldid = $id;
} else if (!in_array($name, $safetoskipfields)) {
$errorfield .= "'$name' ";
}
} else {
// If this is the second time, a field with this name comes up, it must be a field not provided by the user...
// like the username.
if (isset($fields[$name])) {
if ($name == $usernamestring) {
$userfieldid = $id;
}
unset($fieldnames[$id]); // To ensure the user provided content fields remain in the array once flipped.
} else {
$field = $rawfields[$name];
$field->type = clean_param($field->type, PARAM_ALPHA);
$filepath = "$CFG->dirroot/mod/data/field/$field->type/field.class.php";
if (!file_exists($filepath)) {
$errorfield .= "'$name' ";
continue;
}
require_once($filepath);
$classname = 'data_field_' . $field->type;
$fields[$name] = new $classname($field, $data, $cm);
}
}
}
if (!empty($errorfield)) {
throw new \moodle_exception('fieldnotmatched', 'data',
"{$CFG->wwwroot}/mod/data/edit.php?d={$data->id}", $errorfield);
}
$fieldnames = array_flip($fieldnames);
$cir->init();
while ($record = $cir->next()) {
$authorid = null;
if ($userfieldid) {
if (!($author = core_user::get_user_by_username($record[$userfieldid], 'id'))) {
$authorid = null;
} else {
$authorid = $author->id;
}
}
// Determine presence of "approved" field within the record to import.
$approved = true;
if (array_key_exists(get_string('approved', 'data'), $fieldnames)) {
$approvedindex = $fieldnames[get_string('approved', 'data')];
$approved = !empty($record[$approvedindex]);
}
if ($recordid = data_add_record($data, 0, $authorid, $approved)) { // Add instance to data_record.
foreach ($fields as $field) {
$fieldid = $fieldnames[$field->field->name];
if (isset($record[$fieldid])) {
$value = $record[$fieldid];
} else {
$value = '';
}
if (method_exists($field, 'update_content_import')) {
$field->update_content_import($recordid, $value, 'field_' . $field->field->id);
} else {
$content = new stdClass();
$content->fieldid = $field->field->id;
$content->content = $value;
$content->recordid = $recordid;
if ($field->file_import_supported() && $this->importfiletype === 'zip') {
$filecontent = $this->get_file_content_from_zip($content->content);
if (!$filecontent) {
// No corresponding file in zip archive, so no record for this field being added at all.
continue;
}
$contentid = $DB->insert_record('data_content', $content);
$field->import_file_value($contentid, $filecontent, $content->content);
} else {
$DB->insert_record('data_content', $content);
}
}
}
if (core_tag_tag::is_enabled('mod_data', 'data_records') &&
isset($fieldnames[get_string('tags', 'data')])) {
$columnindex = $fieldnames[get_string('tags', 'data')];
$rawtags = $record[$columnindex];
$tags = explode(',', $rawtags);
foreach ($tags as $tag) {
$tag = trim($tag);
if (empty($tag)) {
continue;
}
core_tag_tag::add_item_tag('mod_data', 'data_records', $recordid, $context, $tag);
}
}
$this->addedrecordsmessages[] = get_string('added', 'moodle',
count($this->addedrecordsmessages) + 1)
. ". " . get_string('entry', 'data')
. " (ID $recordid)\n";
}
}
$cir->close();
$cir->cleanup(true);
}
}
/**
* Getter for the array of messages for added records.
*
* For each successfully added record the array contains a log message.
*
* @return array Array of message strings: For each added record one message string
*/
public function get_added_records_messages(): array {
return $this->addedrecordsmessages;
}
}
@@ -0,0 +1,144 @@
<?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\local\importer;
use coding_exception;
use core_php_time_limit;
use file_packer;
/**
* Importer class for importing data and - if needed - files as well from a zip archive.
*
* @package mod_data
* @copyright 2023 ISB Bayern
* @author Philipp Memmel
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class entries_importer {
/** @var string The import file path of the file which data should be imported from. */
protected string $importfilepath;
/** @var string The original name of the import file name including extension of the file which data should be imported from. */
protected string $importfilename;
/** @var string $importfiletype The file type of the import file. */
protected string $importfiletype;
/** @var file_packer Zip file packer to extract files from a zip archive. */
private file_packer $packer;
/** @var bool Tracks state if zip archive has been extracted already. */
private bool $zipfileextracted;
/** @var string Temporary directory where zip archive is being extracted to. */
private string $extracteddir;
/**
* Creates an entries_importer object.
*
* This object can be used to import data from data files (like csv) and zip archives both including a data file and files to be
* stored in the course module context.
*
* @param string $importfilepath the complete path of the import file including filename
* @param string $importfilename the import file name as uploaded by the user
* @throws coding_exception if a wrong file type is being used
*/
public function __construct(string $importfilepath, string $importfilename) {
$this->importfilepath = $importfilepath;
$this->importfilename = $importfilename;
$this->importfiletype = pathinfo($importfilename, PATHINFO_EXTENSION);
$this->zipfileextracted = false;
if ($this->importfiletype !== $this->get_import_data_file_extension() && $this->importfiletype !== 'zip') {
throw new coding_exception('Only "zip" or "' . $this->get_import_data_file_extension() . '" files are '
. 'allowed.');
}
}
/**
* Return the file extension of the import data file which is being used, for example 'csv' for a csv entries_importer.
*
* @return string the file extension of the export data file
*/
abstract public function get_import_data_file_extension(): string;
/**
* Returns the file content of the data file.
*
* Returns the content of the file directly if the entries_importer's file is a data file itself.
* If the entries_importer's file is a zip archive, the content of the first found data file in the
* zip archive's root will be returned.
*
* @return false|string the data file content as string; false, if file cannot be found/read
*/
public function get_data_file_content(): false|string {
if ($this->importfiletype !== 'zip') {
// We have no zip archive, so the file itself must be the data file.
return file_get_contents($this->importfilepath);
}
// So we have a zip archive and need to find the right data file in the root of the zip archive.
$this->extract_zip();
$datafilenames = array_filter($this->packer->list_files($this->importfilepath),
fn($file) => pathinfo($file->pathname, PATHINFO_EXTENSION) === $this->get_import_data_file_extension()
&& !str_contains($file->pathname, '/'));
if (empty($datafilenames) || count($datafilenames) > 1) {
return false;
}
return file_get_contents($this->extracteddir . reset($datafilenames)->pathname);
}
/**
* Returns the file content from a file which has been stored in the zip archive.
*
* @param string $filename
* @param string $zipsubdir
* @return false|string the file content as string, false if the file could not be found/read
*/
public function get_file_content_from_zip(string $filename, string $zipsubdir = 'files/'): false|string {
if (empty($filename)) {
// Nothing to return.
return false;
}
// Just to be sure extract if not extracted yet.
$this->extract_zip();
if (!str_ends_with($zipsubdir, '/')) {
$zipsubdir .= '/';
}
$filepathinextractedzip = $this->extracteddir . $zipsubdir . $filename;
return file_exists($filepathinextractedzip) ? file_get_contents($filepathinextractedzip) : false;
}
/**
* Extracts (if not already done and if we have a zip file to deal with) the zip file to a temporary directory.
*
* @return void
*/
private function extract_zip(): void {
if ($this->zipfileextracted || $this->importfiletype !== 'zip') {
return;
}
$this->packer = get_file_packer();
core_php_time_limit::raise(180);
$this->extracteddir = make_request_directory();
if (!str_ends_with($this->extracteddir, '/')) {
$this->extracteddir .= '/';
}
$this->packer->extract_to_pathname($this->importfilepath, $this->extracteddir);
$this->zipfileextracted = true;
}
}
@@ -0,0 +1,79 @@
<?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\local\importer;
use mod_data\manager;
/**
* Data preset importer for existing presets
* @package mod_data
* @copyright 2022 Amaia Anabitarte <amaia@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class preset_existing_importer extends preset_importer {
/** @var int user id. */
protected $userid;
/** @var string fullname of the preset. */
private $fullname;
/**
* Constructor
*
* @param manager $manager
* @param string $fullname
*/
public function __construct(manager $manager, string $fullname) {
global $USER;
list($userid, $shortname) = explode('/', $fullname, 2);
$context = $manager->get_context();
if ($userid &&
($userid != $USER->id) &&
!has_capability('mod/data:manageuserpresets', $context) &&
!has_capability('mod/data:viewalluserpresets', $context)
) {
throw new \coding_exception('Invalid preset provided');
}
$this->userid = intval($userid);
$this->fullname = $fullname;
$cm = $manager->get_coursemodule();
$course = $cm->get_course();
$filepath = data_preset_path($course, $userid, $shortname);
parent::__construct($manager, $filepath);
}
/**
* Returns user ID
*
* @return int userid
*/
public function get_userid(): int {
return $this->userid;
}
/**
* Returns the information we need to build the importer selector.
*
* @return array Value and name for the preset importer selector
*/
public function get_preset_selector(): array {
return ['name' => 'fullname', 'value' => $this->get_userid().'/'.$this->get_directory()];
}
}
@@ -0,0 +1,506 @@
<?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\local\importer;
use core\notification;
use mod_data\manager;
use mod_data\preset;
use stdClass;
use html_writer;
/**
* Abstract class used for data preset importers
*
* @package mod_data
* @copyright 2022 Amaia Anabitarte <amaia@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class preset_importer {
/** @var manager manager instance. */
private $manager;
/** @var string directory where to find the preset. */
protected $directory;
/** @var array fields to remove. */
public $fieldstoremove;
/** @var array fields to update. */
public $fieldstoupdate;
/** @var array fields to create. */
public $fieldstocreate;
/** @var array settings to be imported. */
public $settings;
/**
* Constructor
*
* @param manager $manager
* @param string $directory
*/
public function __construct(manager $manager, string $directory) {
$this->manager = $manager;
$this->directory = $directory;
// Read the preset and saved result.
$this->settings = $this->get_preset_settings();
}
/**
* Returns the name of the directory the preset is located in
*
* @return string
*/
public function get_directory(): string {
return basename($this->directory);
}
/**
* Retreive the contents of a file. That file may either be in a conventional directory of the Moodle file storage
*
* @param \file_storage|null $filestorage . Should be null if using a conventional directory
* @param \stored_file|null $fileobj the directory to look in. null if using a conventional directory
* @param string|null $dir the directory to look in. null if using the Moodle file storage
* @param string $filename the name of the file we want
* @return string|null the contents of the file or null if the file doesn't exist.
*/
public function get_file_contents(
?\file_storage &$filestorage,
?\stored_file &$fileobj,
?string $dir,
string $filename
): ?string {
if (empty($filestorage) || empty($fileobj)) {
if (substr($dir, -1) != '/') {
$dir .= '/';
}
if (file_exists($dir.$filename)) {
return file_get_contents($dir.$filename);
} else {
return null;
}
} else {
if ($filestorage->file_exists(
DATA_PRESET_CONTEXT,
DATA_PRESET_COMPONENT,
DATA_PRESET_FILEAREA,
0,
$fileobj->get_filepath(),
$filename)
) {
$file = $filestorage->get_file(
DATA_PRESET_CONTEXT,
DATA_PRESET_COMPONENT,
DATA_PRESET_FILEAREA,
0,
$fileobj->get_filepath(),
$filename
);
return $file->get_content();
} else {
return null;
}
}
}
/**
* Gets the preset settings
*
* @return stdClass Settings to be imported.
*/
public function get_preset_settings(): stdClass {
global $CFG;
require_once($CFG->libdir.'/xmlize.php');
$fs = null;
$fileobj = null;
if (!preset::is_directory_a_preset($this->directory)) {
// Maybe the user requested a preset stored in the Moodle file storage.
$fs = get_file_storage();
$files = $fs->get_area_files(DATA_PRESET_CONTEXT, DATA_PRESET_COMPONENT, DATA_PRESET_FILEAREA);
// Preset name to find will be the final element of the directory.
$explodeddirectory = explode('/', $this->directory);
$presettofind = end($explodeddirectory);
// Now go through the available files available and see if we can find it.
foreach ($files as $file) {
if (($file->is_directory() && $file->get_filepath() == '/') || !$file->is_directory()) {
continue;
}
$presetname = trim($file->get_filepath(), '/');
if ($presetname == $presettofind) {
$this->directory = $presetname;
$fileobj = $file;
}
}
if (empty($fileobj)) {
throw new \moodle_exception('invalidpreset', 'data', '', $this->directory);
}
}
$allowedsettings = [
'intro',
'comments',
'requiredentries',
'requiredentriestoview',
'maxentries',
'rssarticles',
'approval',
'defaultsortdir',
'defaultsort'
];
$module = $this->manager->get_instance();
$result = new stdClass;
$result->settings = new stdClass;
$result->importfields = [];
$result->currentfields = $this->manager->get_field_records();
// Grab XML.
$presetxml = $this->get_file_contents($fs, $fileobj, $this->directory, 'preset.xml');
$parsedxml = xmlize($presetxml, 0);
// First, do settings. Put in user friendly array.
$settingsarray = $parsedxml['preset']['#']['settings'][0]['#'];
$result->settings = new StdClass();
foreach ($settingsarray as $setting => $value) {
if (!is_array($value) || !in_array($setting, $allowedsettings)) {
// Unsupported setting.
continue;
}
$result->settings->$setting = $value[0]['#'];
}
// Now work out fields to user friendly array.
if (
array_key_exists('preset', $parsedxml) &&
array_key_exists('#', $parsedxml['preset']) &&
array_key_exists('field', $parsedxml['preset']['#'])) {
$fieldsarray = $parsedxml['preset']['#']['field'];
foreach ($fieldsarray as $field) {
if (!is_array($field)) {
continue;
}
$fieldstoimport = new StdClass();
foreach ($field['#'] as $param => $value) {
if (!is_array($value)) {
continue;
}
$fieldstoimport->$param = $value[0]['#'];
}
$fieldstoimport->dataid = $module->id;
$fieldstoimport->type = clean_param($fieldstoimport->type, PARAM_ALPHA);
$result->importfields[] = $fieldstoimport;
}
}
// Calculate default mapping.
if (is_null($this->fieldstoremove) && is_null($this->fieldstocreate) && is_null($this->fieldstoupdate)) {
$this->set_affected_fields($result->importfields, $result->currentfields);
}
// Now add the HTML templates to the settings array so we can update d.
foreach (manager::TEMPLATES_LIST as $templatename => $templatefile) {
$result->settings->$templatename = $this->get_file_contents(
$fs,
$fileobj,
$this->directory,
$templatefile
);
}
$result->settings->instance = $module->id;
return $result;
}
/**
* Import the preset into the given database module
*
* @param bool $overwritesettings Whether to overwrite activity settings or not.
* @return bool Wether the importing has been successful.
*/
public function import(bool $overwritesettings): bool {
global $DB, $OUTPUT, $CFG;
$settings = $this->settings->settings;
$currentfields = $this->settings->currentfields;
$missingfieldtypes = [];
$module = $this->manager->get_instance();
foreach ($this->fieldstoupdate as $currentid => $updatable) {
if ($currentid != -1 && isset($currentfields[$currentid])) {
$fieldobject = data_get_field_from_id($currentfields[$currentid]->id, $module);
$toupdate = false;
foreach ($updatable as $param => $value) {
if ($param != "id" && $fieldobject->field->$param !== $value) {
$fieldobject->field->$param = $value;
}
}
unset($fieldobject->field->similarfield);
$fieldobject->update_field();
unset($fieldobject);
}
}
foreach ($this->fieldstocreate as $newfield) {
/* Make a new field */
$filepath = $CFG->dirroot."/mod/data/field/$newfield->type/field.class.php";
if (!file_exists($filepath)) {
$missingfieldtypes[] = $newfield->name;
continue;
}
include_once($filepath);
if (!isset($newfield->description)) {
$newfield->description = '';
}
$classname = 'data_field_' . $newfield->type;
$fieldclass = new $classname($newfield, $module);
$fieldclass->insert_field();
unset($fieldclass);
}
if (!empty($missingfieldtypes)) {
echo $OUTPUT->notification(get_string('missingfieldtypeimport', 'data') . html_writer::alist($missingfieldtypes));
}
// Get rid of all old unused data.
foreach ($currentfields as $cid => $currentfield) {
if (!array_key_exists($cid, $this->fieldstoupdate)) {
// Delete all information related to fields.
$todelete = data_get_field_from_id($currentfield->id, $module);
$todelete->delete_field();
}
}
// Handle special settings here.
if (!empty($settings->defaultsort)) {
if (is_numeric($settings->defaultsort)) {
// Old broken value.
$settings->defaultsort = 0;
} else {
$settings->defaultsort = (int)$DB->get_field(
'data_fields',
'id',
['dataid' => $module->id, 'name' => $settings->defaultsort]
);
}
} else {
$settings->defaultsort = 0;
}
// Do we want to overwrite all current database settings?
if ($overwritesettings) {
// All supported settings.
$overwrite = array_keys((array)$settings);
} else {
// Only templates and sorting.
$overwrite = ['singletemplate', 'listtemplate', 'listtemplateheader', 'listtemplatefooter',
'addtemplate', 'rsstemplate', 'rsstitletemplate', 'csstemplate', 'jstemplate',
'asearchtemplate', 'defaultsortdir', 'defaultsort'];
}
// Now overwrite current data settings.
foreach ($module as $prop => $unused) {
if (in_array($prop, $overwrite)) {
$module->$prop = $settings->$prop;
}
}
data_update_instance($module);
return $this->cleanup();
}
/**
* Returns information about the fields needs to be removed, updated or created.
*
* @param array $newfields Array of new fields to be applied.
* @param array $currentfields Array of current fields on database activity.
* @return void
*/
public function set_affected_fields(array $newfields = [], array $currentfields = []): void {
$fieldstoremove = [];
$fieldstocreate = [];
$preservedfields = [];
// Maps fields and makes new ones.
if (!empty($newfields)) {
// We require an injective mapping, and need to know what to protect.
foreach ($newfields as $newid => $newfield) {
$preservedfieldid = optional_param("field_$newid", -1, PARAM_INT);
if (array_key_exists($preservedfieldid, $preservedfields)) {
throw new \moodle_exception('notinjectivemap', 'data');
}
if ($preservedfieldid == -1) {
// Let's check if there is any field with same type and name that we could map to.
foreach ($currentfields as $currentid => $currentfield) {
if (($currentfield->type == $newfield->type) &&
($currentfield->name == $newfield->name) && !array_key_exists($currentid, $preservedfields)) {
// We found a possible default map.
$preservedfieldid = $currentid;
$preservedfields[$currentid] = $newfield;
}
}
}
if ($preservedfieldid == -1) {
// We need to create a new field.
$fieldstocreate[] = $newfield;
} else {
$preservedfields[$preservedfieldid] = $newfield;
}
}
}
foreach ($currentfields as $currentid => $currentfield) {
if (!array_key_exists($currentid, $preservedfields)) {
$fieldstoremove[] = $currentfield;
}
}
$this->fieldstocreate = $fieldstocreate;
$this->fieldstoremove = $fieldstoremove;
$this->fieldstoupdate = $preservedfields;
}
/**
* Any clean up routines should go here
*
* @return bool Wether the preset has been successfully cleaned up.
*/
public function cleanup(): bool {
return true;
}
/**
* Check if the importing process needs fields mapping.
*
* @return bool True if the current database needs to map the fields imported.
*/
public function needs_mapping(): bool {
if (!$this->manager->has_fields()) {
return false;
}
return (!empty($this->fieldstocreate) || !empty($this->fieldstoremove));
}
/**
* Returns the information we need to build the importer selector.
*
* @return array Value and name for the preset importer selector
*/
public function get_preset_selector(): array {
return ['name' => 'directory', 'value' => $this->get_directory()];
}
/**
* Helper function to finish up the import routine.
*
* Called from fields and presets pages.
*
* @param bool $overwritesettings Whether to overwrite activity settings or not.
* @param stdClass $instance database instance object
* @return void
*/
public function finish_import_process(bool $overwritesettings, stdClass $instance): void {
$result = $this->import($overwritesettings);
if ($result) {
notification::success(get_string('importsuccess', 'mod_data'));
} else {
notification::error(get_string('cannotapplypreset', 'mod_data'));
}
$backurl = new \moodle_url('/mod/data/field.php', ['d' => $instance->id]);
redirect($backurl);
}
/**
* Get the right importer instance from the provided parameters (POST or GET)
*
* @param manager $manager the current database manager
* @return preset_importer the relevant preset_importer instance
* @throws \moodle_exception when the file provided as parameter (POST or GET) does not exist
*/
public static function create_from_parameters(manager $manager): preset_importer {
$fullname = optional_param('fullname', '', PARAM_PATH); // Directory the preset is in.
if (!$fullname) {
$fullname = required_param('directory', PARAM_FILE);
}
return self::create_from_plugin_or_directory($manager, $fullname);
}
/**
* Get the right importer instance from the provided parameters (POST or GET)
*
* @param manager $manager the current database manager
* @param string $pluginordirectory The plugin name or directory to create the importer from.
* @return preset_importer the relevant preset_importer instance
*/
public static function create_from_plugin_or_directory(manager $manager, string $pluginordirectory): preset_importer {
global $CFG;
if (!$pluginordirectory) {
throw new \moodle_exception('emptypresetname', 'mod_data');
}
try {
$presetdir = $CFG->tempdir . '/forms/' . $pluginordirectory;
if (file_exists($presetdir) && is_dir($presetdir)) {
return new preset_upload_importer($manager, $presetdir);
} else {
return new preset_existing_importer($manager, $pluginordirectory);
}
} catch (\moodle_exception $e) {
throw new \moodle_exception('errorpresetnotfound', 'mod_data', '', $pluginordirectory);
}
}
/**
* Get the information needed to decide the modal
*
* @return array An array with all the information to decide the mapping
*/
public function get_mapping_information(): array {
return [
'needsmapping' => $this->needs_mapping(),
'presetname' => preset::get_name_from_plugin($this->get_directory()),
'fieldstocreate' => $this->get_field_names($this->fieldstocreate),
'fieldstoremove' => $this->get_field_names($this->fieldstoremove),
];
}
/**
* Returns a list of the fields
*
* @param array $fields Array of fields to get name from.
* @return string A string listing the names of the fields.
*/
public function get_field_names(array $fields): string {
$fieldnames = array_map(function($field) {
return $field->name;
}, $fields);
return implode(', ', $fieldnames);
}
}
@@ -0,0 +1,55 @@
<?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\local\importer;
use mod_data\manager;
/**
* Data preset importer for uploaded presets
*
* @package mod_data
* @copyright 2022 Amaia Anabitarte <amaia@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class preset_upload_importer extends preset_importer {
/**
* Constructor
*
* @param manager $manager
* @param string $filepath
*/
public function __construct(manager $manager, string $filepath) {
if (is_file($filepath)) {
$fp = get_file_packer();
if ($fp->extract_to_pathname($filepath, $filepath.'_extracted')) {
fulldelete($filepath);
}
$filepath .= '_extracted';
}
parent::__construct($manager, $filepath);
}
/**
* Clean uploaded files up
*
* @return bool Wether the preset has been successfully cleaned up.
*/
public function cleanup(): bool {
return fulldelete($this->directory);
}
}
+501
View File
@@ -0,0 +1,501 @@
<?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;
}
}
+337
View File
@@ -0,0 +1,337 @@
<?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\output;
use mod_data\manager;
use mod_data\preset;
use moodle_url;
use url_select;
/**
* Class responsible for generating the action bar elements in the database module pages.
*
* @package mod_data
* @copyright 2021 Mihail Geshoski <mihail@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class action_bar {
/** @var int $id The database module id. */
private $id;
/** @var int $cmid The database course module id. */
private $cmid;
/** @var moodle_url $currenturl The URL of the current page. */
private $currenturl;
/**
* The class constructor.
*
* @param int $id The database module id.
* @param moodle_url $pageurl The URL of the current page.
*/
public function __construct(int $id, moodle_url $pageurl) {
$this->id = $id;
[$course, $cm] = get_course_and_cm_from_instance($this->id, 'data');
$this->cmid = $cm->id;
$this->currenturl = $pageurl;
}
/**
* Generate the output for the action bar in the field page.
*
* @param bool $hasfieldselect Whether the field selector element should be rendered.
* @param null $unused1 This parameter has been deprecated since 4.1 and should not be used anymore.
* @param null $unused2 This parameter has been deprecated since 4.1 and should not be used anymore.
* @return string The HTML code for the action bar.
*/
public function get_fields_action_bar(
bool $hasfieldselect = false,
?bool $unused1 = null,
?bool $unused2 = null
): string {
global $PAGE;
if ($unused1 !== null || $unused2 !== null) {
debugging('Deprecated argument passed to get_fields_action_bar method', DEBUG_DEVELOPER);
}
$renderer = $PAGE->get_renderer('mod_data');
$fieldsactionbar = new fields_action_bar($this->id);
return $renderer->render_fields_action_bar($fieldsactionbar);
}
/**
* Generate the output for the action bar in the field mappings page.
*
* @return string The HTML code for the action bar.
*/
public function get_fields_mapping_action_bar(): string {
global $PAGE;
$renderer = $PAGE->get_renderer('mod_data');
$fieldsactionbar = new fields_mappings_action_bar($this->id);
$data = $fieldsactionbar->export_for_template($renderer);
return $renderer->render_from_template('mod_data/fields_action_bar', $data);
}
/**
* Generate the output for the create a new field action menu.
*
* @param bool $isprimarybutton is the action trigger a primary or secondary button?
* @return \action_menu Action menu to create a new field
*/
public function get_create_fields(bool $isprimarybutton = false): \action_menu {
// Get the list of possible fields (plugins).
$plugins = \core_component::get_plugin_list('datafield');
$menufield = [];
foreach ($plugins as $plugin => $fulldir) {
$menufield[$plugin] = get_string('pluginname', "datafield_{$plugin}");
}
asort($menufield);
$fieldselect = new \action_menu();
$triggerclasses = ['btn'];
$triggerclasses[] = $isprimarybutton ? 'btn-primary' : 'btn-secondary';
$fieldselect->set_menu_trigger(get_string('newfield', 'mod_data'), join(' ', $triggerclasses));
$fieldselectparams = ['id' => $this->cmid, 'mode' => 'new'];
foreach ($menufield as $fieldtype => $fieldname) {
$fieldselectparams['newtype'] = $fieldtype;
$fieldselect->add(new \action_menu_link(
new moodle_url('/mod/data/field.php', $fieldselectparams),
new \pix_icon('field/' . $fieldtype, $fieldname, 'data'),
$fieldname,
false
));
}
$fieldselect->set_additional_classes('singlebutton');
return $fieldselect;
}
/**
* Generate the output for the action selector in the view page.
*
* @param bool $hasentries Whether entries exist.
* @param string $mode The current view mode (list, view...).
* @return string The HTML code for the action selector.
*/
public function get_view_action_bar(bool $hasentries, string $mode): string {
global $PAGE;
$viewlistlink = new moodle_url('/mod/data/view.php', ['d' => $this->id]);
$viewsinglelink = new moodle_url('/mod/data/view.php', ['d' => $this->id, 'mode' => 'single']);
$menu = [
$viewlistlink->out(false) => get_string('listview', 'mod_data'),
$viewsinglelink->out(false) => get_string('singleview', 'mod_data'),
];
$activeurl = $this->currenturl;
if ($this->currenturl->get_param('rid') || $this->currenturl->get_param('mode') == 'single') {
$activeurl = $viewsinglelink;
}
$urlselect = new url_select($menu, $activeurl->out(false), null, 'viewactionselect');
$urlselect->set_label(get_string('viewnavigation', 'mod_data'), ['class' => 'sr-only']);
$renderer = $PAGE->get_renderer('mod_data');
$viewactionbar = new view_action_bar($this->id, $urlselect, $hasentries, $mode);
return $renderer->render_view_action_bar($viewactionbar);
}
/**
* Generate the output for the action selector in the templates page.
*
* @return string The HTML code for the action selector.
*/
public function get_templates_action_bar(): string {
global $PAGE;
$listtemplatelink = new moodle_url('/mod/data/templates.php', ['d' => $this->id,
'mode' => 'listtemplate']);
$singletemplatelink = new moodle_url('/mod/data/templates.php', ['d' => $this->id,
'mode' => 'singletemplate']);
$advancedsearchtemplatelink = new moodle_url('/mod/data/templates.php', ['d' => $this->id,
'mode' => 'asearchtemplate']);
$addtemplatelink = new moodle_url('/mod/data/templates.php', ['d' => $this->id, 'mode' => 'addtemplate']);
$rsstemplatelink = new moodle_url('/mod/data/templates.php', ['d' => $this->id, 'mode' => 'rsstemplate']);
$csstemplatelink = new moodle_url('/mod/data/templates.php', ['d' => $this->id, 'mode' => 'csstemplate']);
$jstemplatelink = new moodle_url('/mod/data/templates.php', ['d' => $this->id, 'mode' => 'jstemplate']);
$menu = [
$addtemplatelink->out(false) => get_string('addtemplate', 'mod_data'),
$singletemplatelink->out(false) => get_string('singletemplate', 'mod_data'),
$listtemplatelink->out(false) => get_string('listtemplate', 'mod_data'),
$advancedsearchtemplatelink->out(false) => get_string('asearchtemplate', 'mod_data'),
$csstemplatelink->out(false) => get_string('csstemplate', 'mod_data'),
$jstemplatelink->out(false) => get_string('jstemplate', 'mod_data'),
$rsstemplatelink->out(false) => get_string('rsstemplate', 'mod_data'),
];
$selectmenu = new \core\output\select_menu('presetsactions', $menu, $this->currenturl->out(false));
$selectmenu->set_label(get_string('templatesnavigation', 'mod_data'), ['class' => 'sr-only']);
$renderer = $PAGE->get_renderer('mod_data');
$presetsactions = $this->get_presets_actions_select(false);
// Reset single template action.
$resetcurrrent = new moodle_url($this->currenturl);
$resetcurrrent->param('action', 'resettemplate');
$presetsactions->add(new \action_menu_link(
$resetcurrrent,
null,
get_string('resettemplate', 'mod_data'),
false,
['data-action' => 'resettemplate', 'data-dataid' => $this->id]
));
// Reset all templates action.
$resetallurl = new moodle_url($this->currenturl);
$resetallurl->params([
'action' => 'resetalltemplates',
'sesskey' => sesskey(),
]);
$presetsactions->add(new \action_menu_link(
$resetallurl,
null,
get_string('resetalltemplates', 'mod_data'),
false,
['data-action' => 'resetalltemplates', 'data-dataid' => $this->id]
));
$templatesactionbar = new templates_action_bar($this->id, $selectmenu, null, null, $presetsactions);
return $renderer->render_templates_action_bar($templatesactionbar);
}
/**
* Generate the output for the action selector in the presets page.
*
* @return string The HTML code for the action selector.
*/
public function get_presets_action_bar(): string {
global $PAGE;
$renderer = $PAGE->get_renderer('mod_data');
$presetsactionbar = new presets_action_bar($this->cmid, $this->get_presets_actions_select(true));
return $renderer->render_presets_action_bar($presetsactionbar);
}
/**
* Generate the output for the action selector in the presets preview page.
*
* @param manager $manager the manager instance
* @param string $fullname the preset fullname
* @param string $current the current template name
* @return string The HTML code for the action selector
*/
public function get_presets_preview_action_bar(manager $manager, string $fullname, string $current): string {
global $PAGE;
$renderer = $PAGE->get_renderer(manager::PLUGINNAME);
$cm = $manager->get_coursemodule();
$menu = [];
$selected = null;
foreach (['listtemplate', 'singletemplate'] as $templatename) {
$link = new moodle_url('/mod/data/preset.php', [
'd' => $this->id,
'template' => $templatename,
'fullname' => $fullname,
'action' => 'preview',
]);
$menu[$link->out(false)] = get_string($templatename, manager::PLUGINNAME);
if (!$selected || $templatename == $current) {
$selected = $link->out(false);
}
}
$urlselect = new url_select($menu, $selected, null);
$urlselect->set_label(get_string('templatesnavigation', manager::PLUGINNAME), ['class' => 'sr-only']);
$data = [
'title' => get_string('preview', manager::PLUGINNAME, preset::get_name_from_plugin($fullname)),
'hasback' => true,
'backtitle' => get_string('back'),
'backurl' => new moodle_url('/mod/data/preset.php', ['id' => $cm->id]),
'extraurlselect' => $urlselect->export_for_template($renderer),
];
return $renderer->render_from_template('mod_data/action_bar', $data);
}
/**
* Helper method to get the selector for the presets action.
*
* @param bool $hasimport Whether the Import buttons must be included or not.
* @return \action_menu|null The selector object used to display the presets actions. Null when the import button is not
* displayed and the database hasn't any fields.
*/
protected function get_presets_actions_select(bool $hasimport = false): ?\action_menu {
global $DB;
$hasfields = $DB->record_exists('data_fields', ['dataid' => $this->id]);
// Early return if the database has no fields and the import action won't be displayed.
if (!$hasfields && !$hasimport) {
return null;
}
$actionsselect = new \action_menu();
$actionsselect->set_menu_trigger(get_string('actions'), 'btn btn-secondary');
if ($hasimport) {
// Import.
$actionsselectparams = ['id' => $this->cmid];
$actionsselect->add(new \action_menu_link(
new moodle_url('/mod/data/preset.php', $actionsselectparams),
null,
get_string('importpreset', 'mod_data'),
false,
['data-action' => 'importpresets', 'data-dataid' => $this->cmid]
));
}
// If the database has no fields, export and save as preset options shouldn't be displayed.
if ($hasfields) {
// Export.
$actionsselectparams = ['id' => $this->cmid, 'action' => 'export'];
$actionsselect->add(new \action_menu_link(
new moodle_url('/mod/data/preset.php', $actionsselectparams),
null,
get_string('exportpreset', 'mod_data'),
false
));
// Save as preset.
$actionsselect->add(new \action_menu_link(
new moodle_url('/mod/data/preset.php', $actionsselectparams),
null,
get_string('saveaspreset', 'mod_data'),
false,
['data-action' => 'saveaspreset', 'data-dataid' => $this->id]
));
}
return $actionsselect;
}
}
@@ -0,0 +1,67 @@
<?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\output;
use moodle_url;
use templatable;
use renderable;
/**
* Renderable class for the Add entries button in the database activity.
*
* @package mod_data
* @copyright 2022 Amaia Anabitarte <amaia@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class add_entries_action implements templatable, renderable {
/** @var int $id The database module id. */
private $id;
/**
* The class constructor.
*
* @param int $id The database module id.
* @param bool $hasentries Whether entries exist.
*/
public function __construct(int $id) {
$this->id = $id;
}
/**
* Export the data for the mustache template.
*
* @param \renderer_base $output The renderer to be used to render the add entries button.
* @return \stdClass or null if the user has no permission to add new entries.
*/
public function export_for_template(\renderer_base $output): ?\stdClass {
global $PAGE, $DB;
$database = $DB->get_record('data', ['id' => $this->id]);
$cm = get_coursemodule_from_instance('data', $this->id);
$currentgroup = groups_get_activity_group($cm);
$groupmode = groups_get_activity_groupmode($cm);
if (data_user_can_add_entry($database, $currentgroup, $groupmode, $PAGE->context)) {
$addentrylink = new moodle_url('/mod/data/edit.php', ['d' => $this->id, 'backto' => $PAGE->url->out(false)]);
$button = new \single_button($addentrylink, get_string('add', 'mod_data'), 'get', \single_button::BUTTON_PRIMARY);
return $button->export_for_template($output);
}
return null;
}
}
+102
View File
@@ -0,0 +1,102 @@
<?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\output;
use core_tag_tag;
use mod_data\manager;
use templatable;
use renderable;
/**
* Renderable class for the default templates in the database activity.
*
* @package mod_data
* @copyright 2022 Sara Arjona <sara@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class defaulttemplate implements templatable, renderable {
/** @var array $fields The array containing the existing fields. */
private $fields;
/** @var string $templatename The template name (addtemplate, listtemplate...). */
private $templatename;
/** @var bool $isform Whether a form should be displayed instead of data. */
private $isform;
/**
* The class constructor.
*
* @param array $fields The array containing the existing fields.
* @param string $templatename The template name (addtemplate, listtemplate...).
* @param bool $isform Whether a form should be displayed instead of data.
*/
public function __construct(array $fields, string $templatename, bool $isform) {
$this->fields = $fields;
$this->templatename = $templatename;
$this->isform = $isform;
}
/**
* Obtains the mustache template name for this database template.
*
* @return string the file mustache path for this template.
*/
public function get_templatename(): string {
return 'mod_data/defaulttemplate_' . $this->templatename;
}
/**
* Export the data for the mustache template.
*
* @param \renderer_base $output The renderer to be used to render the action bar elements.
* @return array The data to display.
*/
public function export_for_template(\renderer_base $output): array {
$result = [];
$exportedfields = [];
foreach ($this->fields as $field) {
$fieldname = $field->field->name;
if ($this->isform) {
$fieldcontent = $field->display_add_field();
} else {
$fieldcontent = '[[' . $fieldname . ']]';
}
$exportedfields[] = [
'fieldname' => $fieldname,
'fieldcontent' => $fieldcontent,
];
}
if (!empty($exportedfields)) {
$result['fields'] = $exportedfields;
}
if (core_tag_tag::is_enabled(manager::PLUGINNAME, 'data_records')) {
// Add tags information only if they are enabled.
if ($this->isform) {
$tags = data_generate_tag_form();
} else {
$tags = '##tags##';
}
$result['tags'] = $tags;
}
return $result;
}
}
@@ -0,0 +1,70 @@
<?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\output;
use mod_data\manager;
use moodle_url;
use templatable;
use renderable;
/**
* Renderable class for the action bar elements for an empty database activity.
*
* @package mod_data
* @copyright 2022 Amaia Anabitarte <amaia@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class empty_database_action_bar implements templatable, renderable {
/** @var manager The manager instance. */
protected $manager;
/**
* The class constructor.
*
* @param int $id The database module id.
*/
public function __construct(manager $manager) {
$this->manager = $manager;
}
/**
* Export the data for the mustache template.
*
* @param \renderer_base $output The renderer to be used to render the action bar elements.
* @return array
*/
public function export_for_template(\renderer_base $output): array {
global $PAGE;
$instance = $this->manager->get_instance();
$addentrybutton = new add_entries_action($instance->id);
$data = ['addentrybutton' => $addentrybutton->export_for_template($output)];
if (has_capability('mod/data:manageentries', $PAGE->context)) {
$params = ['d' => $instance->id, 'backto' => $PAGE->url->out(false)];
$importentrieslink = new moodle_url('/mod/data/import.php', $params);
$importentriesbutton = new \single_button($importentrieslink,
get_string('importentries', 'mod_data'), 'get');
$data['importentriesbutton'] = $importentriesbutton->export_for_template($output);
}
return $data;
}
}
@@ -0,0 +1,70 @@
<?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\output;
use templatable;
use renderable;
/**
* Renderable class for the action bar elements in the field pages in the database activity.
*
* @package mod_data
* @copyright 2021 Mihail Geshoski <mihail@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class fields_action_bar implements templatable, renderable {
/** @var int $id The database module id. */
private $id;
/**
* The class constructor.
*
* @param int $id The database module id
* @param null $unused1 This parameter has been deprecated since 4.1 and should not be used anymore.
* @param null $unused2 This parameter has been deprecated since 4.1 and should not be used anymore.
* @param null $unused3 This parameter has been deprecated since 4.1 and should not be used anymore.
* @param null $unused4 This parameter has been deprecated since 4.1 and should not be used anymore.
* @param \action_menu|null $unused5 This parameter has been deprecated since 4.2 and should not be used anymore.
*/
public function __construct(int $id, $unused1 = null, $unused2 = null,
$unused3 = null, $unused4 = null,
?\action_menu $unused5 = null) {
if ($unused1 !== null || $unused2 !== null || $unused3 !== null || $unused4 !== null || $unused5 !== null) {
debugging('Deprecated argument passed to fields_action_bar constructor', DEBUG_DEVELOPER);
}
$this->id = $id;
}
/**
* Export the data for the mustache template.
*
* @param \renderer_base $output The renderer to be used to render the action bar elements.
* @return array
*/
public function export_for_template(\renderer_base $output): array {
$data = [
'd' => $this->id,
'title' => get_string('managefields', 'mod_data'),
];
return $data;
}
}
@@ -0,0 +1,57 @@
<?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\output;
use templatable;
use renderable;
/**
* Renderable class for the action bar elements in the fields mapping page in the database activity.
*
* @package mod_data
* @copyright 2022 Amaia Anabitarte <amaia@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class fields_mappings_action_bar implements templatable, renderable {
/** @var int $id The database module id. */
private $id;
/**
* The class constructor.
*
* @param int $id The database module id
*/
public function __construct(int $id) {
$this->id = $id;
}
/**
* Export the data for the mustache template.
*
* @param \renderer_base $output The renderer to be used to render the action bar elements.
* @return array
*/
public function export_for_template(\renderer_base $output): array {
return [
'tertiarytitle' => get_string('fieldmappings', 'mod_data'),
'hasback' => true,
'backtitle' => get_string('back'),
'backurl' => new \moodle_url('/mod/data/preset.php', ['d' => $this->id]),
];
}
}
+118
View File
@@ -0,0 +1,118 @@
<?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\output;
use templatable;
use renderable;
use mod_data\manager;
use mod_data\preset;
use mod_data\template;
use moodle_page;
use moodle_url;
/**
* Preset preview output class.
*
* @package mod_data
* @copyright 2022 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class preset_preview implements templatable, renderable {
/** @var manager manager instance. */
private $manager;
/** @var preset the preset. */
private $preset;
/** @var string the template to preview. */
private $templatename;
/**
* The class constructor.
*
* @param manager $manager the activity instance manager
* @param preset $preset the preset
* @param string $templatename the templatename
*/
public function __construct(manager $manager, preset $preset, string $templatename) {
$this->manager = $manager;
$this->preset = $preset;
$this->templatename = $templatename;
}
/**
* Add the preset CSS and JS to the page.
*
* @param moodle_page $page the current page instance
*/
public function prepare_page(moodle_page $page) {
$instance = $this->manager->get_instance();
$preset = $this->preset;
// Add CSS and JS.
$csscontent = $preset->get_template_content('csstemplate');
if (!empty($csscontent)) {
$url = new moodle_url('/mod/data/css.php', ['d' => $instance->id, 'preset' => $preset->get_fullname()]);
$page->requires->css($url);
}
$jscontent = $preset->get_template_content('jstemplate');
if (!empty($jscontent)) {
$url = new moodle_url('/mod/data/js.php', ['d' => $instance->id, 'preset' => $preset->get_fullname()]);
$page->requires->js($url);
}
}
/**
* Export the data for the mustache template.
*
* @param \renderer_base $output renderer to be used to render the action bar elements.
* @return array
*/
public function export_for_template(\renderer_base $output): array {
$coursemodule = $this->manager->get_coursemodule();
$preset = $this->preset;
// Get fields for preview.
$count = ($this->templatename == 'listtemplate') ? 2 : 1;
$fields = $preset->get_fields(true);
$entries = $preset->get_sample_entries($count);
$templatecontent = $preset->get_template_content($this->templatename);
$useurl = new moodle_url('/mod/data/field.php');
// Generate preview content.
$options = ['templatename' => $this->templatename];
if ($this->templatename == 'listtemplate') {
$options['showmore'] = true;
}
$parser = new template($this->manager, $templatecontent, $options, $fields);
$content = $parser->parse_entries($entries);
if ($this->templatename == 'listtemplate') {
$listtemplateheader = $preset->get_template_content('listtemplateheader');
$listtemplatefooter = $preset->get_template_content('listtemplatefooter');
$content = $listtemplateheader . $content . $listtemplatefooter;
}
return [
'cmid' => $coursemodule->id,
'description' => $preset->description ?? '',
'preview' => $content,
'formactionurl' => $useurl->out(),
'userid' => $preset->get_userid() ?? 0,
'shortname' => $preset->shortname,
];
}
}
+238
View File
@@ -0,0 +1,238 @@
<?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\output;
use action_menu;
use action_menu_link_secondary;
use mod_data\manager;
use mod_data\preset;
use moodle_url;
use templatable;
use renderable;
use renderer_base;
use stdClass;
/**
* Renderable class for the presets table in the database activity.
*
* @package mod_data
* @copyright 2021 Mihail Geshoski <mihail@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class presets implements templatable, renderable {
/** @var array $presets The array containing the existing presets. */
private $presets;
/** @var moodle_url $formactionurl The action url for the form. */
private $formactionurl;
/** @var bool $manage Whether the manage preset options should be displayed. */
private $manage;
/** @var int $id instance id */
private $id;
/** @var int $cmid course module id */
private $cmid;
/**
* The class constructor.
*
* @param manager $manager The database manager
* @param array $presets The array containing the existing presets
* @param moodle_url $formactionurl The action url for the form
* @param bool $manage Whether the manage preset options should be displayed
*/
public function __construct(manager $manager, array $presets, moodle_url $formactionurl, bool $manage = false) {
$this->id = $manager->get_instance()->id;
$this->cmid = $manager->get_coursemodule()->id;
$this->presets = $presets;
$this->formactionurl = $formactionurl;
$this->manage = $manage;
}
/**
* Export the data for the mustache template.
*
* @param renderer_base $output The renderer to be used to render the action bar elements.
* @return array
*/
public function export_for_template(renderer_base $output): array {
$presets = $this->get_presets($output);
return [
'id' => $this->cmid,
'formactionurl' => $this->formactionurl->out(),
'showmanage' => $this->manage,
'presets' => $presets,
];
}
/**
* Returns the presets list with the information required to display them.
*
* @param renderer_base $output The renderer to be used to render the action bar elements.
* @return array Presets list.
*/
private function get_presets(renderer_base $output): array {
$presets = [];
foreach ($this->presets as $index => $preset) {
$presetname = $preset->name;
$userid = $preset instanceof preset ? $preset->get_userid() : $preset->userid;
if (!empty($userid)) {
// If the preset has the userid field, the full name of creator it will be added to the end of the name.
$userfieldsapi = \core_user\fields::for_name();
$namefields = $userfieldsapi->get_sql('', false, '', '', false)->selects;
$fields = 'id, ' . $namefields;
$presetuser = \core_user::get_user($userid, $fields, MUST_EXIST);
$username = fullname($presetuser, true);
$presetname = "{$presetname} ({$username})";
}
$actions = $this->get_preset_action_menu($output, $preset, $userid);
$fullname = $preset->get_fullname();
$previewurl = new moodle_url(
'/mod/data/preset.php',
['d' => $this->id, 'fullname' => $fullname, 'action' => 'preview']
);
$presets[] = [
'id' => $this->id,
'cmid' => $this->cmid,
'name' => $preset->name,
'url' => $previewurl->out(),
'shortname' => $preset->shortname,
'fullname' => $presetname,
'description' => $preset->description,
'userid' => $userid,
'actions' => $actions,
'presetindex' => $index,
];
}
return $presets;
}
/**
* Return the preset action menu data.
*
* @param renderer_base $output The renderer to be used to render the action bar elements.
* @param preset|stdClass $preset the preset object
* @param int|null $userid the user id (null for plugin presets)
* @return stdClass the resulting action menu
*/
private function get_preset_action_menu(renderer_base $output, $preset, ?int $userid): stdClass {
$actions = new stdClass();
// If we cannot manage then return an empty menu.
if (!$this->manage) {
return $actions;
}
$actionmenu = new action_menu();
$actionmenu->set_kebab_trigger();
$actionmenu->set_action_label(get_string('actions'));
$actionmenu->set_additional_classes('presets-actions');
$canmanage = $preset->can_manage();
$usepreseturl = new moodle_url('/mod/data/preset.php', [
'action' => 'usepreset',
'cmid' => $this->cmid,
]);
$this->add_action_menu($actionmenu, get_string('usepreset', 'mod_data'), $usepreseturl, [
'data-action' => 'selectpreset',
'data-presetname' => $preset->get_fullname(),
'data-cmid' => $this->cmid,
]
);
// Attention: the id here is the cm->id, not d->id.
$previewpreseturl = new moodle_url('/mod/data/preset.php', [
'fullname' => $preset->get_fullname(),
'action' => 'preview',
'id' => $this->cmid,
]);
$this->add_action_menu($actionmenu, get_string('previewaction', 'mod_data'), $previewpreseturl, [
'data-action' => 'preview',
]
);
// Presets saved by users can be edited or removed.
if (!$preset->isplugin) {
// Edit.
if ($canmanage) {
$editactionurl = new moodle_url('/mod/data/preset.php', [
'action' => 'edit',
'd' => $this->id,
]);
$this->add_action_menu($actionmenu, get_string('edit'), $editactionurl, [
'data-action' => 'editpreset',
'data-presetname' => $preset->name,
'data-presetdescription' => $preset->description,
]);
}
// Export.
$exporturl = new moodle_url('/mod/data/preset.php', [
'presetname' => $preset->name,
'action' => 'export',
'd' => $this->id,
]);
$this->add_action_menu($actionmenu, get_string('export', 'mod_data'), $exporturl, [
'data-action' => 'exportpreset',
'data-presetname' => $preset->name,
'data-presetdescription' => $preset->description,
]);
// Delete.
if ($canmanage) {
$deleteactionurl = new moodle_url('/mod/data/preset.php', [
'action' => 'delete',
'd' => $this->id,
]);
$this->add_action_menu($actionmenu, get_string('delete'), $deleteactionurl, [
'data-action' => 'deletepreset',
'data-presetname' => $preset->name,
]);
}
}
$actions = $actionmenu->export_for_template($output);
return $actions;
}
/**
* Add action to the action menu
*
* @param action_menu $actionmenu
* @param string $actionlabel
* @param moodle_url $actionurl
* @param array $otherattributes
* @return void
*/
private function add_action_menu(action_menu &$actionmenu, string $actionlabel, moodle_url $actionurl,
array $otherattributes) {
$attributes = [
'data-dataid' => $this->id,
];
$actionmenu->add(new action_menu_link_secondary(
$actionurl,
null,
$actionlabel,
array_merge($attributes, $otherattributes),
));
}
}
@@ -0,0 +1,65 @@
<?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\output;
use templatable;
use renderable;
/**
* Renderable class for the action bar elements in the presets page in the database activity.
*
* @package mod_data
* @copyright 2021 Mihail Geshoski <mihail@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class presets_action_bar implements templatable, renderable {
/** @var int $id The database module id. */
private $cmid;
/** @var \action_menu $actionsselect The presets actions selector object. */
private $actionsselect;
/**
* The class constructor.
*
* @param int $cmid The database module id
* @param \action_menu|null $actionsselect The presets actions selector object.
*/
public function __construct(int $cmid, ?\action_menu $actionsselect) {
$this->cmid = $cmid;
$this->actionsselect = $actionsselect;
}
/**
* Export the data for the mustache template.
*
* @param \renderer_base $output The renderer to be used to render the action bar elements.
* @return array
*/
public function export_for_template(\renderer_base $output): array {
$data = [
'id' => $this->cmid,
];
if ($this->actionsselect) {
$data['actionsselect'] = $this->actionsselect->export_for_template($output);
}
return $data;
}
}
+193
View File
@@ -0,0 +1,193 @@
<?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\output;
use templatable;
use renderable;
use mod_data\manager;
use moodle_url;
use texteditor;
/**
* Renderable class for template editor.
*
* @package mod_data
* @copyright 2022 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class template_editor implements templatable, renderable {
/** @var manager manager instance. */
private $manager;
/** @var string the template name. */
private $templatename;
/**
* The class constructor.
*
* @param manager $manager the activity instance manager
* @param string $templatename the template to edit
*/
public function __construct(manager $manager, string $templatename) {
$this->manager = $manager;
$this->templatename = $templatename;
}
/**
* Export the data for the mustache template.
*
* @param \renderer_base $output renderer to be used to render the action bar elements.
* @return array
*/
public function export_for_template(\renderer_base $output): array {
$instance = $this->manager->get_instance();
$cm = $this->manager->get_coursemodule();
$data = [
'title' => get_string('header' . $this->templatename, 'data'),
'sesskey' => sesskey(),
'disableeditor' => true,
'url' => new moodle_url('/mod/data/templates.php', ['id' => $cm->id, 'mode' => $this->templatename]),
];
// Determine whether to use HTML editors.
$usehtmleditor = false;
$disableeditor = false;
if (($this->templatename !== 'csstemplate') && ($this->templatename !== 'jstemplate')) {
$usehtmleditor = data_get_config($instance, "editor_{$this->templatename}", true);
$disableeditor = true;
}
$data['usehtmleditor'] = $usehtmleditor;
// Some templates, like CSS, cannot enable the wysiwyg editor.
$data['disableeditor'] = $disableeditor;
$tools = new template_editor_tools($this->manager, $this->templatename);
$data['toolbar'] = $tools->export_for_template($output);
$data['editors'] = $this->get_editors_data($usehtmleditor);
return $data;
}
/**
* Get the editors data.
*
* @param bool $usehtmleditor if the user wants wysiwyg editor or not
* @return array editors data
*/
private function get_editors_data(bool $usehtmleditor): array {
global $PAGE;
$result = [];
$manager = $this->manager;
$instance = $manager->get_instance();
// Setup editor.
editors_head_setup();
$PAGE->requires->js_call_amd(
'mod_data/templateseditor',
'init',
['d' => $instance->id, 'mode' => $this->templatename]
);
$format = FORMAT_PLAIN;
if ($usehtmleditor) {
$format = FORMAT_HTML;
}
$editor = editors_get_preferred_editor($format);
// Add editors.
if ($this->templatename === 'listtemplate') {
$template = $manager->get_template('listtemplateheader');
$result[] = $this->generate_editor_data(
$editor,
'header',
'listtemplateheader',
$template->get_template_content()
);
$maineditorname = 'multientry';
} else {
$maineditorname = $this->templatename;
}
$template = $manager->get_template($this->templatename);
$result[] = $this->generate_editor_data(
$editor,
$maineditorname,
$this->templatename,
$template->get_template_content()
);
if ($this->templatename === 'listtemplate') {
$template = $manager->get_template('listtemplatefooter');
$result[] = $this->generate_editor_data(
$editor,
'footer',
'listtemplatefooter',
$template->get_template_content()
);
}
if ($this->templatename === 'rsstemplate') {
$template = $manager->get_template('rsstitletemplate');
$result[] = $this->generate_editor_data(
$editor,
'rsstitletemplate',
'rsstitletemplate',
$template->get_template_content()
);
}
return $result;
}
/**
* Generate a single editor data.
*
* @param texteditor $editor the editor object
* @param string $name the editor name
* @param string $fieldname the field name
* @param string|null $value the current value
* @return array the editor data
*/
private function generate_editor_data(
texteditor $editor,
string $name,
string $fieldname,
?string $value
): array {
$options = [
'trusttext' => false,
'forcehttps' => false,
'subdirs' => false,
'maxfiles' => 0,
'maxbytes' => 0,
'changeformat' => 0,
'noclean' => false,
];
$result = [
'name' => get_string($name, 'data'),
'fieldname' => $fieldname,
'value' => $value,
];
$editor->set_text($value);
$editor->use_editor($fieldname, $options);
return $result;
}
}
@@ -0,0 +1,206 @@
<?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\output;
use templatable;
use renderable;
use core_tag_tag;
use mod_data\manager;
/**
* Renderable class for template editor tools.
*
* @package mod_data
* @copyright 2022 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class template_editor_tools implements templatable, renderable {
/** @var manager manager instance. */
private $manager;
/** @var string the template name. */
private $templatename;
/**
* The class constructor.
*
* @param manager $manager the activity instance manager
* @param string $templatename the template to edit
*/
public function __construct(manager $manager, string $templatename) {
$this->manager = $manager;
$this->templatename = $templatename;
}
/**
* Export the data for the mustache template.
*
* @param \renderer_base $output renderer to be used to render the action bar elements.
* @return array
*/
public function export_for_template(\renderer_base $output): array {
$tools = [
$this->get_field_tags($this->templatename),
$this->get_field_info_tags($this->templatename),
$this->get_action_tags($this->templatename),
$this->get_other_tags($this->templatename),
];
$tools = array_filter($tools, static function ($value) {
return !empty($value['tags']);
});
return [
'toolshelp' => $output->help_icon('availabletags', 'data'),
'hastools' => !empty($tools),
'tools' => array_values($tools),
];
}
/**
* Return the field template tags.
*
* @param string $templatename the template name
* @return array|null array of tags.
*/
protected function get_field_tags(string $templatename): array {
$name = get_string('fields', 'data');
if ($templatename == 'csstemplate' || $templatename == 'jstemplate') {
return $this->get_optgroup_data($name, []);
}
$taglist = [];
$fields = $this->manager->get_fields();
foreach ($fields as $field) {
if ($field->type === 'unknown') {
continue;
}
$fieldname = $field->get_name();
$taglist["[[$fieldname]]"] = $fieldname;
}
$taglist['##otherfields##'] = get_string('otherfields', 'data');
return $this->get_optgroup_data($name, $taglist);
}
/**
* Return the field information template tags.
*
* @param string $templatename the template name
* @return array|null array of tags.
*/
protected function get_field_info_tags(string $templatename): array {
$name = get_string('fieldsinformationtags', 'data');
$taglist = [];
$fields = $this->manager->get_fields();
foreach ($fields as $field) {
if ($field->type === 'unknown') {
continue;
}
$fieldname = $field->get_name();
if ($templatename == 'addtemplate') {
$taglist["[[$fieldname#id]]"] = get_string('fieldtagid', 'mod_data', $fieldname);
}
$taglist["[[$fieldname#name]]"] = get_string('fieldtagname', 'mod_data', $fieldname);
$taglist["[[$fieldname#description]]"] = get_string('fieldtagdescription', 'mod_data', $fieldname);
}
return $this->get_optgroup_data($name, $taglist);
}
/**
* Return the field action tags.
*
* @param string $templatename the template name
* @return array|null array of tags.
*/
protected function get_action_tags(string $templatename): array {
$name = get_string('actions');
if ($templatename == 'addtemplate' || $templatename == 'asearchtemplate') {
return $this->get_optgroup_data($name, []);
}
$taglist = [
'##actionsmenu##' => get_string('actionsmenu', 'data'),
'##edit##' => get_string('edit', 'data'),
'##delete##' => get_string('delete', 'data'),
'##approve##' => get_string('approve', 'data'),
'##disapprove##' => get_string('disapprove', 'data'),
];
if ($templatename != 'rsstemplate') {
$taglist['##export##'] = get_string('export', 'data');
}
if ($templatename != 'singletemplate') {
$taglist['##more##'] = get_string('more', 'data');
$taglist['##moreurl##'] = get_string('moreurl', 'data');
$taglist['##delcheck##'] = get_string('delcheck', 'data');
}
return $this->get_optgroup_data($name, $taglist);
}
/**
* Return the available other tags
*
* @param string $templatename the template name
* @return array associative array of tags => tag name
*/
protected function get_other_tags(string $templatename): array {
$name = get_string('other', 'data');
$taglist = [];
if ($templatename == 'asearchtemplate') {
$taglist['##firstname##'] = get_string('firstname');
$taglist['##lastname##'] = get_string('lastname');
return $this->get_optgroup_data($name, $taglist);
}
if (core_tag_tag::is_enabled('mod_data', 'data_records')) {
$taglist['##tags##'] = get_string('tags');
}
if ($templatename == 'addtemplate') {
return $this->get_optgroup_data($name, $taglist);
}
$taglist['##timeadded##'] = get_string('timeadded', 'data');
$taglist['##timemodified##'] = get_string('timemodified', 'data');
$taglist['##user##'] = get_string('user');
$taglist['##userpicture##'] = get_string('userpic');
$taglist['##approvalstatus##'] = get_string('approvalstatus', 'data');
$taglist['##id##'] = get_string('id', 'data');
if ($templatename == 'singletemplate') {
return $this->get_optgroup_data($name, $taglist);
}
$taglist['##comments##'] = get_string('comments', 'data');
return $this->get_optgroup_data($name, $taglist);
}
/**
* Generate a valid optgroup data.
*
* @param string $name the optgroup name
* @param array $taglist the indexed array of taglists ($tag => $tagname)
* @return array of optgroup data
*/
protected function get_optgroup_data(string $name, array $taglist): array {
$tags = [];
foreach ($taglist as $tag => $tagname) {
$tags[] = [
'tag' => "$tag",
'tagname' => $tagname . ' - ' . $tag,
];
}
return [
'name' => $name,
'tags' => $tags,
];
}
}
@@ -0,0 +1,70 @@
<?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\output;
use core\output\select_menu;
use templatable;
use renderable;
/**
* Renderable class for the action bar elements in the template pages in the database activity.
*
* @package mod_data
* @copyright 2021 Mihail Geshoski <mihail@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class templates_action_bar implements templatable, renderable {
/** @var int $id The database module id. */
private $id;
/** @var select_menu $selectmenu The URL selector object. */
private $selectmenu;
/** @var \action_menu $actionsselect The presets actions selector object. */
private $actionsselect;
/**
* The class constructor.
*
* @param int $id The database module id.
* @param select_menu $selectmenu The URL selector object.
* @param null $unused1 This parameter has been deprecated since 4.1 and should not be used anymore.
* @param null $unused2 This parameter has been deprecated since 4.1 and should not be used anymore.
* @param \action_menu $actionsselect The presets actions selector object.
*/
public function __construct(int $id, select_menu $selectmenu, $unused1, $unused2, \action_menu $actionsselect) {
$this->id = $id;
$this->selectmenu = $selectmenu;
$this->actionsselect = $actionsselect;
}
/**
* Export the data for the mustache template.
*
* @param \renderer_base $output renderer to be used to render the action bar elements.
* @return array
*/
public function export_for_template(\renderer_base $output): array {
return [
'd' => $this->id,
'selectmenu' => $this->selectmenu->export_for_template($output),
'actionsselect' => $this->actionsselect->export_for_template($output),
];
}
}
+148
View File
@@ -0,0 +1,148 @@
<?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\output;
use data_portfolio_caller;
use mod_data\manager;
use moodle_url;
use portfolio_add_button;
use templatable;
use renderable;
/**
* Renderable class for the action bar elements in the view pages in the database activity.
*
* @package mod_data
* @copyright 2021 Mihail Geshoski <mihail@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class view_action_bar implements templatable, renderable {
/** @var int $id The database module id. */
private $id;
/** @var \url_select $urlselect The URL selector object. */
private $urlselect;
/** @var bool $hasentries Whether entries exist. */
private $hasentries;
/** @var bool $mode The current view mode (list, view...). */
private $mode;
/**
* The class constructor.
*
* @param int $id The database module id.
* @param \url_select $urlselect The URL selector object.
* @param bool $hasentries Whether entries exist.
* @param string $mode The current view mode (list, view...).
*/
public function __construct(int $id, \url_select $urlselect, bool $hasentries, string $mode) {
$this->id = $id;
$this->urlselect = $urlselect;
$this->hasentries = $hasentries;
$this->mode = $mode;
}
/**
* Export the data for the mustache template.
*
* @param \renderer_base $output The renderer to be used to render the action bar elements.
* @return array
*/
public function export_for_template(\renderer_base $output): array {
global $PAGE, $DB, $CFG;
$data = [
'urlselect' => $this->urlselect->export_for_template($output),
];
$activity = $DB->get_record('data', ['id' => $this->id], '*', MUST_EXIST);
$manager = manager::create_from_instance($activity);
$actionsselect = null;
// Import entries.
if (has_capability('mod/data:manageentries', $manager->get_context())) {
$actionsselect = new \action_menu();
$actionsselect->set_menu_trigger(get_string('actions'), 'btn btn-secondary');
$importentrieslink = new moodle_url('/mod/data/import.php', ['d' => $this->id, 'backto' => $PAGE->url->out(false)]);
$actionsselect->add(new \action_menu_link(
$importentrieslink,
null,
get_string('importentries', 'mod_data'),
false
));
}
// Export entries.
if (has_capability(DATA_CAP_EXPORT, $manager->get_context()) && $this->hasentries) {
if (!$actionsselect) {
$actionsselect = new \action_menu();
$actionsselect->set_menu_trigger(get_string('actions'), 'btn btn-secondary');
}
$exportentrieslink = new moodle_url('/mod/data/export.php', ['d' => $this->id, 'backto' => $PAGE->url->out(false)]);
$actionsselect->add(new \action_menu_link(
$exportentrieslink,
null,
get_string('exportentries', 'mod_data'),
false
));
}
// Export to portfolio. This is for exporting all records, not just the ones in the search.
if ($this->mode == '' && !empty($CFG->enableportfolios) && $this->hasentries) {
if ($manager->can_export_entries()) {
// Add the portfolio export button.
require_once($CFG->libdir . '/portfoliolib.php');
$cm = $manager->get_coursemodule();
$button = new portfolio_add_button();
$button->set_callback_options(
'data_portfolio_caller',
['id' => $cm->id],
'mod_data'
);
if (data_portfolio_caller::has_files($activity)) {
// No plain HTML.
$button->set_formats([PORTFOLIO_FORMAT_RICHHTML, PORTFOLIO_FORMAT_LEAP2A]);
}
$exporturl = $button->to_html(PORTFOLIO_ADD_MOODLE_URL);
if (!is_null($exporturl)) {
if (!$actionsselect) {
$actionsselect = new \action_menu();
$actionsselect->set_menu_trigger(get_string('actions'), 'btn btn-secondary');
}
$actionsselect->add(new \action_menu_link(
$exporturl,
null,
get_string('addtoportfolio', 'portfolio'),
false
));
}
}
}
if ($actionsselect) {
$data['actionsselect'] = $actionsselect->export_for_template($output);
}
return $data;
}
}
+156
View File
@@ -0,0 +1,156 @@
<?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\output;
use action_link;
use core\output\sticky_footer;
use html_writer;
use mod_data\manager;
use mod_data\template;
use moodle_url;
use renderer_base;
/**
* Renderable class for sticky footer in the view pages of the 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 view_footer extends sticky_footer {
/** @var int $totalcount the total records count. */
private $totalcount;
/** @var int $currentpage the current page */
private $currentpage;
/** @var int $nowperpage the number of elements per page */
private $nowperpage;
/** @var moodle_url $baseurl the page base url */
private $baseurl;
/** @var template $parser the template name */
private $parser;
/** @var manager $manager if the user can manage capabilities or not */
private $manager;
/**
* The class constructor.
*
* @param manager $manager the activity manager
* @param int $totalcount the total records count
* @param int $currentpage the current page
* @param int $nowperpage the number of elements per page
* @param moodle_url $baseurl the page base url
* @param template $parser the current template name
*/
public function __construct(
manager $manager,
int $totalcount,
int $currentpage,
int $nowperpage,
moodle_url $baseurl,
template $parser
) {
$this->manager = $manager;
$this->totalcount = $totalcount;
$this->currentpage = $currentpage;
$this->nowperpage = $nowperpage;
$this->baseurl = $baseurl;
$this->parser = $parser;
}
/**
* Export this data so it can be used as the context for a mustache template (core/inplace_editable).
*
* @param renderer_base $output typically, the renderer that's calling this function
* @return array data context for a mustache template
*/
public function export_for_template(renderer_base $output) {
$this->set_content(
$this->get_footer_output($output)
);
return parent::export_for_template($output);
}
/**
* Generate the pre-rendered footer content.
*
* @param \renderer_base $output The renderer to be used to render the action bar elements.
* @return string the rendered content
*/
public function get_footer_output(renderer_base $output): string {
$data = [];
$cm = $this->manager->get_coursemodule();
$instance = $this->manager->get_instance();
$currentgroup = groups_get_activity_group($cm);
$groupmode = groups_get_activity_groupmode($cm);
$context = $this->manager->get_context();
$canmanageentries = has_capability('mod/data:manageentries', $context);
$parser = $this->parser;
// Sticky footer content.
$data['pagination'] = $output->paging_bar(
$this->totalcount,
$this->currentpage,
$this->nowperpage,
$this->baseurl
);
if ($parser->get_template_name() != 'singletemplate' && $parser->has_tag('delcheck') && $canmanageentries) {
// Build the select/deselect all control.
$selectallid = 'selectall-listview-entries';
$togglegroup = 'listview-entries';
$mastercheckbox = new \core\output\checkbox_toggleall($togglegroup, true, [
'id' => $selectallid,
'name' => $selectallid,
'value' => 1,
'label' => get_string('selectall'),
'classes' => 'btn-secondary mx-1',
], true);
$data['selectall'] = $output->render($mastercheckbox);
$data['deleteselected'] = html_writer::empty_tag('input', [
'class' => 'btn btn-secondary mx-1',
'type' => 'submit',
'value' => get_string('deleteselected'),
'disabled' => true,
'data-action' => 'toggle',
'data-togglegroup' => $togglegroup,
'data-toggle' => 'action',
]);
}
if (data_user_can_add_entry($instance, $currentgroup, $groupmode, $context)) {
$addentrylink = new moodle_url(
'/mod/data/edit.php',
['id' => $cm->id, 'backto' => $this->baseurl]
);
$addentrybutton = new action_link(
$addentrylink,
get_string('add', 'mod_data'),
null,
['class' => 'btn btn-primary mx-1', 'role' => 'button']
);
$data['addentrybutton'] = $addentrybutton->export_for_template($output);
}
return $output->render_from_template('mod_data/view_footer', $data);
}
}
@@ -0,0 +1,80 @@
<?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\output;
use mod_data\manager;
use moodle_url;
use templatable;
use renderable;
/**
* Renderable class for the action bar elements in the zero state (no fields created) pages in the database activity.
*
* @package mod_data
* @copyright 2022 Amaia Anabitarte <amaia@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class zero_state_action_bar implements templatable, renderable {
/** @var manager The manager instance. */
protected $manager;
/**
* The class constructor.
*
* @param manager $manager The manager instance.
*/
public function __construct(manager $manager) {
$this->manager = $manager;
}
/**
* Export the data for the mustache template.
*
* @param \renderer_base $output The renderer to be used to render the action bar elements.
* @return array
*/
public function export_for_template(\renderer_base $output): array {
global $PAGE;
$data = [];
if ($this->manager->can_manage_templates()) {
$cm = $this->manager->get_coursemodule();
$instance = $this->manager->get_instance();
$params = ['id' => $cm->id, 'backto' => $PAGE->url->out(false)];
$usepresetlink = new moodle_url('/mod/data/preset.php', $params);
$usepresetbutton = new \single_button($usepresetlink,
get_string('usestandard', 'mod_data'), 'get', \single_button::BUTTON_PRIMARY);
$data['usepresetbutton'] = $usepresetbutton->export_for_template($output);
$actionbar = new \mod_data\output\action_bar($instance->id, $PAGE->url);
$createfieldbutton = $actionbar->get_create_fields();
$data['createfieldbutton'] = $createfieldbutton->export_for_template($output);
$importpresetlink = new moodle_url('/mod/data/preset.php', $params);
$importpresetbutton = new \single_button($importpresetlink,
get_string('importapreset', 'mod_data'), 'get', \single_button::BUTTON_SECONDARY, [
'data-action' => 'importpresets',
'data-dataid' => $cm->id,
]);
$data['importpresetbutton'] = $importpresetbutton->export_for_template($output);
}
return $data;
}
}
+36
View File
@@ -0,0 +1,36 @@
<?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/>.
/**
* Subplugin info class.
*
* @package mod_data
* @copyright 2013 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\plugininfo;
use core\plugininfo\base;
defined('MOODLE_INTERNAL') || die();
class datafield extends base {
public function is_uninstall_allowed() {
global $DB;
return !$DB->record_exists('data_fields', array('type'=>$this->name));
}
}
@@ -0,0 +1,35 @@
<?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/>.
/**
* Subplugin info class.
*
* @package mod_data
* @copyright 2013 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\plugininfo;
use core\plugininfo\base;
defined('MOODLE_INTERNAL') || die();
class datapreset extends base {
public function is_uninstall_allowed() {
return true;
}
}
+827
View File
@@ -0,0 +1,827 @@
<?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 core_component;
use invalid_parameter_exception;
use data_field_base;
use moodle_exception;
use SimpleXMLElement;
use stdClass;
use stored_file;
/**
* Class preset for database activity.
*
* @package mod_data
* @copyright 2022 Sara Arjona <sara@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class preset {
/** @var manager manager instance. */
private $manager;
/** @var bool whether the preset is a plugin or has been saved by the user. */
public $isplugin;
/** @var string The preset name. */
public $name;
/** @var string The preset shortname. For datapreset plugins that is the folder; for saved presets, that's the preset name. */
public $shortname;
/** @var string The preset description. */
public $description;
/** @var stored_file For saved presets that's the file object for the root folder. It's null for plugins or for presets that
* haven't been saved yet. */
public $storedfile;
/** @var array|null the field sample instances. */
private $fields = null;
/** @var stdClass|null the preset.xml parsed information. */
protected $xmlinfo = null;
/**
* Class constructor.
*
* @param manager|null $manager the current instance manager
* @param bool $isplugin whether the preset is a plugin or has been saved by the user
* @param string $name the preset name
* @param string $shortname the preset shortname
* @param string|null $description the preset description
* @param stored_file|null $storedfile for saved presets, that's the file for the root folder
* @throws invalid_parameter_exception
*/
protected function __construct(
?manager $manager,
bool $isplugin,
string $name,
string $shortname,
?string $description = '',
?stored_file $storedfile = null
) {
if (!$isplugin && is_null($manager)) {
throw new invalid_parameter_exception('The $manager parameter can only be null for plugin presets.');
}
$this->manager = $manager;
$this->isplugin = $isplugin;
$this->name = $name;
$this->shortname = $shortname;
$this->description = $description;
$this->storedfile = $storedfile;
}
/**
* Create a preset instance from a stored file.
*
* @param manager $manager the current instance manager
* @param stored_file $file the preset root folder
* @return preset|null If the given file doesn't belong to the expected component/filearea/context, null will be returned
*/
public static function create_from_storedfile(manager $manager, stored_file $file): ?self {
if ($file->get_component() != DATA_PRESET_COMPONENT
|| $file->get_filearea() != DATA_PRESET_FILEAREA
|| $file->get_contextid() != DATA_PRESET_CONTEXT) {
return null;
}
$isplugin = false;
$name = trim($file->get_filepath(), '/');
$description = static::get_attribute_value($file->get_filepath(), 'description');
return new self($manager, $isplugin, $name, $name, $description, $file);
}
/**
* Create a preset instance from a plugin.
*
* @param manager|null $manager the current instance manager
* @param string $pluginname the datapreset plugin name
* @return preset|null The plugin preset or null if there is no datapreset plugin with the given name.
*/
public static function create_from_plugin(?manager $manager, string $pluginname): ?self {
$found = false;
$plugins = array_keys(core_component::get_plugin_list('datapreset'));
foreach ($plugins as $plugin) {
if ($plugin == $pluginname) {
$found = true;
break;
}
}
if (!$found) {
// If there is no datapreset plugin with this name, return null.
return null;
}
$name = static::get_name_from_plugin($pluginname);
$description = static::get_description_from_plugin($pluginname);
return new self($manager, true, $name, $pluginname, $description);
}
/**
* Create a preset instance from a data_record entry, a preset name and a description.
*
* @param manager $manager the current instance manager
* @param string $presetname the preset name
* @param string|null $description the preset description
* @return preset
*/
public static function create_from_instance(manager $manager, string $presetname, ?string $description = ''): self {
$isplugin = false;
$path = '/' . $presetname . '/';
$file = static::get_file($path, '.');
if (!is_null($file)) {
// If the file is not empty, create the instance based on the storedfile.
return self::create_from_storedfile($manager, $file);
}
return new self($manager, $isplugin, $presetname, $presetname, $description, $file);
}
/**
* Create a preset instance from the preset fullname.
*
* The preset fullname is a concatenation of userid and pluginname|presetname used by most
* preset pages. Plugins uses userid zero while preset instances has the owner as identifier.
*
* This method will throw an exception if the preset instance has a different userid thant the one
* from the $fullname. However, it won't check the current user capabilities.
*
* @param manager $manager the current instance manager
* @param string $fullname the preset full name
* @return preset
*/
public static function create_from_fullname(manager $manager, string $fullname): self {
$parts = explode('/', $fullname, 2);
$userid = empty($parts[0]) ? 0 : (int)$parts[0];
$shortname = empty($parts[1]) ? '' : $parts[1];
// Shortnames with userid zero are plugins.
if ($userid == 0) {
return static::create_from_plugin($manager, $shortname);
}
$path = '/' . $shortname . '/';
$file = static::get_file($path, '.');
$result = static::create_from_storedfile($manager, $file);
if ($result->get_userid() != $userid) {
throw new moodle_exception('invalidpreset', manager::PLUGINNAME);
}
return $result;
}
/**
* Save this preset.
*
* @return bool true if the preset has been saved; false otherwise.
*/
public function save(): bool {
global $USER;
if ($this->isplugin) {
// Plugin presets can't be saved.
return false;
}
if (!is_null($this->storedfile)) {
// It's a pre-existing preset, so it needs to be updated.
return $this->update_user_preset();
}
// The preset hasn't been saved before.
$fs = get_file_storage();
// Create and save the preset.xml file, with the description, settings, fields...
$filerecord = static::get_filerecord('preset.xml', $this->get_path(), $USER->id);
$fs->create_file_from_string($filerecord, $this->generate_preset_xml());
// Create and save the template files.
$instance = $this->manager->get_instance();
foreach (manager::TEMPLATES_LIST as $templatename => $templatefile) {
$filerecord->filename = $templatefile;
$fs->create_file_from_string($filerecord, $instance->{$templatename});
}
// Update the storedfile with the one we've just saved.
$this->storedfile = static::get_file($this->get_path(), '.');
return true;
}
/**
* Update the stored user preset.
* This method is used internally by the save method.
*
* @return bool true if the preset has been saved; false otherwise.
*/
private function update_user_preset(): bool {
global $USER;
$result = false;
$shouldbesaved = false;
// Update description (if required).
$oldpresetfile = static::get_file($this->storedfile->get_filepath(), 'preset.xml');
$presetxml = $oldpresetfile->get_content();
$parsedxml = simplexml_load_string($presetxml);
if (property_exists($parsedxml, 'description')) {
if ($parsedxml->description != $this->description) {
$parsedxml->description = $this->description;
$shouldbesaved = true;
}
} else {
if (!is_null($this->description)) {
$parsedxml->addChild('description', $this->description);
$shouldbesaved = true;
}
}
// Update name (if required).
$oldname = trim($this->storedfile->get_filepath(), '/');
$newpath = '/' . $this->name . '/';
if ($oldname != $this->name) {
// Preset name has changed, so files need to be updated too because the preset name is saved in the filepath.
foreach (manager::TEMPLATES_LIST as $templatename => $templatefile) {
$oldfile = static::get_file($this->storedfile->get_filepath(), $templatefile);
$oldfile->rename($newpath, $templatefile);
}
// The root folder should also be renamed.
$this->storedfile->rename($newpath, $this->storedfile->get_filename());
$shouldbesaved = true;
}
// Only save the new preset.xml if there are changes.
if ($shouldbesaved) {
// Before saving preset.xml, the old preset.xml file should be removed.
$oldpresetfile->delete();
// Create the new file with the new content.
$filerecord = static::get_filerecord('preset.xml', $newpath, $USER->id);
$presetcontent = $parsedxml->asXML();
$fs = get_file_storage();
$fs->create_file_from_string($filerecord, $presetcontent);
$result = true;
}
return $result;
}
/**
* Export this preset.
*
* @return string the full path to the exported preset file.
*/
public function export(): string {
if ($this->isplugin) {
// For now, only saved presets can be exported.
return '';
}
$presetname = clean_filename($this->name) . '-preset-' . gmdate("Ymd_Hi");
$exportsubdir = "mod_data/presetexport/$presetname";
$exportdir = make_temp_directory($exportsubdir);
// Generate and write the preset.xml file.
$presetxmldata = static::generate_preset_xml();
$presetxmlfile = fopen($exportdir . '/preset.xml', 'w');
fwrite($presetxmlfile, $presetxmldata);
fclose($presetxmlfile);
// Write the template files.
$instance = $this->manager->get_instance();
foreach (manager::TEMPLATES_LIST as $templatename => $templatefilename) {
$templatefile = fopen("$exportdir/$templatefilename", 'w');
fwrite($templatefile, $instance->{$templatename} ?? '');
fclose($templatefile);
}
// Check if all files have been generated.
if (! static::is_directory_a_preset($exportdir)) {
throw new \moodle_exception('generateerror', 'data');
}
$presetfilenames = array_merge(array_values(manager::TEMPLATES_LIST), ['preset.xml']);
$filelist = [];
foreach ($presetfilenames as $filename) {
$filelist[$filename] = $exportdir . '/' . $filename;
}
$exportfile = $exportdir.'.zip';
file_exists($exportfile) && unlink($exportfile);
$fp = get_file_packer('application/zip');
$fp->archive_to_pathname($filelist, $exportfile);
foreach ($filelist as $file) {
unlink($file);
}
rmdir($exportdir);
return $exportfile;
}
/**
* Return the preset author.
*
* Preset plugins do not have any user id.
*
* @return int|null the userid or null if it is a plugin
*/
public function get_userid(): ?int {
if (!empty($this->storedfile)) {
return $this->storedfile->get_userid();
}
return null;
}
/**
* Return the preset fullname.
*
* Preset fullname is used mostly for urls.
*
* @return string the preset fullname
*/
public function get_fullname(): string {
$userid = $this->get_userid() ?? '0';
return "{$userid}/{$this->shortname}";
}
/**
* Returns the preset path.
*
* @return string|null the preset path is null for plugins and /presetname/ for saved presets.
*/
public function get_path(): ?string {
if ($this->isplugin) {
return null;
}
if (!empty($this->storedfile)) {
return $this->storedfile->get_filepath();
}
return '/' . $this->name . '/';
}
/**
* Return the field instances of the preset.
*
* @param bool $forpreview if the fields are only for preview
* @return data_field_base[] and array with field objects
*/
public function get_fields(bool $forpreview = false): array {
if ($this->fields !== null) {
return $this->fields;
}
// Parse the preset.xml file.
$this->load_preset_xml();
if (empty($this->xmlinfo) || empty($this->xmlinfo->field)) {
$this->fields = [];
return $this->fields;
}
// Generate field instances.
$result = [];
foreach ($this->xmlinfo->field as $fieldinfo) {
$result[(string) $fieldinfo->name] = $this->get_field_instance($fieldinfo, count($result), $forpreview);
}
$this->fields = $result;
return $result;
}
/**
* Convert a preset.xml field data into field instance.
*
* @param SimpleXMLElement $fieldinfo the field xml information
* @param int $id the field id to use
* @param bool $forpreview if the field should support preview
* @return data_field_base the field instance
*/
private function get_field_instance(
SimpleXMLElement $fieldinfo,
int $id = 0,
bool $forpreview = false
): data_field_base {
global $CFG; // Some old field plugins require $CFG to be in the scope.
$fieldrecord = $this->get_fake_field_record($fieldinfo, $id);
$instance = $this->manager->get_instance();
$cm = $this->manager->get_coursemodule();
// Include the plugin.
$filepath = "{$this->manager->path}/field/{$fieldrecord->type}/field.class.php";
if (file_exists($filepath)) {
require_once($filepath);
}
$classname = "data_field_{$fieldrecord->type}";
$newfield = null;
if (class_exists($classname)) {
$newfield = new $classname($fieldrecord, $instance, $cm);
if ($forpreview && !$newfield->supports_preview()) {
$newfield = new data_field_base($fieldrecord, $instance, $cm);
}
} else {
$newfield = new data_field_base($fieldrecord, $instance, $cm);
}
if ($forpreview) {
$newfield->set_preview(true);
}
return $newfield;
}
/**
* Generate a fake field record fomr the preset.xml field data.
*
* @param SimpleXMLElement $fieldinfo the field xml information
* @param int $id the field id to use
* @return stdClass the fake record
*/
private function get_fake_field_record(SimpleXMLElement $fieldinfo, int $id = 0): stdClass {
$instance = $this->manager->get_instance();
// Generate stub record.
$fieldrecord = (object)[
'id' => $id,
'dataid' => $instance->id,
'type' => (string) $fieldinfo->type,
'name' => (string) $fieldinfo->name,
'description' => (string) $fieldinfo->description ?? '',
'required' => (int) $fieldinfo->required ?? 0,
];
for ($i = 1; $i < 11; $i++) {
$name = "param{$i}";
$fieldrecord->{$name} = null;
if (property_exists($fieldinfo, $name)) {
$fieldrecord->{$name} = (string) $fieldinfo->{$name};
}
}
return $fieldrecord;
}
/**
* Return sample entries to preview this preset.
*
* @param int $count the number of entries to generate.
* @return array of sample entries
*/
public function get_sample_entries(int $count = 1): array {
global $USER;
$fields = $this->get_fields();
$instance = $this->manager->get_instance();
$entries = [];
for ($current = 1; $current <= $count; $current++) {
$entry = (object)[
'id' => $current,
'userid' => $USER->id,
'groupid' => 0,
'dataid' => $instance->id,
'timecreated' => time(),
'timemodified' => time(),
'approved' => 1,
];
// Add all necessary user fields.
$userfieldsapi = \core_user\fields::for_userpic()->excluding('id');
$fields = $userfieldsapi->get_required_fields();
foreach ($fields as $field) {
$entry->{$field} = $USER->{$field};
}
$entries[$current] = $entry;
}
return $entries;
}
/**
* Load all the information from the preset.xml.
*/
protected function load_preset_xml() {
if (!empty($this->xmlinfo)) {
return;
}
// Load everything from the XML.
$presetxml = null;
if ($this->isplugin) {
$path = $this->manager->path . '/preset/' . $this->shortname . '/preset.xml';
$presetxml = file_get_contents($path);
} else {
$presetxml = static::get_content_from_file($this->storedfile->get_filepath(), 'preset.xml');
}
$this->xmlinfo = simplexml_load_string($presetxml);
}
/**
* Return the template content from the preset.
*
* @param string $templatename the template name
* @return string the template content
*/
public function get_template_content(string $templatename): string {
$filename = "{$templatename}.html";
if ($templatename == 'csstemplate') {
$filename = "{$templatename}.css";
}
if ($templatename == 'jstemplate') {
$filename = "{$templatename}.js";
}
if ($this->isplugin) {
$path = $this->manager->path . '/preset/' . $this->shortname . '/' . $filename;
$result = file_get_contents($path);
} else {
$result = static::get_content_from_file($this->storedfile->get_filepath(), $filename);
}
if (empty($result)) {
return '';
}
return $result;
}
/**
* Checks if a directory contains all the required files to define a preset.
*
* @param string $directory The patch to check if it contains the preset files or not.
* @return bool True if the directory contains all the preset files; false otherwise.
*/
public static function is_directory_a_preset(string $directory): bool {
$status = true;
$directory = rtrim($directory, '/\\') . '/';
$presetfilenames = array_merge(array_values(manager::TEMPLATES_LIST), ['preset.xml']);
foreach ($presetfilenames as $filename) {
$status &= file_exists($directory.$filename);
}
return $status;
}
/**
* Returns the best name to show for a datapreset plugin.
*
* @param string $pluginname The datapreset plugin name.
* @return string The plugin preset name to display.
*/
public static function get_name_from_plugin(string $pluginname): string {
$pos = strpos($pluginname, '/');
if ($pos !== false) {
$pluginname = substr($pluginname, $pos + 1);
}
if (!strpos(trim($pluginname), ' ') && get_string_manager()->string_exists('modulename', 'datapreset_'.$pluginname)) {
return get_string('modulename', 'datapreset_'.$pluginname);
} else {
return $pluginname;
}
}
/**
* Returns the description to show for a datapreset plugin.
*
* @param string $pluginname The datapreset plugin name.
* @return string The plugin preset description to display.
*/
public static function get_description_from_plugin(string $pluginname): string {
if (get_string_manager()->string_exists('modulename_help', 'datapreset_'.$pluginname)) {
return get_string('modulename_help', 'datapreset_'.$pluginname);
} else {
return '';
}
}
/**
* Helper to get the value of one of the elements in the presets.xml file.
*
* @param string $filepath The preset filepath.
* @param string $name Attribute name to return.
* @return string|null The attribute value; null if the it doesn't exist or the file is not a valid XML.
*/
protected static function get_attribute_value(string $filepath, string $name): ?string {
$value = null;
$presetxml = static::get_content_from_file($filepath, 'preset.xml');
$parsedxml = simplexml_load_string($presetxml);
if ($parsedxml) {
switch ($name) {
case 'description':
if (property_exists($parsedxml, 'description')) {
$value = $parsedxml->description;
}
break;
}
}
return $value;
}
/**
* Helper method to get a file record given a filename, a filepath and a userid, for any of the preset files.
*
* @param string $filename The filename for the filerecord that will be returned.
* @param string $filepath The filepath for the filerecord that will be returned.
* @param int $userid The userid for the filerecord that will be returned.
* @return stdClass A filerecord object with the datapreset context, component and filearea and the given information.
*/
protected static function get_filerecord(string $filename, string $filepath, int $userid): stdClass {
$filerecord = new stdClass;
$filerecord->contextid = DATA_PRESET_CONTEXT;
$filerecord->component = DATA_PRESET_COMPONENT;
$filerecord->filearea = DATA_PRESET_FILEAREA;
$filerecord->itemid = 0;
$filerecord->filepath = $filepath;
$filerecord->userid = $userid;
$filerecord->filename = $filename;
return $filerecord;
}
/**
* Helper method to retrieve a file.
*
* @param string $filepath the directory to look in
* @param string $filename the name of the file we want
* @return stored_file|null the file or null if the file doesn't exist.
*/
public static function get_file(string $filepath, string $filename): ?stored_file {
$file = null;
$fs = get_file_storage();
$fileexists = $fs->file_exists(
DATA_PRESET_CONTEXT,
DATA_PRESET_COMPONENT,
DATA_PRESET_FILEAREA,
0,
$filepath,
$filename
);
if ($fileexists) {
$file = $fs->get_file(
DATA_PRESET_CONTEXT,
DATA_PRESET_COMPONENT,
DATA_PRESET_FILEAREA,
0,
$filepath,
$filename
);
}
return $file;
}
/**
* Helper method to retrieve the contents of a file.
*
* @param string $filepath the directory to look in
* @param string $filename the name of the file we want
* @return string|null the contents of the file or null if the file doesn't exist.
*/
protected static function get_content_from_file(string $filepath, string $filename): ?string {
$templatefile = static::get_file($filepath, $filename);
if ($templatefile) {
return $templatefile->get_content();
}
return null;
}
/**
* Helper method to generate the XML for this preset.
*
* @return string The XML for the preset
*/
protected function generate_preset_xml(): string {
global $DB;
if ($this->isplugin) {
// Only saved presets can generate the preset.xml file.
return '';
}
$presetxmldata = "<preset>\n\n";
// Add description.
$presetxmldata .= '<description>' . htmlspecialchars($this->description ?? '', ENT_COMPAT) . "</description>\n\n";
// Add settings.
// Raw settings are not preprocessed during saving of presets.
$rawsettings = [
'intro',
'comments',
'requiredentries',
'requiredentriestoview',
'maxentries',
'rssarticles',
'approval',
'manageapproved',
'defaultsortdir',
];
$presetxmldata .= "<settings>\n";
$instance = $this->manager->get_instance();
// First, settings that do not require any conversion.
foreach ($rawsettings as $setting) {
$presetxmldata .= "<$setting>" . htmlspecialchars($instance->$setting, ENT_COMPAT) . "</$setting>\n";
}
// Now specific settings.
if ($instance->defaultsort > 0 && $sortfield = data_get_field_from_id($instance->defaultsort, $instance)) {
$presetxmldata .= '<defaultsort>' . htmlspecialchars($sortfield->field->name, ENT_COMPAT) . "</defaultsort>\n";
} else {
$presetxmldata .= "<defaultsort>0</defaultsort>\n";
}
$presetxmldata .= "</settings>\n\n";
// Add fields. Grab all that are non-empty.
$fields = $DB->get_records('data_fields', ['dataid' => $instance->id]);
ksort($fields);
if (!empty($fields)) {
foreach ($fields as $field) {
$presetxmldata .= "<field>\n";
foreach ($field as $key => $value) {
if ($value != '' && $key != 'id' && $key != 'dataid') {
$presetxmldata .= "<$key>" . htmlspecialchars($value, ENT_COMPAT) . "</$key>\n";
}
}
$presetxmldata .= "</field>\n\n";
}
}
$presetxmldata .= '</preset>';
// Check this content is a valid XML.
$preset = new SimpleXMLElement($presetxmldata);
return $preset->asXML();
}
/**
* Checks to see if the user has permission to manage the preset.
*
* @return bool Returns true if the user can manage this preset, false otherwise.
*/
public function can_manage(): bool {
global $USER;
if ($this->isplugin) {
// Plugin presets can't be removed or edited.
return false;
}
$context = $this->manager->get_context();
if (has_capability('mod/data:manageuserpresets', $context)) {
return true;
} else {
if ($this->get_userid() == $USER->id) {
return true;
}
}
return false;
}
/**
* Deletes all files related to a saved preset.
*
* @return bool True if the preset is a saved preset and the file exists in the file system; false otherwise.
*/
public function delete(): bool {
if ($this->isplugin) {
// Plugin presets can't be removed.
return false;
}
$exists = false;
$filepath = $this->get_path();
$dir = self::get_file($filepath, '.');
if (!empty($dir)) {
$exists = true;
$fs = get_file_storage();
$files = $fs->get_directory_files(
$dir->get_contextid(),
$dir->get_component(),
$dir->get_filearea(),
$dir->get_itemid(),
$filepath
);
if (!empty($files)) {
foreach ($files as $file) {
$file->delete();
}
}
$dir->delete();
// Reseting storedfile property because the file has been removed.
$this->storedfile = null;
}
return $exists;
}
}
@@ -0,0 +1,78 @@
<?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/>.
/**
* Contains interface datafield_provider
*
* @package mod_data
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\privacy;
defined('MOODLE_INTERNAL') || die();
/**
* Interface datafield_provider, all datafield plugins need to implement it
*
* @package mod_data
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
interface datafield_provider extends
\core_privacy\local\request\plugin\subplugin_provider,
// The data subplugins do not need to do anything themselves for the shared_userlist.
// This is all handled by the parent plugin.
\core_privacy\local\request\shared_userlist_provider
{
/**
* Exports data about one record in {data_content} table.
*
* Datafield plugins providers should implement this method to:
* - preprocess references to files in the response (examples - textarea, picture, file)
* - make content more human-readable (example - replace values separators in multimenu, format date in date)
* - add more information about the field itself (example - list all options for menu, multimenu, radio)
*
* Sample implementation (from datafield_textarea):
*
* $defaultvalue->content = writer::with_context($context)
* ->rewrite_pluginfile_urls([$recordobj->id, $contentobj->id], 'mod_data', 'content', $contentobj->id,
* $defaultvalue->content);
* writer::with_context($context)->export_data([$recordobj->id, $contentobj->id], $defaultvalue);
*
* @param \context_module $context
* @param \stdClass $recordobj record from DB table {data_records}
* @param \stdClass $fieldobj record from DB table {data_fields}
* @param \stdClass $contentobj record from DB table {data_content}
* @param \stdClass $defaultvalue pre-populated default value that most of plugins will use
*/
public static function export_data_content($context, $recordobj, $fieldobj, $contentobj, $defaultvalue);
/**
* Allows plugins to delete locally stored data.
*
* Usually datafield plugins do not store anything and this method will be empty.
*
* @param \context_module $context
* @param \stdClass $recordobj record from DB table {data_records}
* @param \stdClass $fieldobj record from DB table {data_fields}
* @param \stdClass $contentobj record from DB table {data_content}
*/
public static function delete_data_content($context, $recordobj, $fieldobj, $contentobj);
}
+573
View File
@@ -0,0 +1,573 @@
<?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/>.
/**
* Privacy Subsystem implementation for mod_data.
*
* @package mod_data
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\privacy;
use core_privacy\local\metadata\collection;
use core_privacy\local\request\approved_contextlist;
use core_privacy\local\request\approved_userlist;
use core_privacy\local\request\contextlist;
use core_privacy\local\request\helper;
use core_privacy\local\request\transform;
use core_privacy\local\request\userlist;
use core_privacy\local\request\writer;
use core_privacy\manager;
defined('MOODLE_INTERNAL') || die();
/**
* Implementation of the privacy subsystem plugin provider for the database activity module.
*
* @package mod_data
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements
// This plugin stores personal data.
\core_privacy\local\metadata\provider,
// This plugin is capable of determining which users have data within it.
\core_privacy\local\request\core_userlist_provider,
// This plugin is a core_user_data_provider.
\core_privacy\local\request\plugin\provider {
/**
* Return the fields which contain personal data.
*
* @param collection $collection a reference to the collection to use to store the metadata.
* @return collection the updated collection of metadata items.
*/
public static function get_metadata(collection $collection): collection {
$collection->add_database_table(
'data_records',
[
'userid' => 'privacy:metadata:data_records:userid',
'groupid' => 'privacy:metadata:data_records:groupid',
'timecreated' => 'privacy:metadata:data_records:timecreated',
'timemodified' => 'privacy:metadata:data_records:timemodified',
'approved' => 'privacy:metadata:data_records:approved',
],
'privacy:metadata:data_records'
);
$collection->add_database_table(
'data_content',
[
'fieldid' => 'privacy:metadata:data_content:fieldid',
'content' => 'privacy:metadata:data_content:content',
'content1' => 'privacy:metadata:data_content:content1',
'content2' => 'privacy:metadata:data_content:content2',
'content3' => 'privacy:metadata:data_content:content3',
'content4' => 'privacy:metadata:data_content:content4',
],
'privacy:metadata:data_content'
);
// Link to subplugins.
$collection->add_plugintype_link('datafield', [], 'privacy:metadata:datafieldnpluginsummary');
// Subsystems used.
$collection->link_subsystem('core_comment', 'privacy:metadata:commentpurpose');
$collection->link_subsystem('core_files', 'privacy:metadata:filepurpose');
$collection->link_subsystem('core_tag', 'privacy:metadata:tagpurpose');
$collection->link_subsystem('core_rating', 'privacy:metadata:ratingpurpose');
return $collection;
}
/**
* Get the list of contexts that contain user information for the specified user.
*
* @param int $userid the userid.
* @return contextlist the list of contexts containing user info for the user.
*/
public static function get_contexts_for_userid(int $userid): contextlist {
$contextlist = new contextlist();
// Fetch all data records that the user rote.
$sql = "SELECT c.id
FROM {context} c
JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel
JOIN {modules} m ON m.id = cm.module AND m.name = :modname
JOIN {data} d ON d.id = cm.instance
JOIN {data_records} dr ON dr.dataid = d.id
WHERE dr.userid = :userid";
$params = [
'contextlevel' => CONTEXT_MODULE,
'modname' => 'data',
'userid' => $userid,
];
$contextlist->add_from_sql($sql, $params);
// Fetch contexts where the user commented.
$sql = "SELECT c.id
FROM {context} c
JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel
JOIN {modules} m ON m.id = cm.module AND m.name = :modname
JOIN {data} d ON d.id = cm.instance
JOIN {data_records} dr ON dr.dataid = d.id
JOIN {comments} com ON com.commentarea = :commentarea and com.itemid = dr.id
WHERE com.userid = :userid";
$params = [
'contextlevel' => CONTEXT_MODULE,
'modname' => 'data',
'commentarea' => 'database_entry',
'userid' => $userid,
];
$contextlist->add_from_sql($sql, $params);
// Fetch all data records.
$ratingquery = \core_rating\privacy\provider::get_sql_join('r', 'mod_data', 'entry', 'dr.id', $userid, true);
$sql = "SELECT c.id
FROM {context} c
JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel
JOIN {modules} m ON m.id = cm.module AND m.name = :modname
JOIN {data} d ON d.id = cm.instance
JOIN {data_records} dr ON dr.dataid = d.id
{$ratingquery->join}
WHERE {$ratingquery->userwhere}";
$params = [
'contextlevel' => CONTEXT_MODULE,
'modname' => 'data',
] + $ratingquery->params;
$contextlist->add_from_sql($sql, $params);
return $contextlist;
}
/**
* Get the list of users who have data within a context.
*
* @param userlist $userlist The userlist containing the list of users who have data in this context/plugin combination.
*
*/
public static function get_users_in_context(userlist $userlist) {
$context = $userlist->get_context();
if (!is_a($context, \context_module::class)) {
return;
}
// Find users with data records.
$sql = "SELECT dr.userid
FROM {context} c
JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel
JOIN {modules} m ON m.id = cm.module AND m.name = :modname
JOIN {data} d ON d.id = cm.instance
JOIN {data_records} dr ON dr.dataid = d.id
WHERE c.id = :contextid";
$params = [
'modname' => 'data',
'contextid' => $context->id,
'contextlevel' => CONTEXT_MODULE,
];
$userlist->add_from_sql('userid', $sql, $params);
// Find users with comments.
\core_comment\privacy\provider::get_users_in_context_from_sql($userlist, 'com', 'mod_data', 'database_entry', $context->id);
// Find users with ratings.
$sql = "SELECT dr.id
FROM {context} c
JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel
JOIN {modules} m ON m.id = cm.module AND m.name = :modname
JOIN {data} d ON d.id = cm.instance
JOIN {data_records} dr ON dr.dataid = d.id
WHERE c.id = :contextid";
$params = [
'modname' => 'data',
'contextid' => $context->id,
'contextlevel' => CONTEXT_MODULE,
];
\core_rating\privacy\provider::get_users_in_context_from_sql($userlist, 'rat', 'mod_data', 'entry', $sql, $params);
}
/**
* Creates an object from all fields in the $record where key starts with $prefix
*
* @param \stdClass $record
* @param string $prefix
* @param array $additionalfields
* @return \stdClass
*/
protected static function extract_object_from_record($record, $prefix, $additionalfields = []) {
$object = new \stdClass();
foreach ($record as $key => $value) {
if (preg_match('/^'.preg_quote($prefix, '/').'(.*)/', $key, $matches)) {
$object->{$matches[1]} = $value;
}
}
if ($additionalfields) {
foreach ($additionalfields as $key => $value) {
$object->$key = $value;
}
}
return $object;
}
/**
* Export one field answer in a record in database activity module
*
* @param \context $context
* @param \stdClass $recordobj record from DB table {data_records}
* @param \stdClass $fieldobj record from DB table {data_fields}
* @param \stdClass $contentobj record from DB table {data_content}
*/
protected static function export_data_content($context, $recordobj, $fieldobj, $contentobj) {
$value = (object)[
'field' => [
// Name and description are displayed in mod_data without applying format_string().
'name' => $fieldobj->name,
'description' => $fieldobj->description,
'type' => $fieldobj->type,
'required' => transform::yesno($fieldobj->required),
],
'content' => $contentobj->content
];
foreach (['content1', 'content2', 'content3', 'content4'] as $key) {
if ($contentobj->$key !== null) {
$value->$key = $contentobj->$key;
}
}
$classname = manager::get_provider_classname_for_component('datafield_' . $fieldobj->type);
if (class_exists($classname) && is_subclass_of($classname, datafield_provider::class)) {
component_class_callback($classname, 'export_data_content',
[$context, $recordobj, $fieldobj, $contentobj, $value]);
} else {
// Data field plugin does not implement datafield_provider, just export default value.
writer::with_context($context)->export_data([$recordobj->id, $contentobj->id], $value);
}
writer::with_context($context)->export_area_files([$recordobj->id, $contentobj->id], 'mod_data',
'content', $contentobj->id);
}
/**
* SQL query that returns all fields from {data_content}, {data_fields} and {data_records} tables
*
* @return string
*/
protected static function sql_fields() {
return 'd.id AS dataid, dc.id AS contentid, dc.fieldid, df.type AS fieldtype, df.name AS fieldname,
df.description AS fielddescription, df.required AS fieldrequired,
df.param1 AS fieldparam1, df.param2 AS fieldparam2, df.param3 AS fieldparam3, df.param4 AS fieldparam4,
df.param5 AS fieldparam5, df.param6 AS fieldparam6, df.param7 AS fieldparam7, df.param8 AS fieldparam8,
df.param9 AS fieldparam9, df.param10 AS fieldparam10,
dc.content AS contentcontent, dc.content1 AS contentcontent1, dc.content2 AS contentcontent2,
dc.content3 AS contentcontent3, dc.content4 AS contentcontent4,
dc.recordid, dr.timecreated AS recordtimecreated, dr.timemodified AS recordtimemodified,
dr.approved AS recordapproved, dr.groupid AS recordgroupid, dr.userid AS recorduserid';
}
/**
* Export personal data for the given approved_contextlist. User and context information is contained within the contextlist.
*
* @param approved_contextlist $contextlist a list of contexts approved for export.
*/
public static function export_user_data(approved_contextlist $contextlist) {
global $DB;
if (!$contextlist->count()) {
return;
}
$user = $contextlist->get_user();
list($contextsql, $contextparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);
$sql = "SELECT cm.id AS cmid, d.name AS dataname, cm.course AS courseid, " . self::sql_fields() . "
FROM {context} ctx
JOIN {course_modules} cm ON cm.id = ctx.instanceid
JOIN {modules} m ON m.id = cm.module AND m.name = :modname
JOIN {data} d ON d.id = cm.instance
JOIN {data_records} dr ON dr.dataid = d.id
JOIN {data_content} dc ON dc.recordid = dr.id
JOIN {data_fields} df ON df.id = dc.fieldid
WHERE ctx.id {$contextsql} AND ctx.contextlevel = :contextlevel
AND dr.userid = :userid OR
EXISTS (SELECT 1 FROM {comments} com WHERE com.commentarea=:commentarea
AND com.itemid = dr.id AND com.userid = :userid1) OR
EXISTS (SELECT 1 FROM {rating} r WHERE r.contextid = ctx.id AND r.itemid = dr.id AND r.component = :moddata
AND r.ratingarea = :ratingarea AND r.userid = :userid2)
ORDER BY cm.id, dr.id, dc.fieldid";
$rs = $DB->get_recordset_sql($sql, $contextparams + ['contextlevel' => CONTEXT_MODULE,
'modname' => 'data', 'userid' => $user->id, 'userid1' => $user->id, 'commentarea' => 'database_entry',
'userid2' => $user->id, 'ratingarea' => 'entry', 'moddata' => 'mod_data']);
$context = null;
$recordobj = null;
foreach ($rs as $row) {
if (!$context || $context->instanceid != $row->cmid) {
// This row belongs to the different data module than the previous row.
// Export the data for the previous module.
self::export_data($context, $user);
// Start new data module.
$context = \context_module::instance($row->cmid);
}
if (!$recordobj || $row->recordid != $recordobj->id) {
// Export previous data record.
self::export_data_record($context, $user, $recordobj);
// Prepare for exporting new data record.
$recordobj = self::extract_object_from_record($row, 'record', ['dataid' => $row->dataid]);
}
$fieldobj = self::extract_object_from_record($row, 'field', ['dataid' => $row->dataid]);
$contentobj = self::extract_object_from_record($row, 'content',
['fieldid' => $fieldobj->id, 'recordid' => $recordobj->id]);
self::export_data_content($context, $recordobj, $fieldobj, $contentobj);
}
$rs->close();
self::export_data_record($context, $user, $recordobj);
self::export_data($context, $user);
}
/**
* Export one entry in the database activity module (one record in {data_records} table)
*
* @param \context $context
* @param \stdClass $user
* @param \stdClass $recordobj
*/
protected static function export_data_record($context, $user, $recordobj) {
if (!$recordobj) {
return;
}
$data = [
'userid' => transform::user($user->id),
'groupid' => $recordobj->groupid,
'timecreated' => transform::datetime($recordobj->timecreated),
'timemodified' => transform::datetime($recordobj->timemodified),
'approved' => transform::yesno($recordobj->approved),
];
// Data about the record.
writer::with_context($context)->export_data([$recordobj->id], (object)$data);
// Related tags.
\core_tag\privacy\provider::export_item_tags($user->id, $context, [$recordobj->id],
'mod_data', 'data_records', $recordobj->id);
// Export comments. For records that were not made by this user export only this user's comments, for own records
// export comments made by everybody.
\core_comment\privacy\provider::export_comments($context, 'mod_data', 'database_entry', $recordobj->id,
[$recordobj->id], $recordobj->userid != $user->id);
// Export ratings. For records that were not made by this user export only this user's ratings, for own records
// export ratings from everybody.
\core_rating\privacy\provider::export_area_ratings($user->id, $context, [$recordobj->id], 'mod_data', 'entry',
$recordobj->id, $recordobj->userid != $user->id);
}
/**
* Export basic info about database activity module
*
* @param \context $context
* @param \stdClass $user
*/
protected static function export_data($context, $user) {
if (!$context) {
return;
}
$contextdata = helper::get_context_data($context, $user);
helper::export_context_files($context, $user);
writer::with_context($context)->export_data([], $contextdata);
}
/**
* Delete all data for all users in the specified context.
*
* @param \context $context the context to delete in.
*/
public static function delete_data_for_all_users_in_context(\context $context) {
global $DB;
if (!$context instanceof \context_module) {
return;
}
$recordstobedeleted = [];
$sql = "SELECT " . self::sql_fields() . "
FROM {course_modules} cm
JOIN {modules} m ON m.id = cm.module AND m.name = :modname
JOIN {data} d ON d.id = cm.instance
JOIN {data_records} dr ON dr.dataid = d.id
LEFT JOIN {data_content} dc ON dc.recordid = dr.id
LEFT JOIN {data_fields} df ON df.id = dc.fieldid
WHERE cm.id = :cmid
ORDER BY dr.id";
$rs = $DB->get_recordset_sql($sql, ['cmid' => $context->instanceid, 'modname' => 'data']);
foreach ($rs as $row) {
self::mark_data_content_for_deletion($context, $row);
$recordstobedeleted[$row->recordid] = $row->recordid;
}
$rs->close();
self::delete_data_records($context, $recordstobedeleted);
}
/**
* Delete all user data for the specified user, in the specified contexts.
*
* @param approved_contextlist $contextlist a list of contexts approved for deletion.
*/
public static function delete_data_for_user(approved_contextlist $contextlist) {
global $DB;
if (empty($contextlist->count())) {
return;
}
$user = $contextlist->get_user();
$recordstobedeleted = [];
foreach ($contextlist->get_contexts() as $context) {
$sql = "SELECT " . self::sql_fields() . "
FROM {context} ctx
JOIN {course_modules} cm ON cm.id = ctx.instanceid
JOIN {modules} m ON m.id = cm.module AND m.name = :modname
JOIN {data} d ON d.id = cm.instance
JOIN {data_records} dr ON dr.dataid = d.id AND dr.userid = :userid
LEFT JOIN {data_content} dc ON dc.recordid = dr.id
LEFT JOIN {data_fields} df ON df.id = dc.fieldid
WHERE ctx.id = :ctxid AND ctx.contextlevel = :contextlevel
ORDER BY dr.id";
$rs = $DB->get_recordset_sql($sql, ['ctxid' => $context->id, 'contextlevel' => CONTEXT_MODULE,
'modname' => 'data', 'userid' => $user->id]);
foreach ($rs as $row) {
self::mark_data_content_for_deletion($context, $row);
$recordstobedeleted[$row->recordid] = $row->recordid;
}
$rs->close();
self::delete_data_records($context, $recordstobedeleted);
}
// Additionally remove comments this user made on other entries.
\core_comment\privacy\provider::delete_comments_for_user($contextlist, 'mod_data', 'database_entry');
// We do not delete ratings made by this user on other records because it may change grades.
}
/**
* Delete multiple users within a single context.
*
* @param approved_userlist $userlist The approved context and user information to delete information for.
*/
public static function delete_data_for_users(approved_userlist $userlist) {
global $DB;
$context = $userlist->get_context();
$recordstobedeleted = [];
list($userinsql, $userinparams) = $DB->get_in_or_equal($userlist->get_userids(), SQL_PARAMS_NAMED);
$sql = "SELECT " . self::sql_fields() . "
FROM {context} ctx
JOIN {course_modules} cm ON cm.id = ctx.instanceid
JOIN {modules} m ON m.id = cm.module AND m.name = :modname
JOIN {data} d ON d.id = cm.instance
JOIN {data_records} dr ON dr.dataid = d.id AND dr.userid {$userinsql}
LEFT JOIN {data_content} dc ON dc.recordid = dr.id
LEFT JOIN {data_fields} df ON df.id = dc.fieldid
WHERE ctx.id = :ctxid AND ctx.contextlevel = :contextlevel
ORDER BY dr.id";
$params = [
'ctxid' => $context->id,
'contextlevel' => CONTEXT_MODULE,
'modname' => 'data',
];
$params += $userinparams;
$rs = $DB->get_recordset_sql($sql, $params);
foreach ($rs as $row) {
self::mark_data_content_for_deletion($context, $row);
$recordstobedeleted[$row->recordid] = $row->recordid;
}
$rs->close();
self::delete_data_records($context, $recordstobedeleted);
// Additionally remove comments these users made on other entries.
\core_comment\privacy\provider::delete_comments_for_users($userlist, 'mod_data', 'database_entry');
// We do not delete ratings made by users on other records because it may change grades.
}
/**
* Marks a data_record/data_content for deletion
*
* Also invokes callback from datafield plugin in case it stores additional data that needs to be deleted
*
* @param \context $context
* @param \stdClass $row result of SQL query - tables data_content, data_record, data_fields join together
*/
protected static function mark_data_content_for_deletion($context, $row) {
$recordobj = self::extract_object_from_record($row, 'record', ['dataid' => $row->dataid]);
if ($row->contentid && $row->fieldid) {
$fieldobj = self::extract_object_from_record($row, 'field', ['dataid' => $row->dataid]);
$contentobj = self::extract_object_from_record($row, 'content',
['fieldid' => $fieldobj->id, 'recordid' => $recordobj->id]);
// Allow datafield plugin to implement their own deletion.
$classname = manager::get_provider_classname_for_component('datafield_' . $fieldobj->type);
if (class_exists($classname) && is_subclass_of($classname, datafield_provider::class)) {
component_class_callback($classname, 'delete_data_content',
[$context, $recordobj, $fieldobj, $contentobj]);
}
}
}
/**
* Deletes records marked for deletion and all associated data
*
* Should be executed after all records were marked by {@link mark_data_content_for_deletion()}
*
* Deletes records from data_content and data_records tables, associated files, tags, comments and ratings.
*
* @param \context $context
* @param array $recordstobedeleted list of ids of the data records that need to be deleted
*/
protected static function delete_data_records($context, $recordstobedeleted) {
global $DB;
if (empty($recordstobedeleted)) {
return;
}
list($sql, $params) = $DB->get_in_or_equal($recordstobedeleted, SQL_PARAMS_NAMED);
// Delete files.
get_file_storage()->delete_area_files_select($context->id, 'mod_data', 'data_records',
"IN (SELECT dc.id FROM {data_content} dc WHERE dc.recordid $sql)", $params);
// Delete from data_content.
$DB->delete_records_select('data_content', 'recordid ' . $sql, $params);
// Delete from data_records.
$DB->delete_records_select('data_records', 'id ' . $sql, $params);
// Delete tags.
\core_tag\privacy\provider::delete_item_tags_select($context, 'mod_data', 'data_records', $sql, $params);
// Delete comments.
\core_comment\privacy\provider::delete_comments_for_all_users_select($context, 'mod_data', 'database_entry', $sql, $params);
// Delete ratings.
\core_rating\privacy\provider::delete_ratings_select($context, 'mod_data', 'entry', $sql, $params);
}
}
+47
View File
@@ -0,0 +1,47 @@
<?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/>.
/**
* Search area for mod_data activities.
*
* @package mod_data
* @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\search;
defined('MOODLE_INTERNAL') || die();
/**
* Search area for mod_data activities.
*
* @package mod_data
* @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class activity extends \core_search\base_activity {
/**
* Returns true if this area uses file indexing.
*
* @return bool
*/
public function uses_file_indexing() {
return true;
}
}
+398
View File
@@ -0,0 +1,398 @@
<?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/>.
/**
* Search area for mod_data activity entries.
*
* @package mod_data
* @copyright 2016 Devang Gaur
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\search;
use mod_data\manager;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot . '/mod/data/lib.php');
require_once($CFG->dirroot . '/lib/grouplib.php');
/**
* Search area for mod_data activity entries.
*
* @package mod_data
* @copyright 2016 Devang Gaur
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class entry extends \core_search\base_mod {
/**
* @var array Internal quick static cache.
*/
protected $entriesdata = array();
/**
* Returns recordset containing required data for indexing database entries.
*
* @param int $modifiedfrom timestamp
* @param \context|null $context Optional context to restrict scope of returned results
* @return moodle_recordset|null Recordset (or null if no results)
*/
public function get_document_recordset($modifiedfrom = 0, \context $context = null) {
global $DB;
list ($contextjoin, $contextparams) = $this->get_context_restriction_sql(
$context, 'data', 'd', SQL_PARAMS_NAMED);
if ($contextjoin === null) {
return null;
}
$sql = "SELECT dr.*, d.course
FROM {data_records} dr
JOIN {data} d ON d.id = dr.dataid
$contextjoin
WHERE dr.timemodified >= :timemodified";
return $DB->get_recordset_sql($sql,
array_merge($contextparams, ['timemodified' => $modifiedfrom]));
}
/**
* Returns the documents associated with this glossary entry id.
*
* @param stdClass $entry glossary entry.
* @param array $options
* @return \core_search\document
*/
public function get_document($entry, $options = array()) {
try {
$cm = $this->get_cm('data', $entry->dataid, $entry->course);
$context = \context_module::instance($cm->id);
} catch (\dml_missing_record_exception $ex) {
// Notify it as we run here as admin, we should see everything.
debugging('Error retrieving mod_data ' . $entry->id . ' document, not all required data is available: ' .
$ex->getMessage(), DEBUG_DEVELOPER);
return false;
} catch (\dml_exception $ex) {
// Notify it as we run here as admin, we should see everything.
debugging('Error retrieving mod_data' . $entry->id . ' document: ' . $ex->getMessage(), DEBUG_DEVELOPER);
return false;
}
// Prepare associative array with data from DB.
$doc = \core_search\document_factory::instance($entry->id, $this->componentname, $this->areaname);
$doc->set('contextid', $context->id);
$doc->set('courseid', $entry->course);
$doc->set('userid', $entry->userid);
if ($entry->groupid > 0) {
$doc->set('groupid', $entry->groupid);
}
$doc->set('owneruserid', \core_search\manager::NO_OWNER_ID);
$doc->set('modified', $entry->timemodified);
$indexfields = $this->get_fields_for_entries($entry);
if (count($indexfields) < 2) {
return false;
}
// All fields should be already returned as plain text by data_field_base::get_content_value.
$doc->set('title', $indexfields[0]);
$doc->set('content', $indexfields[1]);
if (isset($indexfields[2])) {
$doc->set('description1', $indexfields[2]);
}
if (isset($indexfields[3])) {
$doc->set('description2', $indexfields[3]);
}
return $doc;
}
/**
* Whether the user can access the document or not.
*
* @throws \dml_missing_record_exception
* @throws \dml_exception
* @param int $id Glossary entry id
* @return bool
*/
public function check_access($id) {
global $DB, $USER;
if (isguestuser()) {
return \core_search\manager::ACCESS_DENIED;
}
$now = time();
$sql = "SELECT dr.*, d.*
FROM {data_records} dr
JOIN {data} d ON d.id = dr.dataid
WHERE dr.id = ?";
$entry = $DB->get_record_sql($sql, array( $id ), IGNORE_MISSING);
if (!$entry) {
return \core_search\manager::ACCESS_DELETED;
}
if (($entry->timeviewfrom && $now < $entry->timeviewfrom) || ($entry->timeviewto && $now > $entry->timeviewto)) {
return \core_search\manager::ACCESS_DENIED;
}
$cm = $this->get_cm('data', $entry->dataid, $entry->course);
$context = \context_module::instance($cm->id);
$canmanageentries = has_capability('mod/data:manageentries', $context);
if (!has_capability('mod/data:viewentry', $context)) {
return \core_search\manager::ACCESS_DENIED;
}
$numberofentriesindb = $DB->count_records('data_records', array('dataid' => $entry->dataid));
$requiredentriestoview = $entry->requiredentriestoview;
if ($requiredentriestoview && ($requiredentriestoview > $numberofentriesindb) &&
($USER->id != $entry->userid) && !$canmanageentries) {
return \core_search\manager::ACCESS_DENIED;
}
if ($entry->approval && !$entry->approved && ($entry->userid != $USER->id) && !$canmanageentries) {
return \core_search\manager::ACCESS_DENIED;
}
$currentgroup = groups_get_activity_group($cm, true);
$groupmode = groups_get_activity_groupmode($cm);
if (($groupmode == 1) && ($entry->groupid != $currentgroup) && !$canmanageentries) {
return \core_search\manager::ACCESS_DENIED;
}
return \core_search\manager::ACCESS_GRANTED;
}
/**
* Link to database entry.
*
* @param \core_search\document $doc
* @return \moodle_url
*/
public function get_doc_url(\core_search\document $doc) {
$entry = $this->get_entry($doc->get('itemid'));
return new \moodle_url('/mod/data/view.php', array( 'd' => $entry->dataid, 'rid' => $entry->id ));
}
/**
* Link to the database activity.
*
* @param \core_search\document $doc
* @return \moodle_url
*/
public function get_context_url(\core_search\document $doc) {
$entry = $this->get_entry($doc->get('itemid'));
return new \moodle_url('/mod/data/view.php', array('d' => $entry->dataid));
}
/**
* Returns true if this area uses file indexing.
*
* @return bool
*/
public function uses_file_indexing() {
return true;
}
/**
* Add the database entries attachments.
*
* @param \core_search\document $doc
* @return void
*/
public function attach_files($doc) {
global $DB;
$entryid = $doc->get('itemid');
try {
$entry = $this->get_entry($entryid);
} catch (\dml_missing_record_exception $e) {
debugging('Could not get record to attach files to '.$doc->get('id'), DEBUG_DEVELOPER);
return;
}
$cm = $this->get_cm('data', $entry->dataid, $doc->get('courseid'));
$context = \context_module::instance($cm->id);
// Get all content fields which have files in them.
$contentssql = "
SELECT con.*
FROM {data_content} con
JOIN {files} fil
ON fil.component = :component
AND fil.filearea = :filearea
AND fil.itemid = con.id
WHERE con.recordid = :recordid
";
$contents = $DB->get_recordset_sql($contentssql, [
'recordid' => $entryid,
'component' => 'mod_data',
'filearea' => 'content',
]);
foreach ($contents as $content) {
// Get the files and attach them.
$fs = get_file_storage();
$files = $fs->get_area_files($context->id, 'mod_data', 'content', $content->id, 'filename', false);
foreach ($files as $file) {
$doc->add_stored_file($file);
}
}
$contents->close();
}
/**
* Get database entry data
*
* @throws \dml_exception
* @param int $entryid
* @return stdClass
*/
protected function get_entry($entryid) {
global $DB;
if (empty($this->entriesdata[$entryid])) {
$this->entriesdata[$entryid] = $DB->get_record('data_records', array( 'id' => $entryid ), '*', MUST_EXIST);
}
return $this->entriesdata[$entryid];
}
/**
* get_fields_for_entries
*
* @param StdClass $entry
* @return array
*/
protected function get_fields_for_entries($entry) {
global $DB;
$indexfields = array();
$validfieldtypes = array('text', 'textarea', 'menu', 'radiobutton', 'checkbox', 'multimenu', 'url');
$sql = "SELECT dc.*, df.name AS fldname,
df.type AS fieldtype, df.required
FROM {data_content} dc, {data_fields} df
WHERE dc.fieldid = df.id
AND dc.recordid = :recordid";
$contents = $DB->get_records_sql($sql, ['recordid' => $entry->id]);
$filteredcontents = [];
$data = $DB->get_record('data', ['id' => $entry->dataid]);
$manager = manager::create_from_instance($data);
$template = $manager->get_template('addtemplate');
$template = $template->get_template_content();
// Filtering out the data_content records having invalid fieldtypes.
foreach ($contents as $content) {
if (in_array($content->fieldtype, $validfieldtypes)) {
$filteredcontents[] = $content;
}
}
foreach ($filteredcontents as $content) {
$classname = $this->get_field_class_name($content->fieldtype);
if (!$classname) {
$content->addtemplateposition = -1;
continue;
}
$content->priority = $classname::get_priority();
$content->addtemplateposition = strpos($template ?? '', '[['.$content->fldname.']]');
}
$orderqueue = new \SPLPriorityQueue();
// Filtering out contents which belong to fields that aren't present in the addtemplate of the database activity instance.
foreach ($filteredcontents as $content) {
if ($content->addtemplateposition >= 0) {
$orderqueue->insert($content, $content->addtemplateposition);
}
}
$filteredcontents = array();
while ($orderqueue->valid()) {
$filteredcontents[] = $orderqueue->extract();
}
// SPLPriorityQueue sorts according to descending order of the priority (here, addtemplateposition).
$filteredcontents = array_reverse($filteredcontents);
// Using a CUSTOM SPLPriorityQueure instance to sort out the filtered contents according to these rules :
// 1. Priorities in $fieldtypepriorities
// 2. Compulsory fieldtypes are to be given the top priority.
$contentqueue = new sortedcontentqueue($filteredcontents);
foreach ($filteredcontents as $key => $content) {
$contentqueue->insert($content, $key);
}
while ($contentqueue->valid()) {
$content = $contentqueue->extract();
$classname = $this->get_field_class_name($content->fieldtype);
$indexfields[] = $classname::get_content_value($content);
}
// Limited to 4 fields as a document only has 4 content fields.
if (count($indexfields) > 4) {
$indexfields[3] = implode(' ', array_slice($indexfields, 3));
}
return $indexfields;
}
/**
* Returns the class name for the given field type and includes it.
*
* @param string $fieldtype
* @return string|null It will return the class name or null if the field type is not available.
*/
protected function get_field_class_name(string $fieldtype): ?string {
global $CFG;
$fieldtype = trim($fieldtype);
$fieldpath = $CFG->dirroot . '/mod/data/field/' . $fieldtype . '/field.class.php';
if (!file_exists($fieldpath)) {
return null;
}
require_once($fieldpath);
return 'data_field_' . $fieldtype;
}
/**
* Confirms that data entries support group restrictions.
*
* @return bool True
*/
public function supports_group_restriction() {
return true;
}
}
@@ -0,0 +1,80 @@
<?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/>.
/**
* Priority Queue class to sort out db entry contents.
*
* @package mod_data
* @copyright 2016 Devang Gaur
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\search;
defined('MOODLE_INTERNAL') || die();
/**
* Priority Queue class to sort out db entry contents.
*
* @package mod_data
* @copyright 2016 Devang Gaur
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class sortedcontentqueue extends \SPLPriorityQueue {
/**
* @var array All contents that will be sorted.
*/
private $contents;
/**
* contructor
*
* @param array $contents
* @return void
*/
public function __construct($contents) {
$this->contents = $contents;
}
/**
* comparator function overriden for sorting the records
* ...as per 'required' and 'priotirity' field values
*
* @param int $key1
* @param int $key2
* @return int
*/
public function compare($key1 , $key2): int {
$record1 = $this->contents[$key1];
$record2 = $this->contents[$key2];
// If a content's fieldtype is compulsory in the database than it would have priority than any other noncompulsory content.
if ( ($record1->required && $record2->required) || (!$record1->required && !$record2->required)) {
if ($record1->priority === $record2->priority) {
return $key1 < $key2 ? 1 : -1;
}
return $record1->priority < $record2->priority ? -1 : 1;
} else if ($record1->required && !$record2->required) {
return 1;
} else {
return -1;
}
}
}
File diff suppressed because it is too large Load Diff