diff --git a/mod/data/amd/build/templateseditor.min.js b/mod/data/amd/build/templateseditor.min.js index 9da187ddee3..9491f41b9e6 100644 --- a/mod/data/amd/build/templateseditor.min.js +++ b/mod/data/amd/build/templateseditor.min.js @@ -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 * @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 \ No newline at end of file diff --git a/mod/data/amd/build/templateseditor.min.js.map b/mod/data/amd/build/templateseditor.min.js.map index 9a229818db5..b7e94269881 100644 --- a/mod/data/amd/build/templateseditor.min.js.map +++ b/mod/data/amd/build/templateseditor.min.js.map @@ -1 +1 @@ -{"version":3,"file":"templateseditor.min.js","sources":["../src/templateseditor.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Javascript module to control the template editor.\n *\n * @module mod_data/templateseditor\n * @copyright 2021 Mihail Geshoski \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {get_string as getString} from 'core/str';\nimport {confirm as confirmDialogue} from 'core/notification';\nimport {relativeUrl} from 'core/url';\n\n/**\n * Template editor constants.\n */\nconst selectors = {\n toggleTemplateEditor: 'input[name=\"useeditor\"]',\n};\n\n/**\n * Register event listeners for the module.\n *\n * @param {int} d The database ID\n * @param {string} mode The template mode\n */\nconst registerEventListeners = (d, mode) => {\n const toggleTemplateEditor = document.querySelector(selectors.toggleTemplateEditor);\n\n if (!toggleTemplateEditor) {\n return;\n }\n\n toggleTemplateEditor.addEventListener('click', async(event) => {\n event.preventDefault();\n // Whether the event action attempts to enable or disable the template editor.\n const enableTemplateEditor = event.target.checked;\n\n if (enableTemplateEditor) {\n // Display a confirmation dialog before enabling the template editor.\n confirmDialogue(\n getString('confirmation', 'admin'),\n getString('enabletemplateeditorcheck', 'mod_data'),\n getString('yes', 'core'),\n getString('no', 'core'),\n () => {\n window.location = relativeUrl('/mod/data/templates.php', {d: d, mode: mode, useeditor: true});\n }\n );\n } else {\n window.location = relativeUrl('/mod/data/templates.php', {d: d, mode: mode, useeditor: false});\n }\n });\n};\n\n/**\n * Initialize the module.\n *\n * @param {int} d The database ID\n * @param {string} mode The template mode\n */\nexport const init = (d, mode) => {\n registerEventListeners(d, mode);\n};\n"],"names":["selectors","d","mode","toggleTemplateEditor","document","querySelector","addEventListener","async","event","preventDefault","target","checked","window","location","useeditor","registerEventListeners"],"mappings":";;;;;;;;MA8BMA,+BACoB,wCA4CN,CAACC,EAAGC,QAnCO,EAACD,EAAGC,cACzBC,qBAAuBC,SAASC,cAAcL,gCAE/CG,sBAILA,qBAAqBG,iBAAiB,SAASC,MAAAA,QAC3CC,MAAMC,iBAEuBD,MAAME,OAAOC,mCAKlC,mBAAU,eAAgB,UAC1B,mBAAU,4BAA6B,aACvC,mBAAU,MAAO,SACjB,mBAAU,KAAM,SAChB,KACIC,OAAOC,UAAW,oBAAY,0BAA2B,CAACZ,EAAGA,EAAGC,KAAMA,KAAMY,WAAW,OAI/FF,OAAOC,UAAW,oBAAY,0BAA2B,CAACZ,EAAGA,EAAGC,KAAMA,KAAMY,WAAW,QAY/FC,CAAuBd,EAAGC"} \ No newline at end of file +{"version":3,"file":"templateseditor.min.js","sources":["../src/templateseditor.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Javascript module to control the template editor.\n *\n * @module mod_data/templateseditor\n * @copyright 2021 Mihail Geshoski \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {get_string as getString} from 'core/str';\nimport {prefetchStrings} from 'core/prefetch';\nimport {relativeUrl} from 'core/url';\nimport {saveCancel} from 'core/notification';\n\nprefetchStrings('admin', ['confirmation']);\nprefetchStrings('mod_data', [\n 'resettemplateconfirmtitle',\n 'resettemplateconfirm',\n 'resettemplate',\n 'enabletemplateeditorcheck',\n 'editorenable'\n]);\n\n/**\n * Template editor constants.\n */\nconst selectors = {\n toggleTemplateEditor: 'input[name=\"useeditor\"]',\n resetTemplate: 'input[name=\"defaultform\"]',\n resetButton: 'input[name=\"resetbutton\"]',\n editForm: '#edittemplateform',\n};\n\n/**\n * Register event listeners for the module.\n *\n * @param {int} instanceId The database ID\n * @param {string} mode The template mode\n */\nconst registerEventListeners = (instanceId, mode) => {\n registerResetButton();\n registerEditorToggler(instanceId, mode);\n};\n\nconst registerResetButton = () => {\n const editForm = document.querySelector(selectors.editForm);\n const resetButton = document.querySelector(selectors.resetButton);\n const resetTemplate = document.querySelector(selectors.resetTemplate);\n\n if (!resetButton || !resetTemplate || !editForm) {\n return;\n }\n\n resetButton.addEventListener('click', async(event) => {\n event.preventDefault();\n saveCancel(\n getString('resettemplateconfirmtitle', 'mod_data'),\n getString('resettemplateconfirm', 'mod_data'),\n getString('resettemplate', 'mod_data'),\n () => {\n resetTemplate.value = \"true\";\n editForm.submit();\n },\n null,\n {triggerElement: event.target}\n );\n });\n};\n\nconst registerEditorToggler = (instanceId, mode) => {\n const toggleTemplateEditor = document.querySelector(selectors.toggleTemplateEditor);\n\n if (!toggleTemplateEditor) {\n return;\n }\n\n toggleTemplateEditor.addEventListener('click', async(event) => {\n event.preventDefault();\n // Whether the event action attempts to enable or disable the template editor.\n const enableTemplateEditor = event.target.checked;\n\n if (enableTemplateEditor) {\n // Display a confirmation dialog before enabling the template editor.\n saveCancel(\n getString('confirmation', 'admin'),\n getString('enabletemplateeditorcheck', 'mod_data'),\n getString('editorenable', 'mod_data'),\n () => {\n window.location = relativeUrl('/mod/data/templates.php', {d: instanceId, mode: mode, useeditor: true});\n },\n null,\n {triggerElement: event.target}\n );\n } else {\n window.location = relativeUrl('/mod/data/templates.php', {d: instanceId, mode: mode, useeditor: false});\n }\n });\n};\n\n/**\n * Initialize the module.\n *\n * @param {int} instanceId The database ID\n * @param {string} mode The template mode\n */\nexport const init = (instanceId, mode) => {\n registerEventListeners(instanceId, mode);\n};\n"],"names":["selectors","registerResetButton","editForm","document","querySelector","resetButton","resetTemplate","addEventListener","async","event","preventDefault","value","submit","triggerElement","target","registerEditorToggler","instanceId","mode","toggleTemplateEditor","checked","window","location","d","useeditor","registerEventListeners"],"mappings":";;;;;;;;8BA4BgB,QAAS,CAAC,+CACV,WAAY,CACxB,4BACA,uBACA,gBACA,4BACA,uBAMEA,+BACoB,0BADpBA,wBAEa,4BAFbA,sBAGW,4BAHXA,mBAIQ,oBAcRC,oBAAsB,WAClBC,SAAWC,SAASC,cAAcJ,oBAClCK,YAAcF,SAASC,cAAcJ,uBACrCM,cAAgBH,SAASC,cAAcJ,yBAExCK,aAAgBC,eAAkBJ,UAIvCG,YAAYE,iBAAiB,SAASC,MAAAA,QAClCC,MAAMC,+CAEF,mBAAU,4BAA6B,aACvC,mBAAU,uBAAwB,aAClC,mBAAU,gBAAiB,aAC3B,KACIJ,cAAcK,MAAQ,OACtBT,SAASU,WAEb,KACA,CAACC,eAAgBJ,MAAMK,aAK7BC,sBAAwB,CAACC,WAAYC,cACjCC,qBAAuBf,SAASC,cAAcJ,gCAE/CkB,sBAILA,qBAAqBX,iBAAiB,SAASC,MAAAA,QAC3CC,MAAMC,iBAEuBD,MAAMK,OAAOK,sCAKlC,mBAAU,eAAgB,UAC1B,mBAAU,4BAA6B,aACvC,mBAAU,eAAgB,aAC1B,KACIC,OAAOC,UAAW,oBAAY,0BAA2B,CAACC,EAAGN,WAAYC,KAAMA,KAAMM,WAAW,MAEpG,KACA,CAACV,eAAgBJ,MAAMK,SAG3BM,OAAOC,UAAW,oBAAY,0BAA2B,CAACC,EAAGN,WAAYC,KAAMA,KAAMM,WAAW,sBAWxF,CAACP,WAAYC,QAlEF,EAACD,WAAYC,QACxChB,sBACAc,sBAAsBC,WAAYC,OAiElCO,CAAuBR,WAAYC"} \ No newline at end of file diff --git a/mod/data/amd/src/templateseditor.js b/mod/data/amd/src/templateseditor.js index 2675eda9563..5dc45eba7b6 100644 --- a/mod/data/amd/src/templateseditor.js +++ b/mod/data/amd/src/templateseditor.js @@ -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); }; diff --git a/mod/data/classes/manager.php b/mod/data/classes/manager.php index a2a18c17ba9..48c2887e49d 100644 --- a/mod/data/classes/manager.php +++ b/mod/data/classes/manager.php @@ -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 mod_data_renderer; use moodle_page; @@ -276,6 +278,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 diff --git a/mod/data/classes/output/template_editor.php b/mod/data/classes/output/template_editor.php index dcfebc16d7d..6da2e9a7832 100644 --- a/mod/data/classes/output/template_editor.php +++ b/mod/data/classes/output/template_editor.php @@ -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() ); } diff --git a/mod/data/classes/search/entry.php b/mod/data/classes/search/entry.php index b1a6fd9fd61..dc65a5751e7 100644 --- a/mod/data/classes/search/entry.php +++ b/mod/data/classes/search/entry.php @@ -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) { diff --git a/mod/data/classes/template.php b/mod/data/classes/template.php index 5fac87b9f21..37c97086372 100644 --- a/mod/data/classes/template.php +++ b/mod/data/classes/template.php @@ -231,6 +231,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. * @@ -799,4 +808,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; + } } diff --git a/mod/data/edit.php b/mod/data/edit.php index dbaad1e54ca..c6adcfe73fa 100644 --- a/mod/data/edit.php +++ b/mod/data/edit.php @@ -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 '
'; echo '
'; echo ''; @@ -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 '
'; - -/// 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(); diff --git a/mod/data/field.php b/mod/data/field.php index de20d658f86..65dbc07c912 100644 --- a/mod/data/field.php +++ b/mod/data/field.php @@ -117,7 +117,7 @@ $data->instance = $cm->instance; /************************************ * Data Processing * ***********************************/ -$renderer = $PAGE->get_renderer('mod_data'); +$renderer = $manager->get_renderer(); if ($formimportzip->is_cancelled()) { redirect(new moodle_url('/mod/data/field.php', ['d' => $data->id])); diff --git a/mod/data/field/date/field.class.php b/mod/data/field/date/field.class.php index 758f307a53b..861c241aa99 100644 --- a/mod/data/field/date/field.class.php +++ b/mod/data/field/date/field.class.php @@ -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']); diff --git a/mod/data/field/file/field.class.php b/mod/data/field/file/field.class.php index 19f372cc51d..16fe7ce15d8 100644 --- a/mod/data/field/file/field.class.php +++ b/mod/data/field/file/field.class.php @@ -1,27 +1,26 @@ . +/** + * 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; diff --git a/mod/data/field/picture/field.class.php b/mod/data/field/picture/field.class.php index 2a432d8fec4..afd8749cae3 100644 --- a/mod/data/field/picture/field.class.php +++ b/mod/data/field/picture/field.class.php @@ -1,26 +1,26 @@ . + +/** + * 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 = ''; diff --git a/mod/data/lang/en/data.php b/mod/data/lang/en/data.php index 93c419360c0..6502d218200 100644 --- a/mod/data/lang/en/data.php +++ b/mod/data/lang/en/data.php @@ -297,12 +297,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}'; @@ -373,6 +371,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.'; @@ -410,6 +410,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'; @@ -449,3 +450,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'; diff --git a/mod/data/lang/en/deprecated.txt b/mod/data/lang/en/deprecated.txt index 3faf76f565d..e3ff3c52742 100644 --- a/mod/data/lang/en/deprecated.txt +++ b/mod/data/lang/en/deprecated.txt @@ -1,2 +1,4 @@ unsupportedexport,mod_data buttons,mod_data +nosingletemplate,mod_data +nolisttemplate,mod_data diff --git a/mod/data/lib.php b/mod/data/lib.php index ceaf98cc83b..d1375cdbe51 100644 --- a/mod/data/lib.php +++ b/mod/data/lib.php @@ -1731,8 +1731,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(); @@ -1788,7 +1789,7 @@ function data_print_preference_form($data, $perpage, $search, $sort='', $order=' $options->para=false; $options->noclean=true; echo ''; - echo preg_replace($patterns, $replacement, format_text($data->asearchtemplate, FORMAT_HTML, $options)); + echo preg_replace($patterns, $replacement, format_text($asearchtemplate, FORMAT_HTML, $options)); echo ''; echo '
' . diff --git a/mod/data/locallib.php b/mod/data/locallib.php index 2cb5020de80..37cf3e805ec 100644 --- a/mod/data/locallib.php +++ b/mod/data/locallib.php @@ -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 = []; diff --git a/mod/data/preset.php b/mod/data/preset.php index 2cc88ebfd3e..b97b84570cf 100644 --- a/mod/data/preset.php +++ b/mod/data/preset.php @@ -80,7 +80,7 @@ $data->course = $cm->course; $data->cmidnumber = $cm->idnumber; $data->instance = $cm->instance; -$renderer = $PAGE->get_renderer('mod_data'); +$renderer = $manager->get_renderer(); $presets = $manager->get_available_presets(); if ($action === 'export') { diff --git a/mod/data/templates.php b/mod/data/templates.php index 9613d9e8575..e22faab327c 100644 --- a/mod/data/templates.php +++ b/mod/data/templates.php @@ -84,33 +84,27 @@ 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'); +$renderer = $manager->get_renderer(); $templateeditor = new \mod_data\output\template_editor($manager, $mode); echo $renderer->render($templateeditor); diff --git a/mod/data/templates/template_editor.mustache b/mod/data/templates/template_editor.mustache index e4e4461f8f2..0ab2241ec45 100644 --- a/mod/data/templates/template_editor.mustache +++ b/mod/data/templates/template_editor.mustache @@ -66,8 +66,9 @@ } }}
{{title}}
-
+ +
{{#toolbar}} {{> mod_data/template_editor_tools }} @@ -96,8 +97,8 @@
fields" exist: + | database | type | name | description | + | data1 | text | Test field name | Test field description | + | data1 | text | Test field 2 name | Test field 2 description | When I am on the "Course 1" course page logged in as student1 And I add an entry to "Test database name" database with: | Test field name | Student original entry | @@ -92,8 +85,6 @@ Feature: Users can add entries to database activities And I press "Save and display" And I add a "Short text" field to "Test database name" database and I fill the form with: | Field name | Test1 | - And I navigate to "Templates" in current page administration - And I press "Save template" And I log out And I am on the "Test database name" "data activity" page logged in as student1 And I press "Add entry" @@ -129,14 +120,12 @@ Feature: Users can add entries to database activities Scenario Outline: Users see the Add entry button in the view page when some field has been created only. Given I am on the "Test database name" "data activity" page logged in as And I should not see "Add entry" - When I log out - And I am on the "Test database name" "data activity" page logged in as teacher1 - And I add a "Short text" field to "Test database name" database and I fill the form with: - | Field name | Test field name | - | Field description | Test field description | And I log out - And I am on the "Test database name" "data activity" page logged in as - Then I should see "Add entry" + When the following "mod_data > fields" exist: + | database | type | name | description | + | data1 | text | Test field name | Test field description | + Then I am on the "Test database name" "data activity" page logged in as + And I should see "Add entry" Examples: | user | diff --git a/mod/data/tests/behat/data_activity_completion.feature b/mod/data/tests/behat/data_activity_completion.feature index 2095ef23fc4..09c6a7efa91 100644 --- a/mod/data/tests/behat/data_activity_completion.feature +++ b/mod/data/tests/behat/data_activity_completion.feature @@ -36,8 +36,6 @@ Feature: View activity completion in the database activity And I press "Save and display" And I add a "Short text" field to "Music history" database and I fill the form with: | Field name | Instrument types | - And I navigate to "Templates" in current page administration - And I press "Save template" And I log out Scenario: View automatic completion items as a teacher diff --git a/mod/data/tests/behat/data_activity_completion_pass_grade.feature b/mod/data/tests/behat/data_activity_completion_pass_grade.feature index f7f7fc942d0..17994603d2b 100644 --- a/mod/data/tests/behat/data_activity_completion_pass_grade.feature +++ b/mod/data/tests/behat/data_activity_completion_pass_grade.feature @@ -50,8 +50,6 @@ Feature: Completion pass grade And I press "Save and display" And I add a "Short text" field to "Music history" database and I fill the form with: | Field name | Instrument types | - And I navigate to "Templates" in current page administration - And I press "Save template" And I log out Scenario: View automatic completion items as a teacher diff --git a/mod/data/tests/behat/default_templates.feature b/mod/data/tests/behat/default_templates.feature new file mode 100644 index 00000000000..ec63a4bd083 --- /dev/null +++ b/mod/data/tests/behat/default_templates.feature @@ -0,0 +1,158 @@ +@mod @mod_data +Feature: Users can use mod_data without editing the templates + In order to use the database module + As a teacher + I need to manage fields and entries using always the default templates. + + Background: + Given the following "users" exist: + | username | firstname | lastname | email | + | teacher1 | Teacher | 1 | teacher1@example.com | + 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 the following "mod_data > fields" exist: + | database | type | name | description | + | data1 | text | field1 | Test field description | + | data1 | text | field2 | Test field 2 description | + And the following "mod_data > entries" exist: + | database | field1 | field2 | + | data1 | Student entry 1 | Some content 1 | + | data1 | Student entry 2 | Some content 2 | + And I am on the "Test database name" "data activity" page logged in as teacher1 + + @javascript + Scenario: The default view templates should be updated when a field is added. + Given I navigate to "Database" in current page administration + And I should see "field1" + And I should see "field2" + And I should see "Student entry 1" + And I should see "Some content 1" + And I should see "Student entry 2" + And I should see "Some content 2" + And I set the field "View mode tertiary navigation" to "Single view" + And I should see "field1" + And I should see "field2" + And I should see "Student entry 1" + And I should see "Some content 1" + And I should not see "Student entry 2" + And I should not see "Some content 2" + When the following "mod_data > fields" exist: + | database | type | name | description | + | data1 | text | field3 | Test field 3 description | + Then I navigate to "Database" in current page administration + And I should see "field1" + And I should see "field2" + And I should see "field3" + And I should see "Student entry 1" + And I should see "Some content 1" + And I should see "Student entry 2" + And I should see "Some content 2" + And I set the field "View mode tertiary navigation" to "Single view" + And I should see "field1" + And I should see "field2" + And I should see "field3" + And I should see "Student entry 1" + And I should see "Some content 1" + And I should not see "Student entry 2" + And I should not see "Some content 2" + + Scenario: The default add templates should be updated when a field is added. + Given I navigate to "Database" in current page administration + And I click on "Add entry" "button" + And I should see "field1" + And I should see "field2" + When the following "mod_data > fields" exist: + | database | type | name | description | + | data1 | text | field3 | Test field 3 description | + Then I navigate to "Database" in current page administration + And I click on "Add entry" "button" + And I should see "field1" + And I should see "field2" + And I should see "field3" + + @javascript + Scenario: The default view templates should be updated when a field is deleted. + Given I navigate to "Database" in current page administration + And I should see "field1" + And I should see "field2" + And I should see "Student entry 1" + And I should see "Some content 1" + And I should see "Student entry 2" + And I should see "Some content 2" + And I set the field "View mode tertiary navigation" to "Single view" + And I should see "field1" + And I should see "field2" + And I should see "Student entry 1" + And I should see "Some content 1" + And I should not see "Student entry 2" + And I should not see "Some content 2" + When I navigate to "Fields" in current page administration + And I click on "Delete" "link" in the "field2" "table_row" + And I click on "Continue" "button" + Then I navigate to "Database" in current page administration + And I should see "field1" + And I should not see "field2" + And I should see "Student entry 1" + And I should not see "Some content 1" + And I should see "Student entry 2" + And I should not see "Some content 2" + And I set the field "View mode tertiary navigation" to "Single view" + And I should see "field1" + And I should not see "field2" + And I should see "Student entry 1" + And I should not see "Some content 1" + And I should not see "Student entry 2" + And I should not see "Some content 2" + + Scenario: The default add templates should be updated when a field is deleted. + Given I navigate to "Database" in current page administration + And I click on "Add entry" "button" + And I should see "field1" + And I should see "field2" + When I navigate to "Fields" in current page administration + And I click on "Delete" "link" in the "field2" "table_row" + And I click on "Continue" "button" + Then I navigate to "Database" in current page administration + And I click on "Add entry" "button" + And I should see "field1" + And I should not see "field2" + + @javascript + Scenario: The dynamic default templates can be reset after a manual edition. + Given I navigate to "Templates" in current page administration + And I set the following fields to these values: + | Header | New header! | + | Repeated entry | This is the template content | + | Footer | New footer! | + And I click on "Save template" "button" + And I navigate to "Database" in current page administration + And I should see "New header!" + And I should see "This is the template content" + And I should see "New footer!" + And I should not see "Student entry 1" + And I should not see "Some content 1" + When I navigate to "Templates" in current page administration + And I click on "Reset template" "button" + And I click on "Reset template" "button" in the "Reset template?" "dialogue" + And I should see "Template reset" + And I navigate to "Database" in current page administration + And I should not see "New header!" + And I should not see "This is the template content" + And I should not see "New footer!" + And I should see "Student entry 1" + And I should see "Some content 1" + Then the following "mod_data > fields" exist: + | database | type | name | description | + | data1 | text | field3 | Test field 3 description | + And I navigate to "Database" in current page administration + And I click on "Add entry" "button" + And I should see "field1" + And I should see "field2" + And I should see "field3" diff --git a/mod/data/tests/behat/edit_templates.feature b/mod/data/tests/behat/edit_templates.feature index 32757d1d62e..bf263616583 100644 --- a/mod/data/tests/behat/edit_templates.feature +++ b/mod/data/tests/behat/edit_templates.feature @@ -21,13 +21,6 @@ Feature: Users can edit the database templates | database | type | name | description | | data1 | text | field1 | Test field description | | data1 | text | field2 | Test field 2 description | - And the following "mod_data > templates" exist: - | database | name | - | data1 | singletemplate | - | data1 | listtemplate | - | data1 | addtemplate | - | data1 | asearchtemplate | - | data1 | rsstemplate | And the following "mod_data > entries" exist: | database | field1 | field2 | | data1 | Student entry 1 | Some content 1 | @@ -113,3 +106,27 @@ Feature: Users can edit the database templates When I navigate to "Database" in current page administration Then I should not see "Nope" And I should see "Yep!" + + @javascript + Scenario: Reset database activity template + Given I set the following fields to these values: + | Header | New header! | + | Repeated entry | This is the template content | + | Footer | New footer! | + And I click on "Save template" "button" + And I navigate to "Database" in current page administration + And I should see "New header!" + And I should see "This is the template content" + And I should see "New footer!" + And I should not see "Student entry 1" + And I should not see "Some content 1" + When I navigate to "Templates" in current page administration + And I click on "Reset template" "button" + And I click on "Reset template" "button" in the "Reset template?" "dialogue" + Then I should see "Template reset" + And I navigate to "Database" in current page administration + And I should not see "New header!" + And I should not see "This is the template content" + And I should not see "New footer!" + And I should see "Student entry 1" + And I should see "Some content 1" diff --git a/mod/data/tests/behat/manageapproved.feature b/mod/data/tests/behat/manageapproved.feature index 8257db07da1..40b22056a27 100644 --- a/mod/data/tests/behat/manageapproved.feature +++ b/mod/data/tests/behat/manageapproved.feature @@ -30,8 +30,6 @@ Feature: Users can edit approved entries in database activities And I add a "Short text" field to "Test database name" database and I fill the form with: | Field name | Test field name | | Field description | Test field description | - # To generate the default templates. - And I navigate to "Templates" in current page administration And I log out # Add an entry as a student. And I am on the "Test database name" "data activity" page logged in as student1 @@ -63,8 +61,6 @@ Feature: Users can edit approved entries in database activities And I add a "Short text" field to "Test database name" database and I fill the form with: | Field name | Test field name | | Field description | Test field description | - # To generate the default templates. - And I navigate to "Templates" in current page administration And I log out # Add an entry as a student. And I am on the "Test database name" "data activity" page logged in as student1 diff --git a/mod/data/tests/behat/required_entries.feature b/mod/data/tests/behat/required_entries.feature index 289dac8533a..4ef61598773 100644 --- a/mod/data/tests/behat/required_entries.feature +++ b/mod/data/tests/behat/required_entries.feature @@ -19,109 +19,29 @@ Feature: Users can be required to specify certain fields when adding entries to And the following "activities" exist: | activity | name | intro | course | idnumber | | data | Test database name | n | C1 | data1 | - And I log in as "teacher1" - And I am on "Course 1" course homepage - And I add a "Short text" field to "Test database name" database and I fill the form with: - | Field name | Base Text input | - | Required | yes | - | Field description | Base Text input | - And I add a "Checkbox" field to "Test database name" database and I fill the form with: - | Field name | Required Checkbox | - | Field description | Required Checkbox | - | Required | yes | - | Options | Required Checkbox Option 1 | - And I follow "Fields" - And I select "Checkbox" from the "newtype" singleselect - And I set the following fields to these values: - | Field name | Required Two-Option Checkbox | - | Field description | Required Two-Option Checkbox | - | Required | yes | - And I set the field "Options" to multiline: - """ - RTOC Option 1 - RTOC Option 2 - """ - And I press "Add" - And I add a "Coordinates" field to "Test database name" database and I fill the form with: - | Field name | Required Coordinates | - | Field description | Required Coordinates | - | Required | yes | - And I add a "Dropdown list" field to "Test database name" database and I fill the form with: - | Field name | Required Menu | - | Field description | Required Menu | - | Required | yes | - | Options | Option 1 | - And I add a "Number" field to "Test database name" database and I fill the form with: - | Field name | Required Number | - | Field description | Required Number | - | Required | yes | - And I add a "Radio button" field to "Test database name" database and I fill the form with: - | Field name | Required Radio | - | Field description | Required Radio | - | Required | yes | - | Options | Required Radio Option 1 | - And I add a "Short text" field to "Test database name" database and I fill the form with: - | Field name | Required Text input | - | Field description | Required Text input | - | Required | yes | - And I add a "Text area" field to "Test database name" database and I fill the form with: - | Field name | Required Text area | - | Field description | Required Text area | - | Required | yes | - And I add a "URL" field to "Test database name" database and I fill the form with: - | Field name | Required URL | - | Field description | Required URL | - | Required | yes | - And I add a "Multi-select list" field to "Test database name" database and I fill the form with: - | Field name | Required Multimenu | - | Field description | Required Multimenu | - | Required | yes | - | Options | Option 1 | - And I follow "Fields" - And I select "Multi-select list" from the "newtype" singleselect - And I set the following fields to these values: - | Field name | Required Two-Option Multimenu | - | Field description | Required Two-Option Multimenu | - | Required | yes | - And I set the field "Options" to multiline: - """ - Option 1 - Option 2 - """ - And I press "Add" - And I add a "Checkbox" field to "Test database name" database and I fill the form with: - | Field name | Not required Checkbox | - | Field description | Not required Checkbox | - | Options | Not required Checkbox Option 1 | - And I add a "Coordinates" field to "Test database name" database and I fill the form with: - | Field name | Not required Coordinates | - | Field description | Not required Coordinates | - And I add a "Dropdown list" field to "Test database name" database and I fill the form with: - | Field name | Not required Menu | - | Field description | Not required Menu | - | Options | Option 1 | - And I add a "Number" field to "Test database name" database and I fill the form with: - | Field name | Not required Number | - | Field description | Not required Number | - And I add a "Radio button" field to "Test database name" database and I fill the form with: - | Field name | Not required Radio | - | Field description | Not required Radio | - | Options | Not required Radio Option 1 | - And I add a "Short text" field to "Test database name" database and I fill the form with: - | Field name | Not required Text input | - | Field description | Not required Text input | - And I add a "Text area" field to "Test database name" database and I fill the form with: - | Field name | Not required Text area | - | Field description | Not required Text area | - And I add a "URL" field to "Test database name" database and I fill the form with: - | Field name | Not required URL | - | Field description | Not required URL | - And I add a "Multi-select list" field to "Test database name" database and I fill the form with: - | Field name | Not required Multimenu | - | Field description | Not required Multimenu | - | Options | Option 1 | - And I navigate to "Templates" in current page administration - And I log out + And the following "mod_data > fields" exist: + | database | type | name | required | description | param1 | + | data1 | text | Base Text input | 1 | Base Text input | | + | data1 | checkbox | Required Checkbox | 1 | Base Text input | Required Checkbox Option 1 | + | data1 | checkbox | Required Two-Option Checkbox | 1 | Required Two-Option Checkbox | RTOC Option 1\nRTOC Option 2 | + | data1 | latlong | Required Coordinates | 1 | Required Coordinates | | + | data1 | menu | Required Menu | 1 | Required Menu | Option 1 | + | data1 | number | Required Number | 1 | Required Number | | + | data1 | radiobutton | Required Radio | 1 | Required Radio | Required Radio Option 1 | + | data1 | text | Required Text input | 1 | Required Text input | | + | data1 | textarea | Required Text area | 1 | Required Text area | | + | data1 | url | Required URL | 1 | Required URL | | + | data1 | multimenu | Required Multimenu | 1 | Required Multimenu | Option 1 | + | data1 | multimenu | Required Two-Option Multimenu | 1 | Required Two-Option Multimenu | Option 1\nOption 2 | + | data1 | checkbox | Not required Checkbox | 0 | Not required Checkbox | Not required Checkbox Option 1 | + | data1 | latlong | Not required Coordinates | 0 | Not required Coordinates | | + | data1 | menu | Not required Menu | 0 | Not required Menu | Option 1 | + | data1 | number | Not required Number | 0 | Not required Number | | + | data1 | radiobutton | Not required Radio | 0 | Not required Radio | Not required Radio Option 1 | + | data1 | text | Not required Text input | 0 | Not required Text input | | + | data1 | textarea | Not required Text area | 0 | Not required Text area | | + | data1 | url | Not required URL | 0 | Not required URL | | + | data1 | multimenu | Not required Multimenu | 0 | Not required Multimenu | Option 1 | Scenario: Students receive errors for empty required fields but not for optional fields When I log in as "student1" diff --git a/mod/data/tests/behat/view_entries.feature b/mod/data/tests/behat/view_entries.feature index 7412309d03e..ecce57d3e50 100644 --- a/mod/data/tests/behat/view_entries.feature +++ b/mod/data/tests/behat/view_entries.feature @@ -29,13 +29,6 @@ Feature: Users can view and search database entries | data1 | text | Test field name | Test field description | | data1 | text | Test field 2 name | Test field 2 description | | data1 | url | Test field 3 name | Test field 3 description | - And the following "mod_data > templates" exist: - | database | name | - | data1 | singletemplate | - | data1 | listtemplate | - | data1 | addtemplate | - | data1 | asearchtemplate | - | data1 | rsstemplate | @javascript Scenario: Students can view, list and search entries diff --git a/mod/data/tests/generator/lib.php b/mod/data/tests/generator/lib.php index 94a18dd2ec8..1f5d28a7e86 100644 --- a/mod/data/tests/generator/lib.php +++ b/mod/data/tests/generator/lib.php @@ -120,6 +120,11 @@ class mod_data_generator extends testing_module_generator { $record['description'] = " This is testField - " . $this->databasefieldcount; } + if (isset($record['param1']) && !empty($record['param1'])) { + // Some fields have multiline entries. + $record['param1'] = str_replace('\n', "\n", $record['param1']); + } + if (!isset($record['param1'])) { if ($record['type'] == 'checkbox') { $record['param1'] = implode("\n", array('opt1', 'opt2', 'opt3', 'opt4')); @@ -178,8 +183,6 @@ class mod_data_generator extends testing_module_generator { $field = data_get_field($record, $data); $field->insert_field(); - data_generate_default_template($data, 'addtemplate', 0, false, true); - return $field; } diff --git a/mod/data/tests/template_test.php b/mod/data/tests/template_test.php index a02321ce760..787d3fe12e8 100644 --- a/mod/data/tests/template_test.php +++ b/mod/data/tests/template_test.php @@ -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, + ], + ]; + } } diff --git a/mod/data/view.php b/mod/data/view.php index f4aa3d21190..4a93fbc062c 100644 --- a/mod/data/view.php +++ b/mod/data/view.php @@ -241,7 +241,7 @@ echo $OUTPUT->header(); if (!$manager->has_fields()) { // It's a brand-new database. There are no fields. - $renderer = $PAGE->get_renderer('mod_data'); + $renderer = $manager->get_renderer(); echo $renderer->render_zero_state($manager); echo $OUTPUT->footer(); // Don't check the rest of the options. There is no field, there is nothing else to work with. @@ -387,7 +387,7 @@ if ($showactivity) { $hasrecords = !empty($records); if ($maxcount == 0) { - $renderer = $PAGE->get_renderer('mod_data'); + $renderer = $manager->get_renderer(); echo $renderer->render_empty_database($manager); echo $OUTPUT->footer(); // There is no entry, so makes no sense to check different views, pagination, etc. @@ -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,