mirror of
https://github.com/moodle/moodle.git
synced 2025-04-14 13:02:07 +02:00
Merge branch 'MDL-75149-master' of https://github.com/ferranrecio/moodle
This commit is contained in:
commit
930c204953
@ -194,7 +194,11 @@ class save_as_preset extends dynamic_form {
|
||||
|
||||
if ($result) {
|
||||
// Add notification in the session to be shown when the page is reloaded on the JS side.
|
||||
notification::success(get_string('savesuccess', 'mod_data'));
|
||||
$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();
|
||||
|
@ -38,6 +38,9 @@ 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',
|
||||
@ -53,7 +56,7 @@ class manager {
|
||||
];
|
||||
|
||||
/** @var string plugin path. */
|
||||
private $path;
|
||||
public $path;
|
||||
|
||||
/** @var stdClass course_module record. */
|
||||
private $instance;
|
||||
@ -239,6 +242,7 @@ class manager {
|
||||
* @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)) {
|
||||
@ -323,6 +327,24 @@ class manager {
|
||||
return true;
|
||||
}
|
||||
|
||||
/** 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.
|
||||
*
|
||||
|
@ -16,7 +16,9 @@
|
||||
|
||||
namespace mod_data\output;
|
||||
|
||||
use mod_data\manager;
|
||||
use moodle_url;
|
||||
use url_select;
|
||||
|
||||
/**
|
||||
* Class responsible for generating the action bar elements in the database module pages.
|
||||
@ -75,7 +77,7 @@ class action_bar {
|
||||
$selected = $presetslink->out(false);
|
||||
}
|
||||
|
||||
$urlselect = new \url_select($menu, $selected, null, 'fieldactionselect');
|
||||
$urlselect = new url_select($menu, $selected, null, 'fieldactionselect');
|
||||
$urlselect->set_label(get_string('fieldsnavigation', 'mod_data'), ['class' => 'sr-only']);
|
||||
|
||||
$fieldselect = null;
|
||||
@ -144,7 +146,7 @@ class action_bar {
|
||||
$activeurl = $viewsinglelink;
|
||||
}
|
||||
|
||||
$urlselect = new \url_select($menu, $activeurl->out(false), null, 'viewactionselect');
|
||||
$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);
|
||||
@ -181,7 +183,7 @@ class action_bar {
|
||||
$jstemplatelink->out(false) => get_string('jstemplate', 'mod_data'),
|
||||
];
|
||||
|
||||
$urlselect = new \url_select($menu, $this->currenturl->out(false), null, 'templatesactionselect');
|
||||
$urlselect = new url_select($menu, $this->currenturl->out(false), null, 'templatesactionselect');
|
||||
$urlselect->set_label(get_string('templatesnavigation', 'mod_data'), ['class' => 'sr-only']);
|
||||
|
||||
$hasfields = $DB->record_exists('data_fields', ['dataid' => $this->id]);
|
||||
@ -221,4 +223,46 @@ class action_bar {
|
||||
|
||||
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),
|
||||
'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);
|
||||
}
|
||||
}
|
||||
|
118
mod/data/classes/output/preset_preview.php
Normal file
118
mod/data/classes/output/preset_preview.php
Normal 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 {
|
||||
$instance = $this->manager->get_instance();
|
||||
$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 [
|
||||
'd' => $instance->id,
|
||||
'description' => $preset->description ?? '',
|
||||
'preview' => $content,
|
||||
'formactionurl' => $useurl->out(),
|
||||
'userid' => $preset->get_userid() ?? 0,
|
||||
'shortname' => $preset->shortname,
|
||||
];
|
||||
}
|
||||
}
|
@ -72,7 +72,7 @@ class presets implements templatable, renderable {
|
||||
$presets = $this->get_presets($output);
|
||||
return [
|
||||
'd' => $this->id,
|
||||
'formactionul' => $this->formactionurl->out(),
|
||||
'formactionurl' => $this->formactionurl->out(),
|
||||
'showmanage' => $this->manage,
|
||||
'presets' => $presets,
|
||||
];
|
||||
@ -100,9 +100,16 @@ class presets implements templatable, renderable {
|
||||
}
|
||||
$actions = $this->get_preset_action_menu($output, $preset, $userid);
|
||||
|
||||
$fullname = "{$userid}/{$preset->shortname}";
|
||||
$previewurl = new moodle_url(
|
||||
'/mod/data/preset.php',
|
||||
['d' => $this->id, 'fullname' => $fullname, 'action' => 'preview']
|
||||
);
|
||||
|
||||
$presets[] = [
|
||||
'id' => $this->id,
|
||||
'name' => $preset->name,
|
||||
'url' => $previewurl->out(),
|
||||
'shortname' => $preset->shortname,
|
||||
'fullname' => $presetname,
|
||||
'description' => $preset->description,
|
||||
|
@ -18,6 +18,8 @@ namespace mod_data;
|
||||
|
||||
use core_component;
|
||||
use invalid_parameter_exception;
|
||||
use data_field_base;
|
||||
use moodle_exception;
|
||||
use SimpleXMLElement;
|
||||
use stdClass;
|
||||
use stored_file;
|
||||
@ -50,6 +52,12 @@ class preset {
|
||||
* 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.
|
||||
*
|
||||
@ -151,6 +159,38 @@ class preset {
|
||||
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.
|
||||
*
|
||||
@ -303,16 +343,29 @@ class preset {
|
||||
/**
|
||||
* Return the preset author.
|
||||
*
|
||||
* @return int|null
|
||||
* 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.
|
||||
*
|
||||
@ -330,6 +383,175 @@ class preset {
|
||||
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.
|
||||
*
|
||||
|
@ -76,6 +76,9 @@ class template {
|
||||
/** @var array All template tags (calculated in load_template_tags). */
|
||||
protected $tags = [];
|
||||
|
||||
/** @var array The mod_data fields. */
|
||||
protected $fields = [];
|
||||
|
||||
/**
|
||||
* Class contructor.
|
||||
*
|
||||
@ -84,8 +87,9 @@ class template {
|
||||
* @param manager $manager the current instance manager
|
||||
* @param string $templatecontent the template string to use
|
||||
* @param array $options an array of extra diplay options
|
||||
* @param array $fields alternative array of fields (for preview presets)
|
||||
*/
|
||||
public function __construct(manager $manager, string $templatecontent, array $options = []) {
|
||||
public function __construct(manager $manager, string $templatecontent, array $options = [], array $fields = null) {
|
||||
$this->manager = $manager;
|
||||
$this->instance = $manager->get_instance();
|
||||
$this->templatecontent = $templatecontent;
|
||||
@ -93,6 +97,7 @@ class template {
|
||||
$context = $manager->get_context();
|
||||
$this->canmanageentries = has_capability('mod/data:manageentries', $context);
|
||||
$this->icons = $this->get_icons();
|
||||
$this->fields = $fields ?? $manager->get_fields();
|
||||
$this->add_options($options);
|
||||
$this->load_template_tags($templatecontent);
|
||||
}
|
||||
@ -215,8 +220,7 @@ class template {
|
||||
*/
|
||||
private function get_fields_replacements(stdClass $entry): array {
|
||||
$result = [];
|
||||
$fields = $this->manager->get_fields();
|
||||
foreach ($fields as $field) {
|
||||
foreach ($this->fields as $field) {
|
||||
// Field value.
|
||||
$pattern = '[[' . $field->field->name . ']]';
|
||||
$result[$pattern] = highlight(
|
||||
@ -448,8 +452,7 @@ class template {
|
||||
['id' => $cm->id, 'recordid' => $entry->id],
|
||||
'mod_data'
|
||||
);
|
||||
$fields = $this->manager->get_fields();
|
||||
list($formats, $files) = data_portfolio_caller::formats($fields, $entry);
|
||||
list($formats, $files) = data_portfolio_caller::formats($this->fields, $entry);
|
||||
$button->set_formats($formats);
|
||||
$result = $button->to_html(PORTFOLIO_ADD_ICON_LINK);
|
||||
if (is_null($result)) {
|
||||
|
@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
@ -22,22 +21,49 @@
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @package mod_data
|
||||
*/
|
||||
use mod_data\manager;
|
||||
use mod_data\preset;
|
||||
|
||||
define('NO_MOODLE_COOKIES', true); // session not used here
|
||||
define('NO_MOODLE_COOKIES', true); // Session not used here.
|
||||
|
||||
require_once('../../config.php');
|
||||
|
||||
$d = optional_param('d', 0, PARAM_INT); // database id
|
||||
$lifetime = 600; // Seconds to cache this stylesheet
|
||||
$id = optional_param('id', 0, PARAM_INT); // Course module id.
|
||||
$d = optional_param('d', 0, PARAM_INT); // Database id.
|
||||
$presetfullname = optional_param('preset', '', PARAM_PATH); // The directory the preset is in.
|
||||
|
||||
$PAGE->set_url('/mod/data/css.php', array('d'=>$d));
|
||||
$lifetime = 600; // Seconds to cache this stylesheet.
|
||||
$url = new moodle_url('/mod/data/css.php');
|
||||
|
||||
if ($data = $DB->get_record('data', array('id'=>$d))) {
|
||||
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', time()) . ' GMT');
|
||||
header('Expires: ' . gmdate("D, d M Y H:i:s", time() + $lifetime) . ' GMT');
|
||||
header('Cache-control: max_age = '. $lifetime);
|
||||
header('Pragma: ');
|
||||
header('Content-type: text/css; charset=utf-8'); // Correct MIME type
|
||||
$manager = null;
|
||||
if ($id) {
|
||||
list($course, $cm) = get_course_and_cm_from_cmid($id, manager::MODULE);
|
||||
$manager = manager::create_from_coursemodule($cm);
|
||||
$instance = $manager->get_instance();
|
||||
} else {
|
||||
// We must have the database activity id.
|
||||
$d = required_param('d', PARAM_INT);
|
||||
$instance = $DB->get_record('data', ['id' => $d], '*', MUST_EXIST);
|
||||
$manager = manager::create_from_instance($instance);
|
||||
}
|
||||
$url->param('d', $instance->id);
|
||||
|
||||
echo $data->csstemplate;
|
||||
}
|
||||
// Get the content.
|
||||
if ($presetfullname) {
|
||||
$url->param('preset', $presetfullname);
|
||||
$preset = preset::create_from_fullname($manager, $presetfullname);
|
||||
$content = $preset->get_template_content('csstemplate');
|
||||
$lifetime = 60; // Preset preview does not need a long cache.
|
||||
} else {
|
||||
$content = $instance->csstemplate;
|
||||
}
|
||||
|
||||
$PAGE->set_url($url);
|
||||
|
||||
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', time()) . ' GMT');
|
||||
header('Expires: ' . gmdate("D, d M Y H:i:s", time() + $lifetime) . ' GMT');
|
||||
header('Cache-control: max_age = '. $lifetime);
|
||||
header('Pragma: ');
|
||||
header('Content-type: text/css; charset=utf-8'); // Correct MIME type.
|
||||
|
||||
echo $content;
|
||||
|
@ -32,6 +32,26 @@ class data_field_checkbox extends data_field_base {
|
||||
*/
|
||||
protected static $priority = self::LOW_PRIORITY;
|
||||
|
||||
public function supports_preview(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function get_data_content_preview(int $recordid): stdClass {
|
||||
$options = explode("\n", $this->field->param1);
|
||||
$options = array_map('trim', $options);
|
||||
$selected = $options[$recordid % count($options)];
|
||||
return (object)[
|
||||
'id' => 0,
|
||||
'fieldid' => $this->field->id,
|
||||
'recordid' => $recordid,
|
||||
'content' => $selected,
|
||||
'content1' => null,
|
||||
'content2' => null,
|
||||
'content3' => null,
|
||||
'content4' => null,
|
||||
];
|
||||
}
|
||||
|
||||
function display_add_field($recordid = 0, $formdata = null) {
|
||||
global $CFG, $DB, $OUTPUT;
|
||||
|
||||
@ -188,28 +208,24 @@ class data_field_checkbox extends data_field_base {
|
||||
}
|
||||
|
||||
function display_browse_field($recordid, $template) {
|
||||
global $DB;
|
||||
|
||||
if ($content = $DB->get_record('data_content', array('fieldid'=>$this->field->id, 'recordid'=>$recordid))) {
|
||||
if (strval($content->content) === '') {
|
||||
return false;
|
||||
}
|
||||
|
||||
$options = explode("\n",$this->field->param1);
|
||||
$options = array_map('trim', $options);
|
||||
|
||||
$contentArr = explode('##', $content->content);
|
||||
$str = '';
|
||||
foreach ($contentArr as $line) {
|
||||
if (!in_array($line, $options)) {
|
||||
// hmm, looks like somebody edited the field definition
|
||||
continue;
|
||||
}
|
||||
$str .= $line . "<br />\n";
|
||||
}
|
||||
return $str;
|
||||
$content = $this->get_data_content($recordid);
|
||||
if (!$content || empty($content->content)) {
|
||||
return '';
|
||||
}
|
||||
return false;
|
||||
|
||||
$options = explode("\n", $this->field->param1);
|
||||
$options = array_map('trim', $options);
|
||||
|
||||
$contentarray = explode('##', $content->content);
|
||||
$str = '';
|
||||
foreach ($contentarray as $line) {
|
||||
if (!in_array($line, $options)) {
|
||||
// Hmm, looks like somebody edited the field definition.
|
||||
continue;
|
||||
}
|
||||
$str .= $line . "<br />\n";
|
||||
}
|
||||
return $str;
|
||||
}
|
||||
|
||||
function format_data_field_checkbox_content($content) {
|
||||
|
@ -34,6 +34,23 @@ class data_field_date extends data_field_base {
|
||||
var $month = 0;
|
||||
var $year = 0;
|
||||
|
||||
public function supports_preview(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function get_data_content_preview(int $recordid): stdClass {
|
||||
return (object)[
|
||||
'id' => 0,
|
||||
'fieldid' => $this->field->id,
|
||||
'recordid' => $recordid,
|
||||
'content' => (string) time(),
|
||||
'content1' => null,
|
||||
'content2' => null,
|
||||
'content3' => null,
|
||||
'content4' => null,
|
||||
];
|
||||
}
|
||||
|
||||
function display_add_field($recordid = 0, $formdata = null) {
|
||||
global $DB, $OUTPUT;
|
||||
|
||||
@ -164,11 +181,11 @@ class data_field_date extends data_field_base {
|
||||
}
|
||||
|
||||
function display_browse_field($recordid, $template) {
|
||||
global $CFG, $DB;
|
||||
|
||||
if ($content = $DB->get_field('data_content', 'content', array('fieldid'=>$this->field->id, 'recordid'=>$recordid))) {
|
||||
return userdate($content, get_string('strftimedate'), 0);
|
||||
$content = $this->get_data_content($recordid);
|
||||
if (!$content || empty($content->content)) {
|
||||
return '';
|
||||
}
|
||||
return userdate($content->content, get_string('strftimedate'), 0);
|
||||
}
|
||||
|
||||
function get_sort_sql($fieldname) {
|
||||
|
@ -25,6 +25,23 @@
|
||||
class data_field_file extends data_field_base {
|
||||
var $type = 'file';
|
||||
|
||||
public function supports_preview(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function get_data_content_preview(int $recordid): stdClass {
|
||||
return (object)[
|
||||
'id' => 0,
|
||||
'fieldid' => $this->field->id,
|
||||
'recordid' => $recordid,
|
||||
'content' => 'samplefile.csv',
|
||||
'content1' => 'samplefile.csv',
|
||||
'content2' => null,
|
||||
'content3' => null,
|
||||
'content4' => null,
|
||||
];
|
||||
}
|
||||
|
||||
function display_add_field($recordid = 0, $formdata = null) {
|
||||
global $DB, $OUTPUT, $PAGE;
|
||||
|
||||
@ -111,7 +128,7 @@ class data_field_file extends data_field_base {
|
||||
function get_file($recordid, $content=null) {
|
||||
global $DB;
|
||||
if (empty($content)) {
|
||||
if (!$content = $DB->get_record('data_content', array('fieldid'=>$this->field->id, 'recordid'=>$recordid))) {
|
||||
if (!$content = $this->get_data_content($recordid)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -124,28 +141,48 @@ class data_field_file extends data_field_base {
|
||||
}
|
||||
|
||||
function display_browse_field($recordid, $template) {
|
||||
global $CFG, $DB, $OUTPUT;
|
||||
global $OUTPUT;
|
||||
|
||||
if (!$content = $DB->get_record('data_content', array('fieldid'=>$this->field->id, 'recordid'=>$recordid))) {
|
||||
$content = $this->get_data_content($recordid);
|
||||
|
||||
if (!$content || empty($content->content)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (empty($content->content)) {
|
||||
return '';
|
||||
$file = null;
|
||||
$url = '';
|
||||
$name = !empty($content->content1) ? $content->content1 : $content->content;
|
||||
|
||||
if ($this->preview) {
|
||||
$file = (object)[
|
||||
'filename' => $content->content,
|
||||
'mimetype' => 'text/csv',
|
||||
];
|
||||
$name = $content->content;
|
||||
} else {
|
||||
$file = $this->get_file($recordid, $content);
|
||||
if (!$file) {
|
||||
return '';
|
||||
}
|
||||
$fileurl = moodle_url::make_pluginfile_url(
|
||||
$file->get_contextid(),
|
||||
$file->get_component(),
|
||||
$file->get_filearea(),
|
||||
$file->get_itemid(),
|
||||
$file->get_filepath(),
|
||||
$file->get_filename()
|
||||
);
|
||||
$url = $fileurl->out();
|
||||
}
|
||||
|
||||
if (!$file = $this->get_file($recordid, $content)) {
|
||||
return '';
|
||||
}
|
||||
$icon = $OUTPUT->pix_icon(
|
||||
file_file_icon($file),
|
||||
get_mimetype_description($file),
|
||||
'moodle',
|
||||
['width' => 16, 'height' => 16]
|
||||
);
|
||||
|
||||
$name = empty($content->content1) ? $file->get_filename() : $content->content1;
|
||||
$src = file_encode_url($CFG->wwwroot.'/pluginfile.php', '/'.$this->context->id.'/mod_data/content/'.$content->id.'/'.$file->get_filename());
|
||||
$width = $this->field->param1 ? ' width = "'.s($this->field->param1).'" ':' ';
|
||||
$height = $this->field->param2 ? ' height = "'.s($this->field->param2).'" ':' ';
|
||||
|
||||
$str = $OUTPUT->pix_icon(file_file_icon($file), get_mimetype_description($file), 'moodle', array('width' => 16, 'height' => 16)). ' '.
|
||||
'<a href="'.$src.'" >'.s($name).'</a>';
|
||||
return $str;
|
||||
return $icon . ' <a class="data-field-link" href="'.$url.'" >' . s($name) . '</a>';
|
||||
}
|
||||
|
||||
|
||||
|
@ -43,6 +43,23 @@ class data_field_latlong extends data_field_base {
|
||||
);
|
||||
// Other map sources listed at http://kvaleberg.com/extensions/mapsources/index.php?params=51_30.4167_N_0_7.65_W_region:earth
|
||||
|
||||
public function supports_preview(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function get_data_content_preview(int $recordid): stdClass {
|
||||
return (object)[
|
||||
'id' => 0,
|
||||
'fieldid' => $this->field->id,
|
||||
'recordid' => $recordid,
|
||||
'content' => 41.391205,
|
||||
'content1' => 2.163873,
|
||||
'content2' => null,
|
||||
'content3' => null,
|
||||
'content4' => null,
|
||||
];
|
||||
}
|
||||
|
||||
function display_add_field($recordid = 0, $formdata = null) {
|
||||
global $CFG, $DB, $OUTPUT;
|
||||
|
||||
@ -145,70 +162,79 @@ class data_field_latlong extends data_field_base {
|
||||
}
|
||||
|
||||
function display_browse_field($recordid, $template) {
|
||||
global $CFG, $DB;
|
||||
if ($content = $DB->get_record('data_content', array('fieldid'=>$this->field->id, 'recordid'=>$recordid))) {
|
||||
$lat = $content->content;
|
||||
if (strlen($lat) < 1) {
|
||||
return false;
|
||||
}
|
||||
$long = $content->content1;
|
||||
if (strlen($long) < 1) {
|
||||
return false;
|
||||
}
|
||||
// We use format_float to display in the regional format.
|
||||
if($lat < 0) {
|
||||
$compasslat = format_float(-$lat, 4) . '°S';
|
||||
} else {
|
||||
$compasslat = format_float($lat, 4) . '°N';
|
||||
}
|
||||
if($long < 0) {
|
||||
$compasslong = format_float(-$long, 4) . '°W';
|
||||
} else {
|
||||
$compasslong = format_float($long, 4) . '°E';
|
||||
}
|
||||
global $CFG;
|
||||
|
||||
// Now let's create the jump-to-services link
|
||||
$servicesshown = explode(',', $this->field->param1);
|
||||
|
||||
// These are the different things that can be magically inserted into URL schemes
|
||||
$urlreplacements = array(
|
||||
'@lat@'=> $lat,
|
||||
'@long@'=> $long,
|
||||
'@wwwroot@'=> $CFG->wwwroot,
|
||||
'@contentid@'=> $content->id,
|
||||
'@dataid@'=> $this->data->id,
|
||||
'@courseid@'=> $this->data->course,
|
||||
'@fieldid@'=> $content->fieldid,
|
||||
'@recordid@'=> $content->recordid,
|
||||
);
|
||||
|
||||
if(sizeof($servicesshown)==1 && $servicesshown[0]) {
|
||||
$str = " <a href='"
|
||||
. str_replace(array_keys($urlreplacements), array_values($urlreplacements), $this->linkoutservices[$servicesshown[0]])
|
||||
."' title='$servicesshown[0]'>$compasslat $compasslong</a>";
|
||||
} elseif (sizeof($servicesshown)>1) {
|
||||
$str = '<form id="latlongfieldbrowse">';
|
||||
$str .= "$compasslat, $compasslong\n";
|
||||
$str .= "<label class='accesshide' for='jumpto'>". get_string('jumpto') ."</label>";
|
||||
$str .= '<select id="jumpto" name="jumpto" class="custom-select">';
|
||||
foreach($servicesshown as $servicename){
|
||||
// Add a link to a service
|
||||
$str .= "\n <option value='"
|
||||
. str_replace(array_keys($urlreplacements), array_values($urlreplacements), $this->linkoutservices[$servicename])
|
||||
. "'>".htmlspecialchars($servicename)."</option>";
|
||||
}
|
||||
// NB! If you are editing this, make sure you don't break the javascript reference "previousSibling"
|
||||
// which allows the "Go" button to refer to the drop-down selector.
|
||||
$str .= '\n</select><input type="button" class="btn ml-1 btn-secondary" value="' . get_string('go');
|
||||
$str .= '" onclick="if(previousSibling.value){self.location=previousSibling.value}"/>';
|
||||
$str .= '</form>';
|
||||
} else {
|
||||
$str = "$compasslat, $compasslong";
|
||||
}
|
||||
|
||||
return $str;
|
||||
$content = $this->get_data_content($recordid);
|
||||
if (!$content) {
|
||||
return '';
|
||||
}
|
||||
return false;
|
||||
|
||||
$lat = $content->content;
|
||||
if (strlen($lat) < 1) {
|
||||
return '';
|
||||
}
|
||||
$long = $content->content1;
|
||||
if (strlen($long) < 1) {
|
||||
return '';
|
||||
}
|
||||
// We use format_float to display in the regional format.
|
||||
if ($lat < 0) {
|
||||
$compasslat = format_float(-$lat, 4) . '°S';
|
||||
} else {
|
||||
$compasslat = format_float($lat, 4) . '°N';
|
||||
}
|
||||
if ($long < 0) {
|
||||
$compasslong = format_float(-$long, 4) . '°W';
|
||||
} else {
|
||||
$compasslong = format_float($long, 4) . '°E';
|
||||
}
|
||||
|
||||
// Now let's create the jump-to-services link.
|
||||
$servicesshown = explode(',', $this->field->param1);
|
||||
|
||||
// These are the different things that can be magically inserted into URL schemes.
|
||||
$urlreplacements = array(
|
||||
'@lat@' => $lat,
|
||||
'@long@' => $long,
|
||||
'@wwwroot@' => $CFG->wwwroot,
|
||||
'@contentid@' => $content->id,
|
||||
'@dataid@' => $this->data->id,
|
||||
'@courseid@' => $this->data->course,
|
||||
'@fieldid@' => $content->fieldid,
|
||||
'@recordid@' => $content->recordid,
|
||||
);
|
||||
|
||||
if (count($servicesshown) == 1 && $servicesshown[0]) {
|
||||
$str = " <a class=\"data-field-link\" href='"
|
||||
. str_replace(
|
||||
array_keys($urlreplacements),
|
||||
array_values($urlreplacements),
|
||||
$this->linkoutservices[$servicesshown[0]]
|
||||
) . "' title='$servicesshown[0]'>$compasslat $compasslong</a>";
|
||||
} else if (count($servicesshown) > 1) {
|
||||
$str = '<form id="latlongfieldbrowse" class="data-field-html">';
|
||||
$str .= "$compasslat, $compasslong\n";
|
||||
$str .= "<label class='accesshide' for='jumpto'>". get_string('jumpto') ."</label>";
|
||||
$str .= '<select id="jumpto" name="jumpto" class="custom-select">';
|
||||
foreach ($servicesshown as $servicename) {
|
||||
// Add a link to a service.
|
||||
$str .= "\n <option value='"
|
||||
. str_replace(
|
||||
array_keys($urlreplacements),
|
||||
array_values($urlreplacements),
|
||||
$this->linkoutservices[$servicename]
|
||||
) . "'>".htmlspecialchars($servicename)."</option>";
|
||||
}
|
||||
// NB! If you are editing this, make sure you don't break the javascript reference "previousSibling"
|
||||
// which allows the "Go" button to refer to the drop-down selector.
|
||||
$str .= '\n</select><input type="button" class="btn ml-1 btn-secondary" value="' . get_string('go');
|
||||
$str .= '" onclick="if(previousSibling.value){self.location=previousSibling.value}"/>';
|
||||
$str .= '</form>';
|
||||
} else {
|
||||
$str = "$compasslat, $compasslong";
|
||||
}
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
function update_content_import($recordid, $value, $name='') {
|
||||
|
@ -32,6 +32,26 @@ class data_field_menu extends data_field_base {
|
||||
*/
|
||||
protected static $priority = self::HIGH_PRIORITY;
|
||||
|
||||
public function supports_preview(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function get_data_content_preview(int $recordid): stdClass {
|
||||
$options = explode("\n", $this->field->param1);
|
||||
$options = array_map('trim', $options);
|
||||
$selected = $options[$recordid % count($options)];
|
||||
return (object)[
|
||||
'id' => 0,
|
||||
'fieldid' => $this->field->id,
|
||||
'recordid' => $recordid,
|
||||
'content' => $selected,
|
||||
'content1' => null,
|
||||
'content2' => null,
|
||||
'content3' => null,
|
||||
'content4' => null,
|
||||
];
|
||||
}
|
||||
|
||||
function display_add_field($recordid = 0, $formdata = null) {
|
||||
global $DB, $OUTPUT;
|
||||
|
||||
|
@ -32,6 +32,27 @@ class data_field_multimenu extends data_field_base {
|
||||
* */
|
||||
protected static $priority = self::LOW_PRIORITY;
|
||||
|
||||
public function supports_preview(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function get_data_content_preview(int $recordid): stdClass {
|
||||
$options = explode("\n", $this->field->param1);
|
||||
$options = array_map('trim', $options);
|
||||
$selected = $options[$recordid % count($options)];
|
||||
$selected .= '##' . $options[($recordid + 1) % count($options)];
|
||||
return (object)[
|
||||
'id' => 0,
|
||||
'fieldid' => $this->field->id,
|
||||
'recordid' => $recordid,
|
||||
'content' => $selected,
|
||||
'content1' => null,
|
||||
'content2' => null,
|
||||
'content3' => null,
|
||||
'content4' => null,
|
||||
];
|
||||
}
|
||||
|
||||
function display_add_field($recordid = 0, $formdata = null) {
|
||||
global $DB, $OUTPUT;
|
||||
|
||||
@ -250,28 +271,23 @@ class data_field_multimenu extends data_field_base {
|
||||
|
||||
|
||||
function display_browse_field($recordid, $template) {
|
||||
global $DB;
|
||||
|
||||
if ($content = $DB->get_record('data_content', array('fieldid'=>$this->field->id, 'recordid'=>$recordid))) {
|
||||
if (strval($content->content) === '') {
|
||||
return false;
|
||||
}
|
||||
|
||||
$options = explode("\n",$this->field->param1);
|
||||
$options = array_map('trim', $options);
|
||||
|
||||
$contentArr = explode('##', $content->content);
|
||||
$str = '';
|
||||
foreach ($contentArr as $line) {
|
||||
if (!in_array($line, $options)) {
|
||||
// hmm, looks like somebody edited the field definition
|
||||
continue;
|
||||
}
|
||||
$str .= $line . "<br />\n";
|
||||
}
|
||||
return $str;
|
||||
$content = $this->get_data_content($recordid);
|
||||
if (!$content || empty($content->content)) {
|
||||
return '';
|
||||
}
|
||||
return false;
|
||||
$options = explode("\n", $this->field->param1);
|
||||
$options = array_map('trim', $options);
|
||||
|
||||
$contentarray = explode('##', $content->content);
|
||||
$str = '';
|
||||
foreach ($contentarray as $line) {
|
||||
if (!in_array($line, $options)) {
|
||||
// Hmm, looks like somebody edited the field definition.
|
||||
continue;
|
||||
}
|
||||
$str .= $line . "<br />\n";
|
||||
}
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -25,6 +25,23 @@
|
||||
class data_field_number extends data_field_base {
|
||||
var $type = 'number';
|
||||
|
||||
public function supports_preview(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function get_data_content_preview(int $recordid): stdClass {
|
||||
return (object)[
|
||||
'id' => 0,
|
||||
'fieldid' => $this->field->id,
|
||||
'recordid' => $recordid,
|
||||
'content' => 1233 + $recordid,
|
||||
'content1' => null,
|
||||
'content2' => null,
|
||||
'content3' => null,
|
||||
'content4' => null,
|
||||
];
|
||||
}
|
||||
|
||||
function update_content($recordid, $value, $name='') {
|
||||
global $DB;
|
||||
|
||||
@ -46,27 +63,21 @@ class data_field_number extends data_field_base {
|
||||
}
|
||||
|
||||
function display_browse_field($recordid, $template) {
|
||||
global $DB;
|
||||
|
||||
if ($content = $DB->get_record('data_content', array('fieldid'=>$this->field->id, 'recordid'=>$recordid))) {
|
||||
if (strlen($content->content) < 1) {
|
||||
return false;
|
||||
}
|
||||
$number = $content->content;
|
||||
$decimals = trim($this->field->param1);
|
||||
// only apply number formatting if param1 contains an integer number >= 0:
|
||||
if (preg_match("/^\d+$/", $decimals)) {
|
||||
$decimals = $decimals * 1;
|
||||
// removes leading zeros (eg. '007' -> '7'; '00' -> '0')
|
||||
$str = format_float($number, $decimals, true);
|
||||
// For debugging only:
|
||||
# $str .= " ($decimals)";
|
||||
} else {
|
||||
$str = $number;
|
||||
}
|
||||
return $str;
|
||||
$content = $this->get_data_content($recordid);
|
||||
if (!$content || empty($content->content)) {
|
||||
return '';
|
||||
}
|
||||
return false;
|
||||
$number = $content->content;
|
||||
$decimals = trim($this->field->param1);
|
||||
// Only apply number formatting if param1 contains an integer number >= 0.
|
||||
if (preg_match("/^\d+$/", $decimals)) {
|
||||
$decimals = $decimals * 1;
|
||||
// Removes leading zeros (eg. '007' -> '7'; '00' -> '0').
|
||||
$str = format_float($number, $decimals, true);
|
||||
} else {
|
||||
$str = $number;
|
||||
}
|
||||
return $str;
|
||||
}
|
||||
|
||||
function display_search_field($value = '') {
|
||||
@ -125,5 +136,3 @@ class data_field_number extends data_field_base {
|
||||
return $configs;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -27,6 +27,23 @@ class data_field_picture extends data_field_base {
|
||||
var $previewwidth = 50;
|
||||
var $previewheight = 50;
|
||||
|
||||
public function supports_preview(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function get_data_content_preview(int $recordid): stdClass {
|
||||
return (object)[
|
||||
'id' => 0,
|
||||
'fieldid' => $this->field->id,
|
||||
'recordid' => $recordid,
|
||||
'content' => 'datafield_picture/preview',
|
||||
'content1' => get_string('sample', 'datafield_picture'),
|
||||
'content2' => null,
|
||||
'content3' => null,
|
||||
'content4' => null,
|
||||
];
|
||||
}
|
||||
|
||||
function display_add_field($recordid = 0, $formdata = null) {
|
||||
global $CFG, $DB, $OUTPUT, $USER, $PAGE;
|
||||
|
||||
@ -158,32 +175,43 @@ class data_field_picture extends data_field_base {
|
||||
}
|
||||
|
||||
function display_browse_field($recordid, $template) {
|
||||
global $CFG, $DB;
|
||||
global $OUTPUT;
|
||||
|
||||
if (!$content = $DB->get_record('data_content', array('fieldid'=>$this->field->id, 'recordid'=>$recordid))) {
|
||||
return false;
|
||||
}
|
||||
$content = $this->get_data_content($recordid);
|
||||
|
||||
if (empty($content->content)) {
|
||||
if (!$content || empty($content->content)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$alt = $content->content1;
|
||||
$title = $alt;
|
||||
|
||||
if ($template == 'listtemplate') {
|
||||
$src = file_encode_url($CFG->wwwroot.'/pluginfile.php', '/'.$this->context->id.'/mod_data/content/'.$content->id.'/'.'thumb_'.$content->content);
|
||||
// no need to add width/height, because the thumb is resized properly
|
||||
$str = '<a href="view.php?d='.$this->field->dataid.'&rid='.$recordid.'"><img src="'.$src.'" alt="'.s($alt).'" title="'.s($title).'" class="list_picture"/></a>';
|
||||
$width = $this->field->param1 ? ' width="' . s($this->field->param1) . '" ' : ' ';
|
||||
$height = $this->field->param2 ? ' height="' . s($this->field->param2) . '" ' : ' ';
|
||||
|
||||
} else {
|
||||
$src = file_encode_url($CFG->wwwroot.'/pluginfile.php', '/'.$this->context->id.'/mod_data/content/'.$content->id.'/'.$content->content);
|
||||
$width = $this->field->param1 ? ' width="'.s($this->field->param1).'" ':' ';
|
||||
$height = $this->field->param2 ? ' height="'.s($this->field->param2).'" ':' ';
|
||||
$str = '<a href="'.$src.'"><img '.$width.$height.' src="'.$src.'" alt="'.s($alt).'" title="'.s($title).'" class="list_picture"/></a>';
|
||||
if ($this->preview) {
|
||||
$imgurl = $OUTPUT->image_url('sample', 'datafield_picture');
|
||||
return '<img ' . $width . $height . ' src="' . $imgurl . '" alt="' . s($alt) . '" class="list_picture"/>';
|
||||
}
|
||||
|
||||
return $str;
|
||||
if ($template == 'listtemplate') {
|
||||
$filename = 'thumb_' . $content->content;
|
||||
// Thumbnails are already converted to the correct width and height.
|
||||
$width = '';
|
||||
$height = '';
|
||||
$url = new moodle_url('/mod/data/view.php', ['d' => $this->field->dataid, 'rid' => $recordid]);
|
||||
} else {
|
||||
$filename = $content->content;
|
||||
$url = null;
|
||||
}
|
||||
$imgurl = moodle_url::make_pluginfile_url($this->context->id, 'mod_data', 'content', $content->id, '/', $filename);
|
||||
|
||||
if (!$url) {
|
||||
$url = $imgurl;
|
||||
}
|
||||
$img = '<img ' . $width . $height . ' src="' . $imgurl->out() . '" alt="' . s($alt) .
|
||||
'" title="' . s($title) . '" class="list_picture"/>';
|
||||
return '<a class="data-field-link" href="' . $url->out() . '">' . $img . '</a>';
|
||||
}
|
||||
|
||||
function update_field() {
|
||||
|
@ -27,3 +27,4 @@
|
||||
$string['pluginname'] = 'Image';
|
||||
$string['fieldtypelabel'] = 'Image';
|
||||
$string['privacy:metadata'] = 'The Image field component doesn\'t store any personal data; it uses tables defined in mod_data.';
|
||||
$string['sample'] = 'Image description sample';
|
||||
|
BIN
mod/data/field/picture/pix/sample.png
Normal file
BIN
mod/data/field/picture/pix/sample.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.1 KiB |
42
mod/data/field/picture/pix/sample.svg
Normal file
42
mod/data/field/picture/pix/sample.svg
Normal file
@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
id="svg8"
|
||||
version="1.1"
|
||||
viewBox="0 0 105.83334 71.4375"
|
||||
height="71.4375mm"
|
||||
width="105.83334mm">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
transform="translate(276.67856,-129.93452)"
|
||||
id="layer1">
|
||||
<rect
|
||||
y="129.93452"
|
||||
x="-276.67856"
|
||||
height="71.4375"
|
||||
width="105.83334"
|
||||
id="rect864"
|
||||
style="opacity:1;vector-effect:none;fill:#e9eef1;fill-opacity:1;stroke:none;stroke-width:3.30729175;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke" />
|
||||
<path
|
||||
id="rect826"
|
||||
d="m -239.31599,150.5547 c -1.81012,0 -3.32227,1.50707 -3.32227,3.31918 v 23.55955 c 0,1.8121 1.51215,3.3184 3.32227,3.3184 h 31.10818 c 1.81012,0 3.32228,-1.5063 3.32228,-3.3184 v -23.55955 c 0,-1.81211 -1.51216,-3.31918 -3.32228,-3.31918 z m -0.004,2.43655 h 31.1149 c 0.46886,0 0.8462,0.3776 0.8462,0.84646 v 23.63112 c 0,0.46886 -0.37734,0.84646 -0.8462,0.84646 h -31.1149 c -0.46886,0 -0.8462,-0.3776 -0.8462,-0.84646 v -23.63112 c 0,-0.46886 0.37734,-0.84646 0.8462,-0.84646 z m 5.40122,2.67271 a 3.7417735,3.7417735 0 0 0 -3.74163,3.74189 3.7417735,3.7417735 0 0 0 3.74163,3.74163 3.7417735,3.7417735 0 0 0 3.74189,-3.74163 3.7417735,3.7417735 0 0 0 -3.74189,-3.74189 z m 15.8024,3.17526 -10.0273,10.02703 -3.24063,-3.24063 -6.27636,6.27636 v 3.7406 h 27.79598 v -8.55141 z"
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#babec1;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.30072331;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.2 KiB |
@ -32,6 +32,26 @@ class data_field_radiobutton extends data_field_base {
|
||||
*/
|
||||
protected static $priority = self::HIGH_PRIORITY;
|
||||
|
||||
public function supports_preview(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function get_data_content_preview(int $recordid): stdClass {
|
||||
$options = explode("\n", $this->field->param1);
|
||||
$options = array_map('trim', $options);
|
||||
$selected = $options[$recordid % count($options)];
|
||||
return (object)[
|
||||
'id' => 0,
|
||||
'fieldid' => $this->field->id,
|
||||
'recordid' => $recordid,
|
||||
'content' => $selected,
|
||||
'content1' => null,
|
||||
'content2' => null,
|
||||
'content3' => null,
|
||||
'content4' => null,
|
||||
];
|
||||
}
|
||||
|
||||
function display_add_field($recordid = 0, $formdata = null) {
|
||||
global $CFG, $DB, $OUTPUT;
|
||||
|
||||
@ -153,4 +173,3 @@ class data_field_radiobutton extends data_field_base {
|
||||
return $configs;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,23 @@ class data_field_text extends data_field_base {
|
||||
*/
|
||||
protected static $priority = self::MAX_PRIORITY;
|
||||
|
||||
public function supports_preview(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function get_data_content_preview(int $recordid): stdClass {
|
||||
return (object)[
|
||||
'id' => 0,
|
||||
'fieldid' => $this->field->id,
|
||||
'recordid' => $recordid,
|
||||
'content' => get_string('sample', 'datafield_text'),
|
||||
'content1' => null,
|
||||
'content2' => null,
|
||||
'content3' => null,
|
||||
'content4' => null,
|
||||
];
|
||||
}
|
||||
|
||||
function display_search_field($value = '') {
|
||||
return '<label class="accesshide" for="f_' . $this->field->id . '">' . $this->field->name.'</label>' .
|
||||
'<input type="text" class="form-control" size="16" id="f_' . $this->field->id . '" ' .
|
||||
@ -81,5 +98,3 @@ class data_field_text extends data_field_base {
|
||||
return $configs;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -27,3 +27,4 @@
|
||||
$string['pluginname'] = 'Short text';
|
||||
$string['fieldtypelabel'] = 'Short text';
|
||||
$string['privacy:metadata'] = 'The Short text field component doesn\'t store any personal data; it uses tables defined in mod_data.';
|
||||
$string['sample'] = 'This is a short text';
|
||||
|
@ -38,6 +38,23 @@ class data_field_textarea extends data_field_base {
|
||||
*/
|
||||
protected static $priority = self::LOW_PRIORITY;
|
||||
|
||||
public function supports_preview(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function get_data_content_preview(int $recordid): stdClass {
|
||||
return (object)[
|
||||
'id' => 0,
|
||||
'fieldid' => $this->field->id,
|
||||
'recordid' => $recordid,
|
||||
'content' => get_string('sample', 'datafield_textarea'),
|
||||
'content1' => 1,
|
||||
'content2' => null,
|
||||
'content3' => null,
|
||||
'content4' => null,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns options for embedded files
|
||||
*
|
||||
@ -255,23 +272,26 @@ class data_field_textarea extends data_field_base {
|
||||
* @return bool|string
|
||||
*/
|
||||
function display_browse_field($recordid, $template) {
|
||||
global $DB;
|
||||
|
||||
if ($content = $DB->get_record('data_content', array('fieldid' => $this->field->id, 'recordid' => $recordid))) {
|
||||
if (isset($content->content)) {
|
||||
$options = new stdClass();
|
||||
if ($this->field->param1 == '1') { // We are autolinking this field, so disable linking within us
|
||||
$options->filter = false;
|
||||
}
|
||||
$options->para = false;
|
||||
$str = file_rewrite_pluginfile_urls($content->content, 'pluginfile.php', $this->context->id, 'mod_data', 'content', $content->id, $this->get_options());
|
||||
$str = format_text($str, $content->content1, $options);
|
||||
} else {
|
||||
$str = '';
|
||||
}
|
||||
return $str;
|
||||
$content = $this->get_data_content($recordid);
|
||||
if (!$content || !isset($content->content)) {
|
||||
return '';
|
||||
}
|
||||
return false;
|
||||
$options = new stdClass();
|
||||
if ($this->field->param1 == '1') { // We are autolinking this field, so disable linking within us.
|
||||
$options->filter = false;
|
||||
}
|
||||
$options->para = false;
|
||||
$str = file_rewrite_pluginfile_urls(
|
||||
$content->content,
|
||||
'pluginfile.php',
|
||||
$this->context->id,
|
||||
'mod_data',
|
||||
'content',
|
||||
$content->id,
|
||||
$this->get_options()
|
||||
);
|
||||
$str = format_text($str, $content->content1, $options);
|
||||
return '<div class="data-field-html">' . $str . '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -29,3 +29,4 @@ $string['maxbytes_desc'] = 'If set to zero will be unlimited by default';
|
||||
$string['pluginname'] = 'Text area';
|
||||
$string['fieldtypelabel'] = 'Text area';
|
||||
$string['privacy:metadata'] = 'The Text area field component doesn\'t store any personal data; it uses tables defined in mod_data.';
|
||||
$string['sample'] = '<p>This is a text area. It lets users enter long form text which spans over multiple lines.</p><p>You can choose the size of your text areas in rows and columns.</p><p>The text in a text area can be formatted, for example in <b>bold</b>, and also include <a href="#">hyperlinks</a> or images.</p>';
|
||||
|
@ -31,6 +31,23 @@ class data_field_url extends data_field_base {
|
||||
*/
|
||||
protected static $priority = self::MIN_PRIORITY;
|
||||
|
||||
public function supports_preview(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function get_data_content_preview(int $recordid): stdClass {
|
||||
return (object)[
|
||||
'id' => 0,
|
||||
'fieldid' => $this->field->id,
|
||||
'recordid' => $recordid,
|
||||
'content' => 'https://example.com',
|
||||
'content1' => null,
|
||||
'content2' => null,
|
||||
'content3' => null,
|
||||
'content4' => null,
|
||||
];
|
||||
}
|
||||
|
||||
function display_add_field($recordid = 0, $formdata = null) {
|
||||
global $CFG, $DB, $OUTPUT, $PAGE;
|
||||
|
||||
@ -138,38 +155,39 @@ class data_field_url extends data_field_base {
|
||||
}
|
||||
|
||||
function display_browse_field($recordid, $template) {
|
||||
global $DB;
|
||||
|
||||
if ($content = $DB->get_record('data_content', array('fieldid'=>$this->field->id, 'recordid'=>$recordid))) {
|
||||
$url = empty($content->content)? '':$content->content;
|
||||
$text = empty($content->content1)? '':$content->content1;
|
||||
if (empty($url) or ($url == 'http://')) {
|
||||
return '';
|
||||
}
|
||||
if (!empty($this->field->param2)) {
|
||||
// param2 forces the text to something
|
||||
$text = $this->field->param2;
|
||||
}
|
||||
if ($this->field->param1) {
|
||||
// param1 defines whether we want to autolink the url.
|
||||
$attributes = array();
|
||||
if ($this->field->param3) {
|
||||
// param3 defines whether this URL should open in a new window.
|
||||
$attributes['target'] = '_blank';
|
||||
$attributes['rel'] = 'noreferrer';
|
||||
}
|
||||
|
||||
if (empty($text)) {
|
||||
$text = $url;
|
||||
}
|
||||
|
||||
$str = html_writer::link($url, $text, $attributes);
|
||||
} else {
|
||||
$str = $url;
|
||||
}
|
||||
return $str;
|
||||
$content = $this->get_data_content($recordid);
|
||||
if (!$content) {
|
||||
return '';
|
||||
}
|
||||
return false;
|
||||
|
||||
$url = empty($content->content) ? '' : $content->content;
|
||||
$text = empty($content->content1) ? '' : $content->content1;
|
||||
if (empty($url) || ($url == 'http://')) {
|
||||
return '';
|
||||
}
|
||||
if (!empty($this->field->param2)) {
|
||||
// Param2 forces the text to something.
|
||||
$text = $this->field->param2;
|
||||
}
|
||||
if ($this->field->param1) {
|
||||
// Param1 defines whether we want to autolink the url.
|
||||
$attributes = ['class' => 'data-field-link'];
|
||||
if ($this->field->param3) {
|
||||
// Param3 defines whether this URL should open in a new window.
|
||||
$attributes['target'] = '_blank';
|
||||
$attributes['rel'] = 'noreferrer';
|
||||
}
|
||||
|
||||
if (empty($text)) {
|
||||
$text = $url;
|
||||
}
|
||||
|
||||
$str = html_writer::link($url, $text, $attributes);
|
||||
} else {
|
||||
$str = $url;
|
||||
}
|
||||
return $str;
|
||||
}
|
||||
|
||||
function update_content_import($recordid, $value, $name='') {
|
||||
|
@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
@ -22,23 +21,49 @@
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @package mod_data
|
||||
*/
|
||||
use mod_data\manager;
|
||||
use mod_data\preset;
|
||||
|
||||
define('NO_MOODLE_COOKIES', true); // session not used here
|
||||
|
||||
require_once('../../config.php');
|
||||
|
||||
$d = optional_param('d', 0, PARAM_INT); // database id
|
||||
$id = optional_param('id', 0, PARAM_INT); // Course module id.
|
||||
$d = optional_param('d', 0, PARAM_INT); // Database id.
|
||||
$presetfullname = optional_param('preset', '', PARAM_PATH); // The directory the preset is in.
|
||||
|
||||
$PAGE->set_url('/mod/data/js.php', array('d'=>$d));
|
||||
$lifetime = 600; // Seconds to cache this stylesheet.
|
||||
$url = new moodle_url('/mod/data/js.php');
|
||||
|
||||
$lifetime = 600; // Seconds to cache this stylesheet
|
||||
$manager = null;
|
||||
if ($id) {
|
||||
list($course, $cm) = get_course_and_cm_from_cmid($id, manager::MODULE);
|
||||
$manager = manager::create_from_coursemodule($cm);
|
||||
$instance = $manager->get_instance();
|
||||
} else {
|
||||
// We must have the database activity id.
|
||||
$d = required_param('d', PARAM_INT);
|
||||
$instance = $DB->get_record('data', ['id' => $d], '*', MUST_EXIST);
|
||||
$manager = manager::create_from_instance($instance);
|
||||
}
|
||||
$url->param('d', $instance->id);
|
||||
|
||||
if ($data = $DB->get_record('data', array('id'=>$d))) {
|
||||
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', time()) . ' GMT');
|
||||
header('Expires: ' . gmdate("D, d M Y H:i:s", time() + $lifetime) . ' GMT');
|
||||
header('Cache-control: max_age = '. $lifetime);
|
||||
header('Pragma: ');
|
||||
header('Content-type: application/javascript; charset=utf-8'); // Correct MIME type.
|
||||
// Get the content.
|
||||
if ($presetfullname) {
|
||||
$url->param('preset', $presetfullname);
|
||||
$preset = preset::create_from_fullname($manager, $presetfullname);
|
||||
$content = $preset->get_template_content('jstemplate');
|
||||
$lifetime = 60; // Preset preview does not need a long cache.
|
||||
} else {
|
||||
$content = $instance->jstemplate;
|
||||
}
|
||||
|
||||
echo $data->jstemplate;
|
||||
}
|
||||
$PAGE->set_url($url);
|
||||
|
||||
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', time()) . ' GMT');
|
||||
header('Expires: ' . gmdate("D, d M Y H:i:s", time() + $lifetime) . ' GMT');
|
||||
header('Cache-control: max_age = '. $lifetime);
|
||||
header('Pragma: ');
|
||||
header('Content-type: application/javascript; charset=utf-8'); // Correct MIME type.
|
||||
|
||||
echo $content;
|
||||
|
@ -297,6 +297,7 @@ $string['nofieldindatabase'] = 'There are no fields defined for this database.';
|
||||
$string['nolisttemplate'] = 'List template is not yet defined';
|
||||
$string['nomatch'] = 'No matching entries found!';
|
||||
$string['nomaximum'] = 'No maximum';
|
||||
$string['nopreviewavailable'] = 'No preview available for {$a}';
|
||||
$string['norecords'] = 'No entries yet';
|
||||
$string['nosingletemplate'] = 'Single template is not yet defined';
|
||||
$string['notapproved'] = 'Entry is not approved yet.';
|
||||
@ -327,6 +328,7 @@ $string['presetinfo'] = 'Saving as a preset will publish this template. Other us
|
||||
$string['presetnotselected'] = 'No preset has been selected.';
|
||||
$string['presets'] = 'Presets';
|
||||
$string['presetshelp'] = 'Choose a preset to use as a starting point.';
|
||||
$string['preview'] = 'Preview';
|
||||
$string['privacy:metadata:commentpurpose'] = 'Comments on database records';
|
||||
$string['privacy:metadata:data_content'] = 'Represents one answer to one field in database activity module';
|
||||
$string['privacy:metadata:data_content:fieldid'] = 'Field definition ID';
|
||||
@ -382,7 +384,7 @@ $string['saveaspreset_help'] = 'The save as preset feature publishes the templat
|
||||
$string['savedataaspreset'] = 'Save all fields and templates as preset';
|
||||
$string['saveaspresetmissingcapability'] = 'The user does not have permission to save the database as a preset.';
|
||||
$string['savesettings'] = 'Save settings';
|
||||
$string['savesuccess'] = 'Preset saved.';
|
||||
$string['savesuccess'] = 'Preset saved. <a href="{$a->url}">Preview preset</a>';
|
||||
$string['savetemplate'] = 'Save template';
|
||||
$string['search'] = 'Search';
|
||||
$string['search:activity'] = 'Database - activity information';
|
||||
|
100
mod/data/lib.php
100
mod/data/lib.php
@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
@ -85,6 +84,9 @@ class data_field_base { // Base class for Database Field Types (see field/*/
|
||||
/** priority value for maximum priority */
|
||||
const MAX_PRIORITY = 4;
|
||||
|
||||
/** @var bool whether the field is used in preview mode. */
|
||||
protected $preview = false;
|
||||
|
||||
/**
|
||||
* Constructor function
|
||||
*
|
||||
@ -148,6 +150,58 @@ class data_field_base { // Base class for Database Field Types (see field/*/
|
||||
return $this->field->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if the field type supports preview.
|
||||
*
|
||||
* Fields without a preview cannot be displayed in the preset preview.
|
||||
*
|
||||
* @return bool if the plugin supports preview.
|
||||
*/
|
||||
public function supports_preview(): bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a fake data_content for this field to be used in preset previews.
|
||||
*
|
||||
* Data plugins must override this method and support_preview in order to enable
|
||||
* preset preview for this field.
|
||||
*
|
||||
* @param int $recordid the fake record id
|
||||
* @return stdClass the fake record
|
||||
*/
|
||||
public function get_data_content_preview(int $recordid): stdClass {
|
||||
$message = get_string('nopreviewavailable', 'mod_data', $this->field->name);
|
||||
return (object)[
|
||||
'id' => 0,
|
||||
'fieldid' => $this->field->id,
|
||||
'recordid' => $recordid,
|
||||
'content' => "<span class=\"nopreview\">$message</span>",
|
||||
'content1' => null,
|
||||
'content2' => null,
|
||||
'content3' => null,
|
||||
'content4' => null,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the field to preview mode.
|
||||
*
|
||||
* @param bool $preview the new preview value
|
||||
*/
|
||||
public function set_preview(bool $preview) {
|
||||
$this->preview = $preview;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the field preview value.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function get_preview(): bool {
|
||||
return $this->preview;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This field just sets up a default field object
|
||||
@ -377,6 +431,23 @@ class data_field_base { // Base class for Database Field Types (see field/*/
|
||||
echo $OUTPUT->box_end();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the data_content of the field, or generate it if it is in preview mode.
|
||||
*
|
||||
* @param int $recordid the record id
|
||||
* @return stdClass|bool the record data or false if none
|
||||
*/
|
||||
protected function get_data_content(int $recordid) {
|
||||
global $DB;
|
||||
if ($this->preview) {
|
||||
return $this->get_data_content_preview($recordid);
|
||||
}
|
||||
return $DB->get_record(
|
||||
'data_content',
|
||||
['fieldid' => $this->field->id, 'recordid' => $recordid]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the content of the field in browse mode
|
||||
*
|
||||
@ -387,23 +458,18 @@ class data_field_base { // Base class for Database Field Types (see field/*/
|
||||
*/
|
||||
function display_browse_field($recordid, $template) {
|
||||
global $DB;
|
||||
|
||||
if ($content = $DB->get_record('data_content', array('fieldid'=>$this->field->id, 'recordid'=>$recordid))) {
|
||||
if (isset($content->content)) {
|
||||
$options = new stdClass();
|
||||
if ($this->field->param1 == '1') { // We are autolinking this field, so disable linking within us
|
||||
//$content->content = '<span class="nolink">'.$content->content.'</span>';
|
||||
//$content->content1 = FORMAT_HTML;
|
||||
$options->filter=false;
|
||||
}
|
||||
$options->para = false;
|
||||
$str = format_text($content->content, $content->content1, $options);
|
||||
} else {
|
||||
$str = '';
|
||||
}
|
||||
return $str;
|
||||
$content = $this->get_data_content($recordid);
|
||||
if (!$content || !isset($content->content)) {
|
||||
return '';
|
||||
}
|
||||
return false;
|
||||
$options = new stdClass();
|
||||
if ($this->field->param1 == '1') {
|
||||
// We are autolinking this field, so disable linking within us.
|
||||
$options->filter = false;
|
||||
}
|
||||
$options->para = false;
|
||||
$str = format_text($content->content, $content->content1, $options);
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -30,6 +30,8 @@
|
||||
|
||||
use mod_data\manager;
|
||||
use mod_data\preset;
|
||||
use mod_data\output\action_bar;
|
||||
use mod_data\output\preset_preview;
|
||||
|
||||
require_once('../../config.php');
|
||||
require_once($CFG->dirroot.'/mod/data/lib.php');
|
||||
@ -54,7 +56,7 @@ if ($id) {
|
||||
|
||||
$action = optional_param('action', 'view', PARAM_ALPHA); // The page action.
|
||||
$allowedactions = ['view', 'import', 'importzip', 'finishimport',
|
||||
'export'];
|
||||
'export', 'preview'];
|
||||
if (!in_array($action, $allowedactions)) {
|
||||
throw new moodle_exception('invalidaccess');
|
||||
}
|
||||
@ -112,6 +114,28 @@ if ($formimportzip->is_cancelled()) {
|
||||
redirect(new moodle_url('/mod/data/preset.php', ['d' => $data->id]));
|
||||
}
|
||||
|
||||
// Preset preview injects CSS and JS to the page and should be done before the page header.
|
||||
if ($action === 'preview') {
|
||||
$fullname = optional_param('fullname', '', PARAM_PATH); // The directory the preset is in.
|
||||
$templatename = optional_param('template', 'listtemplate', PARAM_ALPHA);
|
||||
// Find out preset owner userid and shortname.
|
||||
$preset = preset::create_from_fullname($manager, $fullname);
|
||||
// Validate if the user can view this preset.
|
||||
if (!$manager->can_view_preset($preset)) {
|
||||
throw new \moodle_exception('cannotaccesspresentsother', manager::PLUGINNAME);
|
||||
}
|
||||
$preview = new preset_preview($manager, $preset, $templatename);
|
||||
$preview->prepare_page($PAGE);
|
||||
// Print the preview screen.
|
||||
echo $OUTPUT->header();
|
||||
$actionbar = new action_bar($data->id, $url);
|
||||
echo $actionbar->get_presets_preview_action_bar($manager, $fullname, $templatename);
|
||||
echo $OUTPUT->heading(ucfirst($preset->name), 2, 'mb-4');
|
||||
echo $renderer->render($preview);
|
||||
echo $OUTPUT->footer();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
echo $OUTPUT->header();
|
||||
|
||||
if ($formdata = $formimportzip->get_data()) {
|
||||
|
@ -17,7 +17,7 @@
|
||||
<div id="collapseentry-##id##" class="collapse" role="tabpanel" aria-labelledby="entry-##id##">
|
||||
<div class="card-body">
|
||||
[[Summary]]
|
||||
<p class="mt-3"><a href="##moreurl##">View the full proposal</a></p>
|
||||
<p class="mt-3 preview-disabled"><a href="##moreurl##">View the full proposal</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,4 +1,26 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Database module preset forms.
|
||||
*
|
||||
* @package mod_data
|
||||
* @copyright 2010 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
if (!defined('MOODLE_INTERNAL')) {
|
||||
die('Direct access to this script is forbidden!');
|
||||
|
@ -149,3 +149,19 @@
|
||||
.entry-actionsmenu .dropdown-toggle::after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Preset preview styles */
|
||||
#page-mod-data-preset .nopreview {
|
||||
border: 1px solid var(--secondary);
|
||||
padding: 0.2rem;
|
||||
border-radius: 5px;
|
||||
}
|
||||
/* Disable links and buttons in the preset preview */
|
||||
.template-preview-content .preview-disabled,
|
||||
.template-preview-content .action-icon,
|
||||
.template-preview-content .dropdown-menu a,
|
||||
.template-preview-content .data-field-link,
|
||||
.template-preview-content .data-field-html a,
|
||||
.template-preview-content .data-field-html button {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
117
mod/data/templates/action_bar.mustache
Normal file
117
mod/data/templates/action_bar.mustache
Normal file
@ -0,0 +1,117 @@
|
||||
{{!
|
||||
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/>.
|
||||
}}
|
||||
{{!
|
||||
@template mod_data/action_bar
|
||||
|
||||
General actions bar at the top of the pages in the database activity.
|
||||
|
||||
Context variables required for this template:
|
||||
* d - The database instance id.
|
||||
* urlselect - The data object containing the required properties to render core/url_select.
|
||||
* fieldselect - The data object containing the required properties to render core/single_select.
|
||||
* saveaspreset - Whether to display the save as preset button (mod_data/save_as_preset).
|
||||
* exportpreset - The data object containing the required properties to render core/single_button.
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"d": 1,
|
||||
"hasback": true,
|
||||
"backurl": "https://example.com",
|
||||
"backtitle": "Back",
|
||||
"title": "Some title",
|
||||
"urlselect": {
|
||||
"id": "url_select_test",
|
||||
"action": "https://example.com/post",
|
||||
"formid": "url_select_form",
|
||||
"sesskey": "sesskey",
|
||||
"classes": "urlselect",
|
||||
"label": "",
|
||||
"helpicon": false,
|
||||
"showbutton": null,
|
||||
"options": [
|
||||
{
|
||||
"name": "Some name",
|
||||
"value": "/mod/data/someurl.php",
|
||||
"selected": false
|
||||
}
|
||||
],
|
||||
"disabled": false,
|
||||
"title": null
|
||||
},
|
||||
"extraurlselect": {
|
||||
"id": "url_select_test",
|
||||
"action": "https://example.com/post",
|
||||
"formid": "url_select_form",
|
||||
"sesskey": "sesskey",
|
||||
"classes": "urlselect",
|
||||
"label": "",
|
||||
"helpicon": false,
|
||||
"showbutton": null,
|
||||
"options": [
|
||||
{
|
||||
"name": "Some name",
|
||||
"value": "/mod/data/someurl.php",
|
||||
"selected": false
|
||||
}
|
||||
],
|
||||
"disabled": false,
|
||||
"title": null
|
||||
},
|
||||
"buttons": {
|
||||
"id": "single_button_test",
|
||||
"method" : "post",
|
||||
"formid": "single_button_form",
|
||||
"url" : "https://example.com/post",
|
||||
"primary" : false,
|
||||
"tooltip" : null,
|
||||
"label" : "Label",
|
||||
"attributes": []
|
||||
}
|
||||
}
|
||||
}}
|
||||
<div class="container-fluid tertiary-navigation">
|
||||
<div class="row">
|
||||
<div class="d-flex">
|
||||
{{#hasback}}
|
||||
<div class="navitem backbutton border-right">
|
||||
<a href="{{backurl}}" role="button" title="{{backtitle}}" class="d-flex align-items-center">{{!
|
||||
}}{{#pix}} i/previous, moodle {{/pix}}{{!
|
||||
}}</a>
|
||||
</div>
|
||||
{{/hasback}}
|
||||
{{#urlselect}}
|
||||
<div class="navitem">
|
||||
{{>core/url_select}}
|
||||
</div>
|
||||
{{/urlselect}}
|
||||
{{#title}}
|
||||
<div class="navitem title d-flex align-items-center">
|
||||
{{title}}
|
||||
</div>
|
||||
{{/title}}
|
||||
</div>
|
||||
<div class="ml-sm-auto d-flex">
|
||||
{{#extraurlselect}}
|
||||
<div class="navitem">
|
||||
{{>core/url_select}}
|
||||
</div>
|
||||
{{/extraurlselect}}
|
||||
{{#buttons}}
|
||||
<div class="navitem">
|
||||
{{>core/single_button}}
|
||||
</div>
|
||||
{{/buttons}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
43
mod/data/templates/preset_preview.mustache
Normal file
43
mod/data/templates/preset_preview.mustache
Normal file
@ -0,0 +1,43 @@
|
||||
{{!
|
||||
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/>.
|
||||
}}
|
||||
{{!
|
||||
@template mod_data/preset_preview
|
||||
|
||||
The preset preview.
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"formactionurl": "#",
|
||||
"d": "42",
|
||||
"description": "Preset description",
|
||||
"preview": "<p>The preset preview HTML</p>",
|
||||
"userid": "0",
|
||||
"shortname": "imagegallery"
|
||||
}
|
||||
}}
|
||||
<div id="template-preview">
|
||||
<div class="template-description mb-2">
|
||||
{{description}}
|
||||
</div>
|
||||
<div class="template-preview-content">
|
||||
{{{preview}}}
|
||||
</div>
|
||||
</div>
|
||||
<form method="post" action="{{formactionurl}}" class="mt-4">
|
||||
<input type="hidden" name="d" value="{{d}}">
|
||||
<input type="hidden" name="mode" value="usepreset">
|
||||
<input type="hidden" name="action" value="select">
|
||||
<input type="hidden" name="fullname" value="{{userid}}/{{shortname}}">
|
||||
<input type="submit" name="selectpreset" value="{{#str}}usepreset, mod_data{{/str}}" class="btn btn-secondary mt-2 float-right">
|
||||
</form>
|
@ -17,13 +17,13 @@
|
||||
Form containing all database presets displayed within a table.
|
||||
|
||||
Context variables required for this template:
|
||||
* formactionul - The form action url.
|
||||
* formactionurl - The form action url.
|
||||
* d - The database id.
|
||||
* presets - List of presets containing id, name, fullname, shortname and actions.
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"formactionul": "http://www.example.com",
|
||||
"formactionurl": "http://www.example.com",
|
||||
"d": 1,
|
||||
"presets": [
|
||||
{
|
||||
@ -33,6 +33,7 @@
|
||||
"fullname": "Image gallery",
|
||||
"description": "Use this preset to collect images",
|
||||
"userid": 0,
|
||||
"url": "http://www.example.com",
|
||||
"actions": []
|
||||
},
|
||||
{
|
||||
@ -41,6 +42,7 @@
|
||||
"shortname": "Preset saved manually",
|
||||
"fullname": "Preset saved manually (admin)",
|
||||
"userid": 2,
|
||||
"url": "http://www.example.com",
|
||||
"actions": []
|
||||
}
|
||||
]
|
||||
@ -48,7 +50,7 @@
|
||||
}}
|
||||
{{#str}}presetshelp, mod_data{{/str}}
|
||||
|
||||
<form method="post" action="{{formactionul}}" class="mt-4">
|
||||
<form method="post" action="{{formactionurl}}" class="mt-4">
|
||||
<input type="hidden" name="d" value="{{d}}">
|
||||
<input type="hidden" name="mode" value="usepreset">
|
||||
<input type="hidden" name="action" value="select">
|
||||
@ -68,7 +70,9 @@
|
||||
<td class="p-4 border-right">
|
||||
<input type="radio" name="fullname" value="{{userid}}/{{shortname}}" />
|
||||
</td>
|
||||
<td class="p-4">{{fullname}}</td>
|
||||
<td class="p-4">
|
||||
<a href="{{{url}}}">{{fullname}}</a>
|
||||
</td>
|
||||
<td class="p-4">{{description}}</td>
|
||||
<td class="p-4 preset_action_menu">
|
||||
{{#actions}}
|
||||
|
@ -27,7 +27,7 @@
|
||||
<div class="container-fluid tertiary-navigation">
|
||||
<div class="row float-right">
|
||||
<div class="navitem">
|
||||
<a href="{{importpreseturl}}" class="btn btn-secondary">{{#str}}import, core{{/str}}</a>
|
||||
<a role="button" href="{{importpreseturl}}" class="btn btn-secondary">{{#str}}import, core{{/str}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -260,3 +260,30 @@ Feature: Users can view and manage data presets
|
||||
And I click on "Delete" "button" in the "Delete preset Saved preset by teacher1?" "dialogue"
|
||||
And I should see "Preset deleted"
|
||||
And I should not see "Saved preset by teacher1"
|
||||
|
||||
@javascript
|
||||
Scenario: Teachers can preview a saved preset from the notification
|
||||
Given the following "mod_data > fields" exist:
|
||||
| database | type | name | description |
|
||||
| data1 | text | Test field name | Test field description |
|
||||
And the following "mod_data > templates" exist:
|
||||
| database | name |
|
||||
| data1 | singletemplate |
|
||||
| data1 | listtemplate |
|
||||
| data1 | addtemplate |
|
||||
| data1 | asearchtemplate |
|
||||
| data1 | rsstemplate |
|
||||
And I am on the "Mountain landscapes" "data activity" page logged in as teacher1
|
||||
And I follow "Templates"
|
||||
And I click on "Save as preset" "button"
|
||||
And I set the field "Name" to "New saved preset"
|
||||
And I set the field "Description" to "My funny description goes here."
|
||||
And I click on "Save" "button" in the "Save all fields and templates as preset" "dialogue"
|
||||
And I should see "Preset saved"
|
||||
When I click on "Preview preset" "link"
|
||||
Then I should see "Preview"
|
||||
And I should see "New saved preset"
|
||||
And I should see "My funny description goes here"
|
||||
And I should see "Test field name"
|
||||
And I should see "This is a short text"
|
||||
Then "Use preset" "button" should exist
|
||||
|
164
mod/data/tests/behat/preview_preset.feature
Normal file
164
mod/data/tests/behat/preview_preset.feature
Normal file
@ -0,0 +1,164 @@
|
||||
@mod @mod_data
|
||||
Feature: Users can preview presets
|
||||
In order to find the preset I am looking for
|
||||
As a teacher
|
||||
I need to preview the database activity presets
|
||||
|
||||
Background:
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| teacher1 | Teacher | 1 | teacher1@example.com |
|
||||
And the following "tags" exist:
|
||||
| name | isstandard |
|
||||
| Tag1 | 1 |
|
||||
And the following "courses" exist:
|
||||
| fullname | shortname | category |
|
||||
| Course 1 | C1 | 0 |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
And the following "activities" exist:
|
||||
| activity | name | intro | course | idnumber |
|
||||
| data | Test database name | Database intro | C1 | data1 |
|
||||
And I am on the "Test database name" "data activity" page logged in as teacher1
|
||||
|
||||
@javascript @_file_upload
|
||||
Scenario: Preview a user preset as list view template in database
|
||||
Given I follow "Presets"
|
||||
And I click on "Import" "button"
|
||||
And I upload "mod/data/tests/fixtures/behat_preset.zip" file to "Choose file" filemanager
|
||||
And I click on "Save" "button"
|
||||
And I click on "Continue" "button"
|
||||
And I follow "Templates"
|
||||
And I click on "Save as preset" "button"
|
||||
And I set the field "Name" to "Saved preset by teacher1"
|
||||
And I set the field "Description" to "Behat test preset"
|
||||
And I click on "Save" "button" in the "Save all fields and templates as preset" "dialogue"
|
||||
When I follow "Presets"
|
||||
And I click on "Saved preset by teacher1" "link"
|
||||
# Check list template preview fields.
|
||||
Then I should see "Behat test preset" in the "template-preview" "region"
|
||||
And I should see "List header" in the "template-preview" "region"
|
||||
And I should see "List footer" in the "template-preview" "region"
|
||||
And I should see "List template content" in the "template-preview" "region"
|
||||
And I should see "My text field" in the "template-preview" "region"
|
||||
And I should see "This is a short text" in the "template-preview" "region"
|
||||
And I should see "My multiple selection" in the "template-preview" "region"
|
||||
And I should see "Multi 1" in the "template-preview" "region"
|
||||
And I should see "My date field" in the "template-preview" "region"
|
||||
And I should see "My checkbox field" in the "template-preview" "region"
|
||||
And I should see "Check 2" in the "template-preview" "region"
|
||||
And I should see "My geo field" in the "template-preview" "region"
|
||||
And I should see "41.3912°N 2.1639°E" in the "template-preview" "region"
|
||||
And I should see "My menu field" in the "template-preview" "region"
|
||||
And I should see "Menu 2" in the "template-preview" "region"
|
||||
And I should see "My number field" in the "template-preview" "region"
|
||||
And I should see "1234" in the "template-preview" "region"
|
||||
And I should see "My radio field" in the "template-preview" "region"
|
||||
And I should see "Radio 2" in the "template-preview" "region"
|
||||
And I should see "My text area field" in the "template-preview" "region"
|
||||
And I should see "This is a text area" in the "template-preview" "region"
|
||||
And I should see "My URL field" in the "template-preview" "region"
|
||||
And I should see "https://example.com" in the "template-preview" "region"
|
||||
And I should see "My file field" in the "template-preview" "region"
|
||||
And "Comma-separated values" "icon" should exist in the "template-preview" "region"
|
||||
And I should see "samplefile.csv" in the "template-preview" "region"
|
||||
And I should see "My picture field" in the "template-preview" "region"
|
||||
# Test CSS and JS templates.
|
||||
And I should not see "This content should not be displayed" in the "template-preview" "region"
|
||||
And I should not see "This text should change" in the "template-preview" "region"
|
||||
And I should see "New value" in the "template-preview" "region"
|
||||
|
||||
@javascript @_file_upload
|
||||
Scenario: Preview a user preset as single view template in database
|
||||
Given I follow "Presets"
|
||||
And I click on "Import" "button"
|
||||
And I upload "mod/data/tests/fixtures/behat_preset.zip" file to "Choose file" filemanager
|
||||
And I click on "Save" "button"
|
||||
And I click on "Continue" "button"
|
||||
And I follow "Templates"
|
||||
And I click on "Save as preset" "button"
|
||||
And I set the field "Name" to "Saved preset by teacher1"
|
||||
And I set the field "Description" to "Behat test preset"
|
||||
And I click on "Save" "button" in the "Save all fields and templates as preset" "dialogue"
|
||||
When I follow "Presets"
|
||||
And I click on "Saved preset by teacher1" "link"
|
||||
And I set the field "Templates tertiary navigation" to "Single template"
|
||||
# Check single template preview fields.
|
||||
Then I should see "Behat test preset" in the "template-preview" "region"
|
||||
And I should see "Single template content" in the "template-preview" "region"
|
||||
And I should see "My text field" in the "template-preview" "region"
|
||||
And I should see "This is a short text" in the "template-preview" "region"
|
||||
And I should see "My multiple selection" in the "template-preview" "region"
|
||||
And I should see "Multi 2" in the "template-preview" "region"
|
||||
And I should see "My date field" in the "template-preview" "region"
|
||||
And I should see "My checkbox field" in the "template-preview" "region"
|
||||
And I should see "Check 2" in the "template-preview" "region"
|
||||
And I should see "My geo field" in the "template-preview" "region"
|
||||
And I should see "41.3912°N 2.1639°E" in the "template-preview" "region"
|
||||
And I should see "My menu field" in the "template-preview" "region"
|
||||
And I should see "Menu 2" in the "template-preview" "region"
|
||||
And I should see "My number field" in the "template-preview" "region"
|
||||
And I should see "1234" in the "template-preview" "region"
|
||||
And I should see "My radio field" in the "template-preview" "region"
|
||||
And I should see "Radio 2" in the "template-preview" "region"
|
||||
And I should see "My text area field" in the "template-preview" "region"
|
||||
And I should see "This is a text area" in the "template-preview" "region"
|
||||
And I should see "My URL field" in the "template-preview" "region"
|
||||
And I should see "https://example.com" in the "template-preview" "region"
|
||||
And I should see "My file field" in the "template-preview" "region"
|
||||
And "Comma-separated values" "icon" should exist in the "template-preview" "region"
|
||||
And I should see "samplefile.csv" in the "template-preview" "region"
|
||||
And I should see "My picture field" in the "template-preview" "region"
|
||||
# Test CSS and JS templates.
|
||||
And I should not see "This content should not be displayed" in the "template-preview" "region"
|
||||
And I should not see "This text should change" in the "template-preview" "region"
|
||||
And I should see "New value" in the "template-preview" "region"
|
||||
|
||||
@javascript
|
||||
Scenario: Preview a plugin preset in database
|
||||
Given I follow "Presets"
|
||||
When I click on "Journal" "link"
|
||||
Then I should see "Use this preset for a journal, diary, reflections tool or research log." in the "template-preview" "region"
|
||||
And I should see "This is a short text"
|
||||
And I should see "This is a text area"
|
||||
And I select "Single template" from the "Templates tertiary navigation" singleselect
|
||||
And I should see "Use this preset for a journal, diary, reflections tool or research log." in the "template-preview" "region"
|
||||
And I should see "This is a short text"
|
||||
And I should see "This is a text area"
|
||||
And I should see "This is a short text" in the "template-preview" "region"
|
||||
|
||||
@javascript
|
||||
Scenario: Use back button to return to the presets page in database
|
||||
Given I follow "Presets"
|
||||
And I click on "Image gallery" "link"
|
||||
And I should see "Use this preset to collect images." in the "template-preview" "region"
|
||||
When I click on "Back" "button"
|
||||
Then I should see "Choose a preset to use as a starting point."
|
||||
|
||||
@javascript
|
||||
Scenario: Apply plugin preset from preview in database
|
||||
Given I follow "Presets"
|
||||
And I click on "Image gallery" "link"
|
||||
When I click on "Use preset" "button"
|
||||
Then I should see "Field mappings"
|
||||
And I should see "image"
|
||||
And I should see "title"
|
||||
|
||||
@javascript @_file_upload
|
||||
Scenario: Apply user preset from preview in database
|
||||
Given I follow "Presets"
|
||||
And I click on "Import" "button"
|
||||
And I upload "mod/data/tests/fixtures/behat_preset.zip" file to "Choose file" filemanager
|
||||
And I click on "Save" "button"
|
||||
And I click on "Continue" "button"
|
||||
And I follow "Templates"
|
||||
And I click on "Save as preset" "button"
|
||||
And I set the field "Name" to "Saved preset by teacher1"
|
||||
And I set the field "Description" to "Behat test preset"
|
||||
And I click on "Save" "button" in the "Save all fields and templates as preset" "dialogue"
|
||||
When I follow "Presets"
|
||||
And I click on "Saved preset by teacher1" "link"
|
||||
And I click on "Use preset" "button"
|
||||
Then I should see "Field mappings"
|
||||
And I should see "My URL field"
|
BIN
mod/data/tests/fixtures/behat_preset.zip
vendored
Normal file
BIN
mod/data/tests/fixtures/behat_preset.zip
vendored
Normal file
Binary file not shown.
@ -399,4 +399,149 @@ class manager_test extends \advanced_testcase {
|
||||
$preset = reset($presets);
|
||||
$this->assertEquals($teacherpreset->name, $preset->name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for can_view_preset().
|
||||
*
|
||||
* @covers ::can_view_preset
|
||||
* @dataProvider can_view_preset_provider
|
||||
* @param string $rolename the user role name
|
||||
* @param bool $ownpreset if the preset belongs to the user
|
||||
* @param bool|null $useridparam if the method should be called with a user id param
|
||||
* @param bool $plugin if the preset is a plugin or not
|
||||
* @param bool $expected the expected result
|
||||
*/
|
||||
public function test_can_view_preset(string $rolename, bool $ownpreset, ?bool $useridparam, bool $plugin, bool $expected) {
|
||||
|
||||
$this->resetAfterTest();
|
||||
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$user = $this->getDataGenerator()->create_and_enrol($course, $rolename);
|
||||
$activity = $this->getDataGenerator()->create_module(manager::MODULE, ['course' => $course]);
|
||||
$cm = get_coursemodule_from_id(manager::MODULE, $activity->cmid, 0, false, MUST_EXIST);
|
||||
$manager = manager::create_from_coursemodule($cm);
|
||||
|
||||
// Create preset.
|
||||
if ($ownpreset) {
|
||||
$this->setUser($user);
|
||||
} else {
|
||||
$this->setAdminUser();
|
||||
}
|
||||
|
||||
if ($plugin) {
|
||||
$preset = preset::create_from_plugin($manager, 'imagegallery');
|
||||
} else {
|
||||
$plugingenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
|
||||
$preset = $plugingenerator->create_preset($activity, (object)['name' => 'Preset name']);
|
||||
}
|
||||
|
||||
// Setup user param.
|
||||
if ($useridparam) {
|
||||
// Login as a different user to validate the userid param is working.
|
||||
$otheruser = $this->getDataGenerator()->create_user();
|
||||
$this->setUser($otheruser);
|
||||
$useridparam = $user->id;
|
||||
} else {
|
||||
$this->setUser($user);
|
||||
}
|
||||
|
||||
$result = $manager->can_view_preset($preset, $useridparam);
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for test_can_view_preset.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function can_view_preset_provider(): array {
|
||||
return [
|
||||
// User presets.
|
||||
'Teacher owned preset without user id param' => [
|
||||
'rolename' => 'editingteacher',
|
||||
'ownpreset' => true,
|
||||
'useridparam' => null,
|
||||
'plugin' => false,
|
||||
'expected' => true,
|
||||
],
|
||||
'Teacher owned preset with user id param' => [
|
||||
'rolename' => 'editingteacher',
|
||||
'ownpreset' => true,
|
||||
'useridparam' => true,
|
||||
'plugin' => false,
|
||||
'expected' => true,
|
||||
],
|
||||
'Teacher not owned preset without user id param' => [
|
||||
'rolename' => 'editingteacher',
|
||||
'ownpreset' => false,
|
||||
'useridparam' => null,
|
||||
'plugin' => false,
|
||||
'expected' => true,
|
||||
],
|
||||
'Teacher not owned preset with user id param' => [
|
||||
'rolename' => 'editingteacher',
|
||||
'ownpreset' => false,
|
||||
'useridparam' => true,
|
||||
'plugin' => false,
|
||||
'expected' => true,
|
||||
],
|
||||
'Student owned preset without user id param' => [
|
||||
'rolename' => 'student',
|
||||
'ownpreset' => true,
|
||||
'useridparam' => null,
|
||||
'plugin' => false,
|
||||
'expected' => true,
|
||||
],
|
||||
'Student owned preset with user id param' => [
|
||||
'rolename' => 'student',
|
||||
'ownpreset' => true,
|
||||
'useridparam' => true,
|
||||
'plugin' => false,
|
||||
'expected' => true,
|
||||
],
|
||||
'Student not owned preset without user id param' => [
|
||||
'rolename' => 'student',
|
||||
'ownpreset' => false,
|
||||
'useridparam' => null,
|
||||
'plugin' => false,
|
||||
'expected' => false,
|
||||
],
|
||||
'Student not owned preset with user id param' => [
|
||||
'rolename' => 'student',
|
||||
'ownpreset' => false,
|
||||
'useridparam' => true,
|
||||
'plugin' => false,
|
||||
'expected' => false,
|
||||
],
|
||||
// Plugin presets.
|
||||
'Teacher plugin preset without user id param' => [
|
||||
'rolename' => 'editingteacher',
|
||||
'ownpreset' => false,
|
||||
'useridparam' => null,
|
||||
'plugin' => true,
|
||||
'expected' => true,
|
||||
],
|
||||
'Teacher plugin preset with user id param' => [
|
||||
'rolename' => 'editingteacher',
|
||||
'ownpreset' => false,
|
||||
'useridparam' => true,
|
||||
'plugin' => true,
|
||||
'expected' => true,
|
||||
],
|
||||
'Student plugin preset without user id param' => [
|
||||
'rolename' => 'student',
|
||||
'ownpreset' => false,
|
||||
'useridparam' => null,
|
||||
'plugin' => true,
|
||||
'expected' => true,
|
||||
],
|
||||
'Student plugin preset with user id param' => [
|
||||
'rolename' => 'student',
|
||||
'ownpreset' => false,
|
||||
'useridparam' => true,
|
||||
'plugin' => true,
|
||||
'expected' => true,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -156,6 +156,53 @@ class preset_test extends \advanced_testcase {
|
||||
$this->assertEquals('/' . $presetname . '/', $result->get_path());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for static create_from_fullname method.
|
||||
*
|
||||
* @covers ::create_from_fullname
|
||||
*/
|
||||
public function test_create_from_fullname() {
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
|
||||
// Create a course and a database activity.
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$activity = $this->getDataGenerator()->create_module(manager::MODULE, ['course' => $course]);
|
||||
$manager = manager::create_from_instance($activity);
|
||||
|
||||
// Create a saved preset.
|
||||
$plugingenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
|
||||
$record = (object) [
|
||||
'name' => 'Testing preset name',
|
||||
'description' => 'Testing preset description',
|
||||
];
|
||||
$savedpreset = $plugingenerator->create_preset($activity, $record);
|
||||
|
||||
// Check instantiate from plugin.
|
||||
$pluginname = 'imagegallery';
|
||||
$fullname = '0/imagegallery';
|
||||
$result = preset::create_from_fullname($manager, $fullname);
|
||||
$this->assertTrue($result->isplugin);
|
||||
$this->assertEquals(get_string('modulename', "datapreset_$pluginname"), $result->name);
|
||||
$this->assertEquals($pluginname, $result->shortname);
|
||||
$this->assertEquals(get_string('modulename_help', "datapreset_$pluginname"), $result->description);
|
||||
$this->assertEmpty($result->get_userid());
|
||||
$this->assertEmpty($result->storedfile);
|
||||
$this->assertNull($result->get_path());
|
||||
|
||||
// Check instantiate from user preset
|
||||
// Check create_from_instance is working as expected when a preset with this name exists.
|
||||
$fullname = $savedpreset->get_userid() . '/' . $savedpreset->name;
|
||||
$result = preset::create_from_fullname($manager, $fullname);
|
||||
$this->assertFalse($result->isplugin);
|
||||
$this->assertEquals($savedpreset->name, $result->name);
|
||||
$this->assertEquals($savedpreset->shortname, $result->shortname);
|
||||
$this->assertEquals($savedpreset->description, $savedpreset->description);
|
||||
$this->assertEquals($savedpreset->storedfile->get_userid(), $result->get_userid());
|
||||
$this->assertNotEmpty($result->storedfile);
|
||||
$this->assertEquals('/' . $savedpreset->name . '/', $result->get_path());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for the save a preset method when the preset hasn't been saved before.
|
||||
*
|
||||
@ -726,4 +773,188 @@ class preset_test extends \advanced_testcase {
|
||||
$currentpresets = $manager->get_available_presets();
|
||||
$this->assertEquals(count($initialpresets) - 1, count($currentpresets));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for the get_fields method.
|
||||
*
|
||||
* @covers ::get_fields
|
||||
*/
|
||||
public function test_get_fields() {
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
|
||||
// Create a course and a database activity.
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$activity = $this->getDataGenerator()->create_module(manager::MODULE, ['course' => $course]);
|
||||
$manager = manager::create_from_instance($activity);
|
||||
|
||||
// Add a field to the activity.
|
||||
$fieldrecord = new stdClass();
|
||||
$fieldrecord->name = 'field-1';
|
||||
$fieldrecord->type = 'text';
|
||||
$datagenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
|
||||
$datagenerator->create_field($fieldrecord, $activity);
|
||||
|
||||
// Create a saved preset.
|
||||
$plugingenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
|
||||
$record = (object) [
|
||||
'name' => 'Testing preset name',
|
||||
'description' => 'Testing preset description',
|
||||
];
|
||||
$preset = $plugingenerator->create_preset($activity, $record);
|
||||
|
||||
// Check regular fields.
|
||||
$fields = $preset->get_fields();
|
||||
$this->assertCount(1, $fields);
|
||||
$this->assertArrayHasKey('field-1', $fields);
|
||||
$field = $fields['field-1'];
|
||||
$this->assertEquals('text', $field->type);
|
||||
$this->assertEquals('field-1', $field->get_name());
|
||||
$this->assertEquals(false, $field->get_preview());
|
||||
|
||||
// Check preview fields.
|
||||
$savedpresets = $manager->get_available_saved_presets();
|
||||
$preset = reset($savedpresets);
|
||||
$fields = $preset->get_fields(true);
|
||||
$this->assertCount(1, $fields);
|
||||
$this->assertArrayHasKey('field-1', $fields);
|
||||
$field = $fields['field-1'];
|
||||
$this->assertEquals('text', $field->type);
|
||||
$this->assertEquals('field-1', $field->get_name());
|
||||
$this->assertEquals(true, $field->get_preview());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for the get_sample_entries method.
|
||||
*
|
||||
* @covers ::get_sample_entries
|
||||
*/
|
||||
public function test_get_sample_entries() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$this->setUser($user);
|
||||
|
||||
// Create a course and a database activity.
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$activity = $this->getDataGenerator()->create_module(manager::MODULE, ['course' => $course]);
|
||||
$manager = manager::create_from_instance($activity);
|
||||
|
||||
// Add a field to the activity.
|
||||
$fieldrecord = new stdClass();
|
||||
$fieldrecord->name = 'field-1';
|
||||
$fieldrecord->type = 'text';
|
||||
$datagenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
|
||||
$datagenerator->create_field($fieldrecord, $activity);
|
||||
|
||||
// Create a saved preset.
|
||||
$plugingenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
|
||||
$record = (object) [
|
||||
'name' => 'Testing preset name',
|
||||
'description' => 'Testing preset description',
|
||||
];
|
||||
$preset = $plugingenerator->create_preset($activity, $record);
|
||||
|
||||
$entries = $preset->get_sample_entries(3);
|
||||
$this->assertCount(3, $entries);
|
||||
foreach ($entries as $entry) {
|
||||
$this->assertEquals($user->id, $entry->userid);
|
||||
$this->assertEquals($user->email, $entry->email);
|
||||
$this->assertEquals($user->firstname, $entry->firstname);
|
||||
$this->assertEquals($user->lastname, $entry->lastname);
|
||||
$this->assertEquals($activity->id, $entry->dataid);
|
||||
$this->assertEquals(0, $entry->groupid);
|
||||
$this->assertEquals(1, $entry->approved);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for the get_template_content method.
|
||||
*
|
||||
* @covers ::get_template_content
|
||||
*/
|
||||
public function test_get_template_content() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$this->setUser($user);
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
|
||||
// Module data with templates.
|
||||
$templates = [
|
||||
'singletemplate' => 'Single template content',
|
||||
'listtemplate' => 'List template content',
|
||||
'listtemplateheader' => 'List template content header',
|
||||
'listtemplatefooter' => 'List template content footer',
|
||||
'addtemplate' => 'Add template content',
|
||||
'rsstemplate' => 'RSS template content',
|
||||
'rsstitletemplate' => 'RSS title template content',
|
||||
'csstemplate' => 'CSS template content',
|
||||
'jstemplate' => 'JS template content',
|
||||
'asearchtemplate' => 'Advanced search template content',
|
||||
];
|
||||
$params = array_merge(['course' => $course], $templates);
|
||||
|
||||
// Create a database activity.
|
||||
$activity = $this->getDataGenerator()->create_module(manager::MODULE, $params);
|
||||
$manager = manager::create_from_instance($activity);
|
||||
|
||||
// Create a saved preset.
|
||||
$plugingenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
|
||||
$record = (object) [
|
||||
'name' => 'Testing preset name',
|
||||
'description' => 'Testing preset description',
|
||||
];
|
||||
$preset = $plugingenerator->create_preset($activity, $record);
|
||||
|
||||
// Test user preset templates.
|
||||
foreach ($templates as $templatename => $templatecontent) {
|
||||
$content = $preset->get_template_content($templatename);
|
||||
$this->assertEquals($templatecontent, $content);
|
||||
}
|
||||
|
||||
// Test plugin preset content.
|
||||
$pluginname = 'imagegallery';
|
||||
$preset = preset::create_from_plugin($manager, $pluginname);
|
||||
foreach (manager::TEMPLATES_LIST as $templatename => $templatefile) {
|
||||
// Get real file contents.
|
||||
$path = $manager->path . '/preset/' . $pluginname . '/' . $templatefile;
|
||||
$templatecontent = file_get_contents($path);
|
||||
$content = $preset->get_template_content($templatename);
|
||||
$this->assertEquals($templatecontent, $content);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for the get_fullname method.
|
||||
*
|
||||
* @covers ::get_fullname
|
||||
*/
|
||||
public function test_get_fullname() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$this->setUser($user);
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
|
||||
// Create a database activity.
|
||||
$activity = $this->getDataGenerator()->create_module(manager::MODULE, ['course' => $course]);
|
||||
$manager = manager::create_from_instance($activity);
|
||||
|
||||
// Create a saved preset.
|
||||
$plugingenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
|
||||
$record = (object) [
|
||||
'name' => 'Testing preset name',
|
||||
'description' => 'Testing preset description',
|
||||
];
|
||||
$preset = $plugingenerator->create_preset($activity, $record);
|
||||
|
||||
// Test user preset templates.
|
||||
$this->assertEquals("{$user->id}/Testing preset name", $preset->get_fullname());
|
||||
|
||||
// Test plugin preset content.
|
||||
$pluginname = 'imagegallery';
|
||||
$preset = preset::create_from_plugin($manager, $pluginname);
|
||||
$this->assertEquals("0/imagegallery", $preset->get_fullname());
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user