MDL-75234 mod_data: load default templates when empty

The mod_data is forcing teachers to understand how to write templates
even if they want to use basic forms. With this patch the default
templates will be auto updated unless the user manually define the
templates.
This commit is contained in:
Ferran Recio 2022-08-29 19:23:30 +02:00
parent 24f97edd91
commit 2a0991113b
19 changed files with 463 additions and 294 deletions

View File

@ -1,4 +1,4 @@
define("mod_data/templateseditor",["exports","core/str","core/notification","core/url"],(function(_exports,_str,_notification,_url){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0;
define("mod_data/templateseditor",["exports","core/str","core/prefetch","core/url","core/notification"],(function(_exports,_str,_prefetch,_url,_notification){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,
/**
* Javascript module to control the template editor.
*
@ -6,6 +6,6 @@ define("mod_data/templateseditor",["exports","core/str","core/notification","cor
* @copyright 2021 Mihail Geshoski <mihail@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
const selectors_toggleTemplateEditor='input[name="useeditor"]';_exports.init=(d,mode)=>{((d,mode)=>{const toggleTemplateEditor=document.querySelector(selectors_toggleTemplateEditor);toggleTemplateEditor&&toggleTemplateEditor.addEventListener("click",(async event=>{event.preventDefault(),event.target.checked?(0,_notification.confirm)((0,_str.get_string)("confirmation","admin"),(0,_str.get_string)("enabletemplateeditorcheck","mod_data"),(0,_str.get_string)("yes","core"),(0,_str.get_string)("no","core"),(()=>{window.location=(0,_url.relativeUrl)("/mod/data/templates.php",{d:d,mode:mode,useeditor:!0})})):window.location=(0,_url.relativeUrl)("/mod/data/templates.php",{d:d,mode:mode,useeditor:!1})}))})(d,mode)}}));
(0,_prefetch.prefetchStrings)("admin",["confirmation"]),(0,_prefetch.prefetchStrings)("mod_data",["resettemplateconfirmtitle","resettemplateconfirm","resettemplate","enabletemplateeditorcheck","editorenable"]);const selectors_toggleTemplateEditor='input[name="useeditor"]',selectors_resetTemplate='input[name="defaultform"]',selectors_resetButton='input[name="resetbutton"]',selectors_editForm="#edittemplateform",registerResetButton=()=>{const editForm=document.querySelector(selectors_editForm),resetButton=document.querySelector(selectors_resetButton),resetTemplate=document.querySelector(selectors_resetTemplate);resetButton&&resetTemplate&&editForm&&resetButton.addEventListener("click",(async event=>{event.preventDefault(),(0,_notification.saveCancel)((0,_str.get_string)("resettemplateconfirmtitle","mod_data"),(0,_str.get_string)("resettemplateconfirm","mod_data"),(0,_str.get_string)("resettemplate","mod_data"),(()=>{resetTemplate.value="true",editForm.submit()}),null,{triggerElement:event.target})}))},registerEditorToggler=(instanceId,mode)=>{const toggleTemplateEditor=document.querySelector(selectors_toggleTemplateEditor);toggleTemplateEditor&&toggleTemplateEditor.addEventListener("click",(async event=>{event.preventDefault();event.target.checked?(0,_notification.saveCancel)((0,_str.get_string)("confirmation","admin"),(0,_str.get_string)("enabletemplateeditorcheck","mod_data"),(0,_str.get_string)("editorenable","mod_data"),(()=>{window.location=(0,_url.relativeUrl)("/mod/data/templates.php",{d:instanceId,mode:mode,useeditor:!0})}),null,{triggerElement:event.target}):window.location=(0,_url.relativeUrl)("/mod/data/templates.php",{d:instanceId,mode:mode,useeditor:!1})}))};_exports.init=(instanceId,mode)=>{((instanceId,mode)=>{registerResetButton(),registerEditorToggler(instanceId,mode)})(instanceId,mode)}}));
//# sourceMappingURL=templateseditor.min.js.map

File diff suppressed because one or more lines are too long

View File

@ -22,23 +22,66 @@
*/
import {get_string as getString} from 'core/str';
import {confirm as confirmDialogue} from 'core/notification';
import {prefetchStrings} from 'core/prefetch';
import {relativeUrl} from 'core/url';
import {saveCancel} from 'core/notification';
prefetchStrings('admin', ['confirmation']);
prefetchStrings('mod_data', [
'resettemplateconfirmtitle',
'resettemplateconfirm',
'resettemplate',
'enabletemplateeditorcheck',
'editorenable'
]);
/**
* Template editor constants.
*/
const selectors = {
toggleTemplateEditor: 'input[name="useeditor"]',
resetTemplate: 'input[name="defaultform"]',
resetButton: 'input[name="resetbutton"]',
editForm: '#edittemplateform',
};
/**
* Register event listeners for the module.
*
* @param {int} d The database ID
* @param {int} instanceId The database ID
* @param {string} mode The template mode
*/
const registerEventListeners = (d, mode) => {
const registerEventListeners = (instanceId, mode) => {
registerResetButton();
registerEditorToggler(instanceId, mode);
};
const registerResetButton = () => {
const editForm = document.querySelector(selectors.editForm);
const resetButton = document.querySelector(selectors.resetButton);
const resetTemplate = document.querySelector(selectors.resetTemplate);
if (!resetButton || !resetTemplate || !editForm) {
return;
}
resetButton.addEventListener('click', async(event) => {
event.preventDefault();
saveCancel(
getString('resettemplateconfirmtitle', 'mod_data'),
getString('resettemplateconfirm', 'mod_data'),
getString('resettemplate', 'mod_data'),
() => {
resetTemplate.value = "true";
editForm.submit();
},
null,
{triggerElement: event.target}
);
});
};
const registerEditorToggler = (instanceId, mode) => {
const toggleTemplateEditor = document.querySelector(selectors.toggleTemplateEditor);
if (!toggleTemplateEditor) {
@ -52,17 +95,18 @@ const registerEventListeners = (d, mode) => {
if (enableTemplateEditor) {
// Display a confirmation dialog before enabling the template editor.
confirmDialogue(
saveCancel(
getString('confirmation', 'admin'),
getString('enabletemplateeditorcheck', 'mod_data'),
getString('yes', 'core'),
getString('no', 'core'),
getString('editorenable', 'mod_data'),
() => {
window.location = relativeUrl('/mod/data/templates.php', {d: d, mode: mode, useeditor: true});
}
window.location = relativeUrl('/mod/data/templates.php', {d: instanceId, mode: mode, useeditor: true});
},
null,
{triggerElement: event.target}
);
} else {
window.location = relativeUrl('/mod/data/templates.php', {d: d, mode: mode, useeditor: false});
window.location = relativeUrl('/mod/data/templates.php', {d: instanceId, mode: mode, useeditor: false});
}
});
};
@ -70,9 +114,9 @@ const registerEventListeners = (d, mode) => {
/**
* Initialize the module.
*
* @param {int} d The database ID
* @param {int} instanceId The database ID
* @param {string} mode The template mode
*/
export const init = (d, mode) => {
registerEventListeners(d, mode);
export const init = (instanceId, mode) => {
registerEventListeners(instanceId, mode);
};

View File

@ -20,9 +20,11 @@ use cm_info;
use context_module;
use completion_info;
use data_field_base;
use mod_data_renderer;
use mod_data\event\course_module_viewed;
use mod_data\event\template_viewed;
use mod_data\event\template_updated;
use moodle_page;
use core_component;
use stdClass;
@ -155,6 +157,18 @@ class manager {
return $this->cm;
}
/**
* Return the current module renderer.
*
* @param moodle_page|null $page the current page
* @return mod_data_renderer the module renderer
*/
public function get_renderer(?moodle_page $page = null): mod_data_renderer {
global $PAGE;
$page = $page ?? $PAGE;
return $page->get_renderer(self::PLUGINNAME);
}
/**
* Trigger module viewed event and set the module viewed for completion.
*
@ -262,6 +276,11 @@ class manager {
* NOTE: this method returns a default template if the module template is empty.
* However, it won't update the template database field.
*
* Some possible options:
* - search: string with the current searching text.
* - page: integer repesenting the current pagination page numbre (if any)
* - baseurl: a moodle_url object to the current page.
*
* @param string $templatename
* @param array $options extra display options array
* @return template the template instance

View File

@ -93,7 +93,8 @@ class template_editor implements templatable, renderable {
global $PAGE;
$result = [];
$instance = $this->manager->get_instance();
$manager = $this->manager;
$instance = $manager->get_instance();
// Setup editor.
editors_head_setup();
@ -112,40 +113,43 @@ class template_editor implements templatable, renderable {
// Add editors.
if ($this->templatename === 'listtemplate') {
$template = $manager->get_template('listtemplateheader');
$result[] = $this->generate_editor_data(
$editor,
'header',
'listtemplateheader',
$instance->listtemplateheader
$template->get_template_content()
);
$maineditorname = 'multientry';
} else {
$maineditorname = $this->templatename;
}
$value = $instance->{$this->templatename} ?? '';
$template = $manager->get_template($this->templatename);
$result[] = $this->generate_editor_data(
$editor,
$maineditorname,
$this->templatename,
$value
$template->get_template_content()
);
if ($this->templatename === 'listtemplate') {
$template = $manager->get_template('listtemplatefooter');
$result[] = $this->generate_editor_data(
$editor,
'footer',
'listtemplatefooter',
$instance->listtemplatefooter
$template->get_template_content()
);
}
if ($this->templatename === 'rsstemplate') {
$template = $manager->get_template('rsstitletemplate');
$result[] = $this->generate_editor_data(
$editor,
'rsstitletemplate',
'rsstitletemplate',
$instance->rsstitletemplate
$template->get_template_content()
);
}

View File

@ -24,6 +24,8 @@
namespace mod_data\search;
use mod_data\manager;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot . '/mod/data/lib.php');
@ -280,11 +282,13 @@ class entry extends \core_search\base_mod {
WHERE dc.fieldid = df.id
AND dc.recordid = :recordid";
$contents = $DB->get_records_sql($sql, array('recordid' => $entry->id));
$filteredcontents = array();
$contents = $DB->get_records_sql($sql, ['recordid' => $entry->id]);
$filteredcontents = [];
$template = $DB->get_record_sql('SELECT addtemplate FROM {data} WHERE id = ?', array($entry->dataid));
$template = $template->addtemplate;
$data = $DB->get_record('data', ['id' => $entry->dataid]);
$manager = manager::create_from_instance($data);
$template = $manager->get_template('addtemplate');
$template = $template->get_template_content();
// Filtering out the data_content records having invalid fieldtypes.
foreach ($contents as $content) {

View File

@ -168,6 +168,15 @@ class template {
];
}
/**
* Return the raw template content.
*
* @return string the template content before parsing
*/
public function get_template_content(): string {
return $this->templatecontent;
}
/**
* Return the parsed entry using a template.
*
@ -736,4 +745,71 @@ class template {
return $OUTPUT->render($actionmenu);
}
/**
* Parse the template as if it was for add entry.
*
* This method is similar to the parse_entry but it uses the display_add_field method
* instead of the display_browse_field.
*
* @param stdClass|null $processeddata the previous process data information.
* @param int|null $entryid the possible entry id
* @param stdClass|null $entrydata the entry data from a previous form or from a real entry
* @return string the add entry HTML content
*/
public function parse_add_entry(
?stdClass $processeddata = null,
?int $entryid = null,
?stdClass $entrydata = null
): string {
$manager = $this->manager;
$renderer = $manager->get_renderer();
$templatecontent = $this->templatecontent;
if (!$processeddata) {
$processeddata = (object)[
'generalnotifications' => [],
'fieldnotifications' => [],
];
}
$result = '';
foreach ($processeddata->generalnotifications as $notification) {
$result .= $renderer->notification($notification);
}
$possiblefields = $manager->get_fields();
$patterns = [];
$replacements = [];
// Then we generate strings to replace.
foreach ($possiblefields as $field) {
// To skip unnecessary calls to display_add_field().
if (strpos($templatecontent, "[[" . $field->field->name . "]]") !== false) {
// Replace the field tag.
$patterns[] = "[[" . $field->field->name . "]]";
$errors = '';
$fieldnotifications = $processeddata->fieldnotifications[$field->field->name] ?? [];
if (!empty($fieldnotifications)) {
foreach ($fieldnotifications as $notification) {
$errors .= $renderer->notification($notification);
}
}
$replacements[] = $errors . $field->display_add_field($entryid, $entrydata);
}
// Replace the field id tag.
$patterns[] = "[[" . $field->field->name . "#id]]";
$replacements[] = 'field_' . $field->field->id;
}
if (core_tag_tag::is_enabled('mod_data', 'data_records')) {
$patterns[] = "##tags##";
$replacements[] = data_generate_tag_form($entryid);
}
$result .= str_ireplace($patterns, $replacements, $templatecontent);
return $result;
}
}

View File

@ -23,103 +23,85 @@
* @package mod_data
*/
use mod_data\manager;
require_once('../../config.php');
require_once('locallib.php');
require_once("$CFG->libdir/rsslib.php");
require_once("$CFG->libdir/form/filemanager.php");
$id = optional_param('id', 0, PARAM_INT); // course module id
$d = optional_param('d', 0, PARAM_INT); // database id
$rid = optional_param('rid', 0, PARAM_INT); //record id
$mode ='addtemplate'; //define the mode for this page, only 1 mode available
$id = optional_param('id', 0, PARAM_INT); // Course module id.
$d = optional_param('d', 0, PARAM_INT); // Database id.
$rid = optional_param('rid', 0, PARAM_INT); // Record id.
$mode = 'addtemplate'; // Define the mode for this page, only 1 mode available.
$tags = optional_param_array('tags', [], PARAM_TAGLIST);
$redirectbackto = optional_param('backto', '', PARAM_LOCALURL); // The location to redirect back.
$url = new moodle_url('/mod/data/edit.php');
$record = null;
if ($id) {
list($course, $cm) = get_course_and_cm_from_cmid($id, manager::MODULE);
$manager = manager::create_from_coursemodule($cm);
} else { // We must have $d.
$data = $DB->get_record('data', ['id' => $d], '*', MUST_EXIST);
$manager = manager::create_from_instance($data);
$cm = $manager->get_coursemodule();
$course = get_course($cm->course);
}
$data = $manager->get_instance();
$context = $manager->get_context();
$url->param('id', $cm->id);
if ($rid !== 0) {
$record = $DB->get_record('data_records', array(
'id' => $rid,
'dataid' => $d,
), '*', MUST_EXIST);
$record = $DB->get_record(
'data_records',
['id' => $rid, 'dataid' => $data->id],
'*',
MUST_EXIST
);
$url->param('rid', $rid);
}
if ($id) {
$url->param('id', $id);
$PAGE->set_url($url);
if (! $cm = get_coursemodule_from_id('data', $id)) {
throw new \moodle_exception('invalidcoursemodule');
}
if (! $course = $DB->get_record('course', array('id'=>$cm->course))) {
throw new \moodle_exception('coursemisconf');
}
if (! $data = $DB->get_record('data', array('id'=>$cm->instance))) {
throw new \moodle_exception('invalidcoursemodule');
}
} else {
$url->param('d', $d);
$PAGE->set_url($url);
if (! $data = $DB->get_record('data', array('id'=>$d))) {
throw new \moodle_exception('invalidid', 'data');
}
if (! $course = $DB->get_record('course', array('id'=>$data->course))) {
throw new \moodle_exception('coursemisconf');
}
if (! $cm = get_coursemodule_from_instance('data', $data->id, $course->id)) {
throw new \moodle_exception('invalidcoursemodule');
}
}
$PAGE->set_url($url);
require_login($course, false, $cm);
$url->param('backto', $redirectbackto);
require_login($course, false, $cm);
if (isguestuser()) {
redirect('view.php?d='.$data->id);
}
$context = context_module::instance($cm->id);
/// If it's hidden then it doesn't show anything. :)
if (empty($cm->visible) and !has_capability('moodle/course:viewhiddenactivities', $context)) {
$strdatabases = get_string("modulenameplural", "data");
$PAGE->set_title($data->name);
$PAGE->set_heading($course->fullname);
echo $OUTPUT->header();
notice(get_string("activityiscurrentlyhidden"));
}
/// Can't use this if there are no fields
if (has_capability('mod/data:managetemplates', $context)) {
if (!$DB->record_exists('data_fields', array('dataid'=>$data->id))) { // Brand new database!
redirect($CFG->wwwroot.'/mod/data/field.php?d='.$data->id); // Redirect to field entry
if (!$manager->has_fields()) {
redirect($CFG->wwwroot.'/mod/data/field.php?d='.$data->id); // Redirect to field entry.
}
}
if ($rid) {
// When editing an existing record, we require the session key
// When editing an existing record, we require the session key.
require_sesskey();
}
// Get Group information for permission testing and record creation
// Get Group information for permission testing and record creation.
$currentgroup = groups_get_activity_group($cm);
$groupmode = groups_get_activity_groupmode($cm);
if (!has_capability('mod/data:manageentries', $context)) {
if ($rid) {
// User is editing an existing record
// User is editing an existing record.
if (!data_user_can_manage_entry($record, $data, $context)) {
throw new \moodle_exception('noaccess', 'data');
}
} else if (!data_user_can_add_entry($data, $currentgroup, $groupmode, $context)) {
// User is trying to create a new record
// User is trying to create a new record.
throw new \moodle_exception('noaccess', 'data');
}
}
/// RSS and CSS and JS meta
// RSS and CSS and JS meta.
if (!empty($CFG->enablerssfeeds) && !empty($CFG->data_enablerssfeeds) && $data->rssarticles > 0) {
$courseshortname = format_string($course->shortname, true, array('context' => context_course::instance($course->id)));
$rsstitle = $courseshortname . ': ' . format_string($data->name);
@ -132,16 +114,7 @@ if ($data->jstemplate) {
$PAGE->requires->js('/mod/data/js.php?d='.$data->id, true);
}
$possiblefields = $DB->get_records('data_fields', array('dataid'=>$data->id), 'id');
foreach ($possiblefields as $field) {
if ($field->type == 'file' || $field->type == 'picture') {
require_once($CFG->dirroot.'/repository/lib.php');
break;
}
}
/// Define page variables
// Define page variables.
$strdata = get_string('modulenameplural','data');
if ($rid) {
@ -156,86 +129,53 @@ $PAGE->activityheader->disable();
// Process incoming data for adding/updating records.
// Keep track of any notifications.
$generalnotifications = array();
$fieldnotifications = array();
// Keep track of any notifications ad submitted data.
$processeddata = null;
$datarecord = data_submitted() ?: null;
// Process the submitted form.
if ($datarecord = data_submitted() and confirm_sesskey()) {
if ($rid) {
// Updating an existing record.
if ($datarecord && confirm_sesskey()) {
// Validate the form to ensure that enough data was submitted.
$fields = $manager->get_field_records();
$processeddata = data_process_submission($data, $fields, $datarecord);
// Retrieve the format for the fields.
$fields = $DB->get_records('data_fields', array('dataid' => $datarecord->d));
// Validate the form to ensure that enough data was submitted.
$processeddata = data_process_submission($data, $fields, $datarecord);
// Add the new notification data.
$generalnotifications = array_merge($generalnotifications, $processeddata->generalnotifications);
$fieldnotifications = array_merge($fieldnotifications, $processeddata->fieldnotifications);
if ($processeddata->validated) {
// Enough data to update the record.
if ($processeddata->validated) {
if ($rid) {
$recordid = $rid;
// Updating an existing record.
data_update_record_fields_contents($data, $record, $context, $datarecord, $processeddata);
core_tag_tag::set_item_tags('mod_data', 'data_records', $rid, $context, $tags);
$viewurl = new moodle_url('/mod/data/view.php', array(
'd' => $data->id,
'rid' => $rid,
));
redirect($viewurl);
} else {
// Add instance to data_record.
$recordid = data_add_record($data, $currentgroup);
if ($recordid) {
// Now populate the fields contents of the new record.
data_add_fields_contents_to_new_record($data, $context, $recordid, $fields, $datarecord, $processeddata);
}
}
} else {
// No recordid was specified - creating a new entry.
// Retrieve the format for the fields.
$fields = $DB->get_records('data_fields', array('dataid' => $datarecord->d));
// Validate the form to ensure that enough data was submitted.
$processeddata = data_process_submission($data, $fields, $datarecord);
// Add the new notification data.
$generalnotifications = array_merge($generalnotifications, $processeddata->generalnotifications);
$fieldnotifications = array_merge($fieldnotifications, $processeddata->fieldnotifications);
// Add instance to data_record.
if ($processeddata->validated && $recordid = data_add_record($data, $currentgroup)) {
// Now populate the fields contents of the new record.
data_add_fields_contents_to_new_record($data, $context, $recordid, $fields, $datarecord, $processeddata);
if ($recordid) {
core_tag_tag::set_item_tags('mod_data', 'data_records', $recordid, $context, $tags);
if (!empty($datarecord->saveandview)) {
$viewurl = new moodle_url('/mod/data/view.php', array(
'd' => $data->id,
'rid' => $recordid,
));
redirect($viewurl);
} else if (!empty($datarecord->saveandadd)) {
if (!empty($datarecord->saveandadd)) {
// User has clicked "Save and add another". Reset all of the fields.
$datarecord = null;
} else {
$viewurl = new moodle_url('/mod/data/view.php', [
'd' => $data->id,
'rid' => $recordid,
]);
redirect($viewurl);
}
}
}
}
// End of form processing.
/// Print the page header
echo $OUTPUT->header();
groups_print_activity_menu($cm, $CFG->wwwroot.'/mod/data/edit.php?d='.$data->id);
/// Print the browsing interface
$patterns = array(); //tags to replace
$replacement = array(); //html to replace those yucky tags
//form goes here first in case add template is empty
// Form goes here first in case add template is empty.
echo '<form enctype="multipart/form-data" action="edit.php" method="post">';
echo '<div>';
echo '<input name="d" value="'.$data->id.'" type="hidden" />';
@ -249,80 +189,45 @@ if (!$rid){
echo $OUTPUT->heading(get_string('editentry','data'));
}
/******************************************
* Regular expression replacement section *
******************************************/
if ($data->addtemplate){
$possiblefields = $DB->get_records('data_fields', array('dataid'=>$data->id), 'id');
$patterns = array();
$replacements = array();
$template = $manager->get_template($mode);
echo $template->parse_add_entry($processeddata, $rid, $datarecord);
///then we generate strings to replace
foreach ($possiblefields as $eachfield){
$field = data_get_field($eachfield, $data);
// To skip unnecessary calls to display_add_field().
if (strpos($data->addtemplate, "[[".$field->field->name."]]") !== false) {
// Replace the field tag.
$patterns[] = "[[".$field->field->name."]]";
$errors = '';
if (!empty($fieldnotifications[$field->field->name])) {
foreach ($fieldnotifications[$field->field->name] as $notification) {
$errors .= $OUTPUT->notification($notification);
}
}
$replacements[] = $errors . $field->display_add_field($rid, $datarecord);
}
// Replace the field id tag.
$patterns[] = "[[".$field->field->name."#id]]";
$replacements[] = 'field_'.$field->field->id;
}
if (core_tag_tag::is_enabled('mod_data', 'data_records')) {
$patterns[] = "##tags##";
$replacements[] = data_generate_tag_form($rid);
}
$newtext = str_ireplace($patterns, $replacements, $data->{$mode});
} else { //if the add template is not yet defined, print the default form!
echo data_generate_default_template($data, 'addtemplate', $rid, true, false);
$newtext = '';
if (empty($redirectbackto)) {
$redirectbackto = new \moodle_url('/mod/data/view.php', ['id' => $cm->id]);
}
foreach ($generalnotifications as $notification) {
echo $OUTPUT->notification($notification);
}
echo $newtext;
$redirectbackto = !empty($redirectbackto) ? $redirectbackto :
new \moodle_url('/mod/data/view.php', ['d' => $data->id]);
$actionbuttons = html_writer::link($redirectbackto, get_string('cancel'), ['class' => 'btn btn-secondary']);
$actionbuttons .= html_writer::empty_tag('input', ['type' => 'submit', 'name' => 'saveandview',
'value' => get_string('save'), 'class' => 'btn btn-primary ml-2']);
$actionbuttons = html_writer::link(
$redirectbackto,
get_string('cancel'),
['class' => 'btn btn-secondary', 'role' => 'button']
);
$actionbuttons .= html_writer::empty_tag('input', [
'type' => 'submit',
'name' => 'saveandview',
'value' => get_string('save'),
'class' => 'btn btn-primary ml-2'
]);
if (!$rid && ((!$data->maxentries) ||
has_capability('mod/data:manageentries', $context) ||
(data_numentries($data) < ($data->maxentries - 1)))) {
$actionbuttons .= html_writer::empty_tag('input', ['type' => 'submit', 'name' => 'saveandadd',
'value' => get_string('saveandadd', 'data'), 'class' => 'btn btn-primary ml-2']);
has_capability('mod/data:manageentries', $context) ||
(data_numentries($data) < ($data->maxentries - 1)))) {
$actionbuttons .= html_writer::empty_tag('input', [
'type' => 'submit', 'name' => 'saveandadd',
'value' => get_string('saveandadd', 'data'), 'class' => 'btn btn-primary ml-2'
]);
}
echo html_writer::div($actionbuttons, 'mdl-align mt-2');
echo $OUTPUT->box_end();
echo '</div></form>';
/// Finish the page
// Print the stuff that need to come after the form fields.
if (!$fields = $DB->get_records('data_fields', array('dataid'=>$data->id))) {
throw new \moodle_exception('nofieldindatabase', 'data');
}
foreach ($fields as $eachfield) {
$field = data_get_field($eachfield, $data);
$possiblefields = $manager->get_fields();
foreach ($possiblefields as $field) {
$field->print_after_form();
}
// Finish the page.
if (empty($possiblefields)) {
throw new \moodle_exception('nofieldindatabase', 'data');
}
echo $OUTPUT->footer();

View File

@ -90,7 +90,10 @@ class data_field_date extends data_field_base {
}
//Enable the following three functions once core API issues have been addressed.
function display_search_field($value=0) {
function display_search_field($value = null) {
if (empty($value)) {
$value = ['timestamp' => 0, 'usedate' => false];
}
$selectors = html_writer::select_time('days', 'f_'.$this->field->id.'_d', $value['timestamp'])
. html_writer::select_time('months', 'f_'.$this->field->id.'_m', $value['timestamp'])
. html_writer::select_time('years', 'f_'.$this->field->id.'_y', $value['timestamp']);

View File

@ -1,27 +1,26 @@
<?php
///////////////////////////////////////////////////////////////////////////
// //
// NOTICE OF COPYRIGHT //
// //
// Moodle - Modular Object-Oriented Dynamic Learning Environment //
// http://moodle.org //
// //
// Copyright (C) 1999-onwards Moodle Pty Ltd http://moodle.com //
// //
// This program 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 2 of the License, or //
// (at your option) any later version. //
// //
// This program 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: //
// //
// http://www.gnu.org/copyleft/gpl.html //
// //
///////////////////////////////////////////////////////////////////////////
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Class file field for database activity
*
* @package datafield_file
* @copyright 2005 Martin Dougiamas
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class data_field_file extends data_field_base {
var $type = 'file';
@ -43,7 +42,10 @@ class data_field_file extends data_field_base {
}
function display_add_field($recordid = 0, $formdata = null) {
global $DB, $OUTPUT, $PAGE;
global $CFG, $DB, $OUTPUT, $PAGE;
// Necessary for the constants used in args.
require_once($CFG->dirroot . '/repository/lib.php');
$itemid = null;

View File

@ -1,26 +1,26 @@
<?php
///////////////////////////////////////////////////////////////////////////
// //
// NOTICE OF COPYRIGHT //
// //
// Moodle - Modular Object-Oriented Dynamic Learning Environment //
// http://moodle.org //
// //
// Copyright (C) 1999-onwards Moodle Pty Ltd http://moodle.com //
// //
// This program 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 2 of the License, or //
// (at your option) any later version. //
// //
// This program 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: //
// //
// http://www.gnu.org/copyleft/gpl.html //
// //
///////////////////////////////////////////////////////////////////////////
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Class picture field for database activity
*
* @package datafield_picture
* @copyright 2005 Martin Dougiamas
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class data_field_picture extends data_field_base {
var $type = 'picture';
@ -47,6 +47,9 @@ class data_field_picture extends data_field_base {
function display_add_field($recordid = 0, $formdata = null) {
global $CFG, $DB, $OUTPUT, $USER, $PAGE;
// Necessary for the constants used in args.
require_once($CFG->dirroot . '/repository/lib.php');
$file = false;
$content = false;
$alttext = '';

View File

@ -294,12 +294,10 @@ $string['noaccess'] = 'You do not have access to this page';
$string['nodefinedfields'] = 'New preset has no defined fields!';
$string['nofieldcontent'] = 'Field content not found';
$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.';
$string['notinjectivemap'] = 'Not an injective map';
$string['notopenyet'] = 'Sorry, this activity is not available until {$a}';
@ -370,6 +368,8 @@ Note: If entries are required before viewing, the database auto-linking filter s
$string['requiredfield'] = 'Required field';
$string['resetsettings'] = 'Reset filters';
$string['resettemplate'] = 'Reset template';
$string['resettemplateconfirmtitle'] = 'Reset template?';
$string['resettemplateconfirm'] = 'Resetting a template removes the existing preset and any customisations you have done to the template. You can\'t undo this action.';
$string['resizingimages'] = 'Resizing image thumbnails...';
$string['rows'] = 'rows';
$string['rssglobaldisabled'] = 'Disabled. See site configuration variables.';
@ -407,6 +407,7 @@ $string['tags'] = 'Tags';
$string['tagsdeleted'] = 'Database tags have been deleted';
$string['teachersandstudents'] = '{$a->teachers} and {$a->students}';
$string['templates'] = 'Templates';
$string['templatereset'] = 'Template reset';
$string['templatesnavigation'] = 'Templates tertiary navigation';
$string['templatesaved'] = 'Template saved';
$string['text'] = 'Text';
@ -446,3 +447,5 @@ $string['unsupportedexport'] = '({$a->fieldtype}) cannot be exported.';
// Deprecated since Moodle 4.1.
$string['buttons'] = 'Actions';
$string['nolisttemplate'] = 'List template is not yet defined';
$string['nosingletemplate'] = 'Single template is not yet defined';

View File

@ -1,2 +1,4 @@
unsupportedexport,mod_data
buttons,mod_data
nosingletemplate,mod_data
nolisttemplate,mod_data

View File

@ -1784,8 +1784,9 @@ function data_print_preference_form($data, $perpage, $search, $sort='', $order='
// Determine if we are printing all fields for advanced search, or the template for advanced search
// If a template is not defined, use the deafault template and display all fields.
if(empty($data->asearchtemplate)) {
data_generate_default_template($data, 'asearchtemplate');
$asearchtemplate = $data->asearchtemplate;
if (empty($asearchtemplate)) {
$asearchtemplate = data_generate_default_template($data, 'asearchtemplate', 0, false, false);
}
static $fields = array();
@ -1841,7 +1842,7 @@ function data_print_preference_form($data, $perpage, $search, $sort='', $order='
$options->para=false;
$options->noclean=true;
echo '<tr><td>';
echo preg_replace($patterns, $replacement, format_text($data->asearchtemplate, FORMAT_HTML, $options));
echo preg_replace($patterns, $replacement, format_text($asearchtemplate, FORMAT_HTML, $options));
echo '</td></tr>';
echo '<tr><td colspan="4"><br/>' .

View File

@ -959,6 +959,10 @@ function data_get_tag_title_field($dataid) {
$validfieldtypes = array('text', 'textarea', 'menu', 'radiobutton', 'checkbox', 'multimenu', 'url');
$fields = $DB->get_records('data_fields', ['dataid' => $dataid]);
$template = $DB->get_field('data', 'addtemplate', ['id' => $dataid]);
if (empty($template)) {
$data = $DB->get_record('data', ['id' => $dataid]);
$template = data_generate_default_template($data, 'addtemplate', 0, false, false);
}
$filteredfields = [];

View File

@ -84,30 +84,24 @@ echo $actionbar->get_templates_action_bar();
echo $OUTPUT->heading(get_string($mode, 'data'), 2, 'mb-4');
if (($formdata = data_submitted()) && confirm_sesskey()) {
$notificationstr = get_string('templatesaved', 'data');
if (!empty($formdata->defaultform)) {
// Reset the template to default, but don't save yet.
$instance->{$mode} = data_generate_default_template($instance, $mode, 0, false, false);
// Reset the template to default.
$formdata->{$mode} = '';
if ($mode == 'listtemplate') {
$instance->listtemplateheader = '';
$instance->listtemplatefooter = '';
$formdata->listtemplateheader = '';
$formdata->listtemplatefooter = '';
}
} else {
if ($manager->update_templates($formdata)) {
// Reload instance.
$instance = $manager->get_instance();
echo $OUTPUT->notification(get_string('templatesaved', 'data'), 'notifysuccess');
if ($mode == 'rsstemplate') {
$formdata->rsstitletemplate = '';
}
$notificationstr = get_string('templatereset', 'data');
}
if ($manager->update_templates($formdata)) {
// Reload instance.
$instance = $manager->get_instance();
echo $OUTPUT->notification($notificationstr, 'notifysuccess');
}
}
/// If everything is empty then generate some defaults
if (empty($instance->addtemplate) && empty($instance->singletemplate) &&
empty($instance->listtemplate) && empty($instance->rsstemplate)) {
data_generate_default_template($instance, 'singletemplate');
data_generate_default_template($instance, 'listtemplate');
data_generate_default_template($instance, 'addtemplate');
data_generate_default_template($instance, 'asearchtemplate');
data_generate_default_template($instance, 'rsstemplate');
}
$renderer = $PAGE->get_renderer('mod_data');

View File

@ -66,8 +66,9 @@
}
}}
<div>{{title}}</div>
<form id="tempform" action="{{{url}}}" method="post">
<form id="edittemplateform" action="{{{url}}}" method="post">
<input name="sesskey" value="{{sesskey}}" type="hidden" />
<input name="defaultform" type="hidden" value=""/>
<div class="d-flex flex-row align-items-center">
{{#toolbar}}
{{> mod_data/template_editor_tools }}
@ -96,8 +97,8 @@
<div>
<input
class="btn btn-secondary"
type="submit"
name="defaultform"
type="button"
name="resetbutton"
value="{{#str}} resettemplate, data {{/str}}"
/>
<input

View File

@ -794,4 +794,117 @@ class template_test extends \advanced_testcase {
$rm = new rating_manager();
return $rm->get_ratings($ratingoptions);
}
/**
* Test parse add entry template parsing.
*
* @covers ::parse_add_entry
* @dataProvider parse_add_entry_provider
* @param string $templatecontent the template string
* @param string $expected expected output
* @param bool $newentry if it is a new entry or editing and existing one
*/
public function test_parse_add_entry(
string $templatecontent,
string $expected,
bool $newentry = false
) {
global $DB, $PAGE;
// Comments, tags, approval, user role.
$this->resetAfterTest();
$params = ['approval' => true];
$course = $this->getDataGenerator()->create_course();
$params['course'] = $course;
$activity = $this->getDataGenerator()->create_module('data', $params);
$author = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
// Generate an entry.
$generator = $this->getDataGenerator()->get_plugin_generator('mod_data');
$fieldrecord = (object)[
'name' => 'myfield',
'type' => 'text',
];
$field = $generator->create_field($fieldrecord, $activity);
if ($newentry) {
$entryid = null;
$entry = null;
} else {
$entryid = $generator->create_entry(
$activity,
[$field->field->id => 'Example entry'],
0,
['Cats', 'Dogs']
);
$entry = (object)[
'd' => $activity->id,
'rid' => $entryid,
"field_{$field->field->id}" => "New value",
];
}
$manager = manager::create_from_instance($activity);
// Some cooked variables for the regular expression.
$replace = [
'{fieldid}' => $field->field->id,
];
$processdata = (object)[
'generalnotifications' => ['GENERAL'],
'fieldnotifications' => [$field->field->name => ['FIELD']],
];
$parser = new template($manager, $templatecontent);
$result = $parser->parse_add_entry($processdata, $entryid, $entry);
// We don't want line breaks for the validations.
$result = str_replace("\n", '', $result);
$regexp = str_replace(array_keys($replace), array_values($replace), $expected);
$this->assertMatchesRegularExpression($regexp, $result);
}
/**
* Data provider for test_parse_add_entry().
*
* @return array of scenarios
*/
public function parse_add_entry_provider(): array {
return [
// Editing an entry.
'Teacher editing entry tags tag' => [
'templatecontent' => 'Some ##tags## tag',
'expected' => '|GENERAL.*Some .*select .*tags.*Cats.* tag|',
'newentry' => false,
],
'Teacher editing entry field name tag' => [
'templatecontent' => 'Some [[myfield]] tag',
'expected' => '|GENERAL.*Some .*FIELD.*field_{fieldid}.*input.*New value.* tag|',
'newentry' => false,
],
'Teacher editing entry field#id tag' => [
'templatecontent' => 'Some [[myfield#id]] tag',
'expected' => '|GENERAL.*Some field_{fieldid} tag|',
'newentry' => false,
],
// New entry.
'Teacher new entry tags tag' => [
'templatecontent' => 'Some ##tags## tag',
'expected' => '|GENERAL.*Some .*select .*tags\[\].* tag|',
'newentry' => true,
],
'Teacher new entry field name tag' => [
'templatecontent' => 'Some [[myfield]] tag',
'expected' => '|GENERAL.*Some .*FIELD.*field_{fieldid}.*input.*value="".* tag|',
'newentry' => true,
],
'Teacher new entry field#id name tag' => [
'templatecontent' => 'Some [[myfield#id]] tag',
'expected' => '|GENERAL.*Some field_{fieldid} tag|',
'newentry' => true,
],
];
}
}

View File

@ -449,11 +449,6 @@ if ($showactivity) {
$baseurl = new moodle_url($baseurl, $baseurlparams);
echo $OUTPUT->paging_bar($totalcount, $page, $nowperpage, $baseurl);
if (empty($data->singletemplate)){
echo $OUTPUT->notification(get_string('nosingletemplate','data'));
data_generate_default_template($data, 'singletemplate', 0, false, false);
}
require_once($CFG->dirroot.'/rating/lib.php');
if ($data->assessed != RATING_AGGREGATE_NONE) {
$ratingoptions = new stdClass;
@ -492,10 +487,6 @@ if ($showactivity) {
echo $OUTPUT->paging_bar($totalcount, $page, $nowperpage, $baseurl);
if (empty($data->listtemplate)){
echo $OUTPUT->notification(get_string('nolisttemplate','data'));
data_generate_default_template($data, 'listtemplate', 0, false, false);
}
echo $data->listtemplateheader;
$options = [
'search' => $search,