diff --git a/contentbank/amd/build/upload.min.js b/contentbank/amd/build/upload.min.js new file mode 100644 index 00000000000..9df927d217c --- /dev/null +++ b/contentbank/amd/build/upload.min.js @@ -0,0 +1,2 @@ +define ("core_contentbank/upload",["exports","core_form/modalform","core/str"],function(a,b,c){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.initModal=void 0;b=function(a){return a&&a.__esModule?a:{default:a}}(b);a.initModal=function initModal(a,d,f,g){var h=document.querySelector(a);h.addEventListener("click",function(a){a.preventDefault();var e=new b.default({formClass:d,args:{contextid:f,id:g},modalConfig:{title:(0,c.get_string)("upload","contentbank")},returnFocus:a.target});e.addEventListener(e.events.FORM_SUBMITTED,function(a){document.location=a.detail.returnurl});e.show()})}}); +//# sourceMappingURL=upload.min.js.map diff --git a/contentbank/amd/build/upload.min.js.map b/contentbank/amd/build/upload.min.js.map new file mode 100644 index 00000000000..c14d2d706d4 --- /dev/null +++ b/contentbank/amd/build/upload.min.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../src/upload.js"],"names":["initModal","elementSelector","formClass","contextId","contentId","element","document","querySelector","addEventListener","e","preventDefault","form","ModalForm","args","contextid","id","modalConfig","title","returnFocus","target","events","FORM_SUBMITTED","event","location","detail","returnurl","show"],"mappings":"gLAsBA,uD,YAWyB,QAAZA,CAAAA,SAAY,CAACC,CAAD,CAAkBC,CAAlB,CAA6BC,CAA7B,CAAwCC,CAAxC,CAAsD,CAC3E,GAAMC,CAAAA,CAAO,CAAGC,QAAQ,CAACC,aAAT,CAAuBN,CAAvB,CAAhB,CACAI,CAAO,CAACG,gBAAR,CAAyB,OAAzB,CAAkC,SAASC,CAAT,CAAY,CAC1CA,CAAC,CAACC,cAAF,GACA,GAAMC,CAAAA,CAAI,CAAG,GAAIC,UAAJ,CAAc,CACvBV,SAAS,CAATA,CADuB,CAEvBW,IAAI,CAAE,CACFC,SAAS,CAAEX,CADT,CAEFY,EAAE,CAAEX,CAFF,CAFiB,CAMvBY,WAAW,CAAE,CAACC,KAAK,CAAE,iBAAU,QAAV,CAAoB,aAApB,CAAR,CANU,CAOvBC,WAAW,CAAET,CAAC,CAACU,MAPQ,CAAd,CAAb,CASAR,CAAI,CAACH,gBAAL,CAAsBG,CAAI,CAACS,MAAL,CAAYC,cAAlC,CAAkD,SAACC,CAAD,CAAW,CACzDhB,QAAQ,CAACiB,QAAT,CAAoBD,CAAK,CAACE,MAAN,CAAaC,SACpC,CAFD,EAGAd,CAAI,CAACe,IAAL,EACH,CAfD,CAgBH,C","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 * Module to handle AJAX interactions with content bank upload files.\n *\n * @module core_contentbank/upload\n * @copyright 2021 Sara Arjona \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport ModalForm from 'core_form/modalform';\nimport {get_string as getString} from 'core/str';\n\n/**\n * Initialize upload files to the content bank form as Modal form.\n *\n * @param {String} elementSelector\n * @param {String} formClass\n * @param {Integer} contextId\n * @param {Integer} contentId\n */\nexport const initModal = (elementSelector, formClass, contextId, contentId) => {\n const element = document.querySelector(elementSelector);\n element.addEventListener('click', function(e) {\n e.preventDefault();\n const form = new ModalForm({\n formClass,\n args: {\n contextid: contextId,\n id: contentId,\n },\n modalConfig: {title: getString('upload', 'contentbank')},\n returnFocus: e.target,\n });\n form.addEventListener(form.events.FORM_SUBMITTED, (event) => {\n document.location = event.detail.returnurl;\n });\n form.show();\n });\n};\n"],"file":"upload.min.js"} \ No newline at end of file diff --git a/contentbank/amd/src/upload.js b/contentbank/amd/src/upload.js new file mode 100644 index 00000000000..fb1f7c8b241 --- /dev/null +++ b/contentbank/amd/src/upload.js @@ -0,0 +1,52 @@ +// 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 . + +/** + * Module to handle AJAX interactions with content bank upload files. + * + * @module core_contentbank/upload + * @copyright 2021 Sara Arjona + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +import ModalForm from 'core_form/modalform'; +import {get_string as getString} from 'core/str'; + +/** + * Initialize upload files to the content bank form as Modal form. + * + * @param {String} elementSelector + * @param {String} formClass + * @param {Integer} contextId + * @param {Integer} contentId + */ +export const initModal = (elementSelector, formClass, contextId, contentId) => { + const element = document.querySelector(elementSelector); + element.addEventListener('click', function(e) { + e.preventDefault(); + const form = new ModalForm({ + formClass, + args: { + contextid: contextId, + id: contentId, + }, + modalConfig: {title: getString('upload', 'contentbank')}, + returnFocus: e.target, + }); + form.addEventListener(form.events.FORM_SUBMITTED, (event) => { + document.location = event.detail.returnurl; + }); + form.show(); + }); +}; diff --git a/contentbank/classes/form/upload_files.php b/contentbank/classes/form/upload_files.php new file mode 100644 index 00000000000..91f8d3a29ca --- /dev/null +++ b/contentbank/classes/form/upload_files.php @@ -0,0 +1,209 @@ +. + +namespace core_contentbank\form; + +/** + * Upload files to content bank form + * + * @package core_contentbank + * @copyright 2020 Amaia Anabitarte + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class upload_files extends \core_form\dynamic_form { + + /** + * Add elements to this form. + */ + public function definition() { + $mform = $this->_form; + + $mform->addElement('hidden', 'contextid'); + $mform->setType('contextid', PARAM_INT); + + $mform->addElement('hidden', 'id'); + $mform->setType('id', PARAM_INT); + + $mform->addElement('filepicker', 'file', get_string('file', 'core_contentbank'), null, $this->get_options()); + $mform->addHelpButton('file', 'file', 'core_contentbank'); + $mform->addRule('file', null, 'required'); + } + + /** + * Validate incoming data. + * + * @param array $data + * @param array $files + * @return array + */ + public function validation($data, $files) { + $errors = array(); + $draftitemid = $data['file']; + $options = $this->get_options(); + if (file_is_draft_area_limit_reached($draftitemid, $options['areamaxbytes'])) { + $errors['file'] = get_string('userquotalimit', 'error'); + } + return $errors; + } + + /** + * Check if current user has access to this form, otherwise throw exception + * + * Sometimes permission check may depend on the action and/or id of the entity. + * If necessary, form data is available in $this->_ajaxformdata or + * by calling $this->optional_param() + */ + protected function check_access_for_dynamic_submission(): void { + require_capability('moodle/contentbank:upload', $this->get_context_for_dynamic_submission()); + + // Check the context used by the content bank is allowed. + $cb = new \core_contentbank\contentbank(); + if (!$cb->is_context_allowed($this->get_context_for_dynamic_submission())) { + throw new \moodle_exception('contextnotallowed', 'core_contentbank'); + } + + // If $id is defined, the file content will be replaced (instead of uploading a new one). + // Check that the user has the right permissions to replace this content file. + $id = $this->optional_param('id', null, PARAM_INT); + if ($id) { + $content = $cb->get_content_from_id($id); + $contenttype = $content->get_content_type_instance(); + if (!$contenttype->can_manage($content) || !$contenttype->can_upload()) { + throw new \moodle_exception('nopermissions', 'error', '', null, get_string('replacecontent', 'contentbank')); + } + } + } + + /** + * Returns form context + * + * If context depends on the form data, it is available in $this->_ajaxformdata or + * by calling $this->optional_param() + * + * @return \context + */ + protected function get_context_for_dynamic_submission(): \context { + $contextid = $this->optional_param('contextid', null, PARAM_INT); + return \context::instance_by_id($contextid, MUST_EXIST); + } + + /** + * File upload options + * + * @return array + * @throws \coding_exception + */ + protected function get_options(): array { + global $CFG; + + $maxbytes = $CFG->userquota; + $maxareabytes = $CFG->userquota; + if (has_capability('moodle/user:ignoreuserquota', $this->get_context_for_dynamic_submission())) { + $maxbytes = USER_CAN_IGNORE_FILE_SIZE_LIMITS; + $maxareabytes = FILE_AREA_MAX_BYTES_UNLIMITED; + } + + $cb = new \core_contentbank\contentbank(); + $id = $this->optional_param('id', null, PARAM_INT); + if ($id) { + $content = $cb->get_content_from_id($id); + $contenttype = $content->get_content_type_instance(); + $extensions = $contenttype->get_manageable_extensions(); + $acceptedtypes = implode(',', $extensions); + } else { + $acceptedtypes = $cb->get_supported_extensions_as_string($this->get_context_for_dynamic_submission()); + } + + return ['subdirs' => 1, 'maxbytes' => $maxbytes, 'maxfiles' => -1, 'accepted_types' => $acceptedtypes, + 'areamaxbytes' => $maxareabytes]; + } + + /** + * Process the form submission, used if form was submitted via AJAX + * + * This method can return scalar values or arrays that can be json-encoded, they will be passed to the caller JS. + * + * Submission data can be accessed as: $this->get_data() + * + * @return mixed + */ + public function process_dynamic_submission() { + global $USER; + + // Get the file and create the content based on it. + $usercontext = \context_user::instance($USER->id); + $fs = get_file_storage(); + $files = $fs->get_area_files($usercontext->id, 'user', 'draft', $this->get_data()->file, 'itemid, filepath, + filename', false); + if (!empty($files)) { + $file = reset($files); + $cb = new \core_contentbank\contentbank(); + if ($this->get_data()->id) { + $content = $cb->get_content_from_id($this->get_data()->id); + $contenttype = $content->get_content_type_instance(); + $content = $contenttype->replace_content($file, $content); + } else { + $content = $cb->create_content_from_file($this->get_context_for_dynamic_submission(), $USER->id, $file); + } + $params = ['id' => $content->get_id(), 'contextid' => $this->get_context_for_dynamic_submission()->id]; + $viewurl = new \moodle_url('/contentbank/view.php', $params); + return ['returnurl' => $viewurl->out(false)]; + } + + return null; + } + + /** + * Load in existing data as form defaults + * + * Can be overridden to retrieve existing values from db by entity id and also + * to preprocess editor and filemanager elements + * + * Example: + * $this->set_data(get_entity($this->_ajaxformdata['id'])); + */ + public function set_data_for_dynamic_submission(): void { + $data = (object)[ + 'contextid' => $this->optional_param('contextid', null, PARAM_INT), + 'id' => $this->optional_param('id', null, PARAM_INT), + ]; + $this->set_data($data); + } + + /** + * Returns url to set in $PAGE->set_url() when form is being rendered or submitted via AJAX + * + * This is used in the form elements sensitive to the page url, such as Atto autosave in 'editor' + * + * If the form has arguments (such as 'id' of the element being edited), the URL should + * also have respective argument. + * + * @return \moodle_url + */ + protected function get_page_url_for_dynamic_submission(): \moodle_url { + $params = ['contextid' => $this->get_context_for_dynamic_submission()->id]; + + $id = $this->optional_param('id', null, PARAM_INT); + if ($id) { + $url = '/contentbank/view.php'; + $params['id'] = $id; + } else { + $url = '/contentbank/index.php'; + } + + return new \moodle_url($url, $params); + } +} diff --git a/contentbank/files_form.php b/contentbank/files_form.php deleted file mode 100644 index 6a001f05675..00000000000 --- a/contentbank/files_form.php +++ /dev/null @@ -1,78 +0,0 @@ -. - -/** - * Upload files to content bank form - * - * @package core_contentbank - * @copyright 2020 Amaia Anabitarte - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -defined('MOODLE_INTERNAL') || die(); - -require_once("$CFG->libdir/formslib.php"); - -/** - * Class contentbank_files_form - * - * @package core_contentbank - * @copyright 2020 Amaia Anabitarte - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class contentbank_files_form extends moodleform { - - /** - * Add elements to this form. - */ - public function definition() { - $mform = $this->_form; - - $mform->addElement('hidden', 'contextid', $this->_customdata['contextid']); - $mform->setType('contextid', PARAM_INT); - - if (!empty($this->_customdata['id'])) { - $mform->addElement('hidden', 'id', $this->_customdata['id']); - $mform->setType('id', PARAM_INT); - } - - $options = $this->_customdata['options']; - $mform->addElement('filepicker', 'file', get_string('file', 'core_contentbank'), null, $options); - $mform->addHelpButton('file', 'file', 'core_contentbank'); - $mform->addRule('file', null, 'required'); - - $this->add_action_buttons(true, get_string('savechanges')); - - $data = $this->_customdata['data']; - $this->set_data($data); - } - - /** - * Validate incoming data. - * - * @param array $data - * @param array $files - * @return array - */ - public function validation($data, $files) { - $errors = array(); - $draftitemid = $data['file']; - if (file_is_draft_area_limit_reached($draftitemid, $this->_customdata['options']['areamaxbytes'])) { - $errors['file'] = get_string('userquotalimit', 'error'); - } - return $errors; - } -} diff --git a/contentbank/index.php b/contentbank/index.php index c4d22428d9f..7845f58ecd4 100644 --- a/contentbank/index.php +++ b/contentbank/index.php @@ -88,13 +88,18 @@ if (has_capability('moodle/contentbank:upload', $context)) { // Don' show upload button if there's no plugin to support any file extension. $accepted = $cb->get_supported_extensions_as_string($context); if (!empty($accepted)) { - $importurl = new moodle_url('/contentbank/upload.php', ['contextid' => $contextid]); + $importurl = new moodle_url('/contentbank/index.php', ['contextid' => $contextid]); $toolbar[] = [ 'name' => get_string('upload', 'contentbank'), - 'link' => $importurl, + 'link' => $importurl->out(false), 'icon' => 'i/upload', 'action' => 'upload' ]; + $PAGE->requires->js_call_amd( + 'core_contentbank/upload', + 'initModal', + ['[data-action=upload]', \core_contentbank\form\upload_files::class, $contextid] + ); } } diff --git a/contentbank/templates/bankcontent/toolbar.mustache b/contentbank/templates/bankcontent/toolbar.mustache index 4d590ce5061..d1fbb7db100 100644 --- a/contentbank/templates/bankcontent/toolbar.mustache +++ b/contentbank/templates/bankcontent/toolbar.mustache @@ -59,7 +59,7 @@ {{>core_contentbank/bankcontent/toolbar_dropdown}} {{/dropdown}} {{^dropdown}} - + {{#pix}} {{{ icon }}} {{/pix}} {{{ name }}} {{/dropdown}} diff --git a/contentbank/upgrade.txt b/contentbank/upgrade.txt index 8eca72b6fa0..aa7d8edf3f7 100644 --- a/contentbank/upgrade.txt +++ b/contentbank/upgrade.txt @@ -4,3 +4,5 @@ information provided here is intended especially for developers. === 3.11 === * Added "get_uses()" method to content class to return places where a content is used. * Added set_visibility()/get_visibility() methods to let users decide if their content should be listed in the content bank. +* The contentbank/upload.php page for displaying the upload files form has been removed. The form for uploading/replacing +files now is displayed in a modal. diff --git a/contentbank/upload.php b/contentbank/upload.php deleted file mode 100644 index 81a7870d044..00000000000 --- a/contentbank/upload.php +++ /dev/null @@ -1,125 +0,0 @@ -. - -/** - * Upload a file to content bank. - * - * @package core_contentbank - * @copyright 2020 Amaia Anabitarte - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -require('../config.php'); -require_once("$CFG->dirroot/contentbank/files_form.php"); - -use core\output\notification; - -require_login(); - -$contextid = optional_param('contextid', \context_system::instance()->id, PARAM_INT); -$context = context::instance_by_id($contextid, MUST_EXIST); - -$cb = new \core_contentbank\contentbank(); -if (!$cb->is_context_allowed($context)) { - print_error('contextnotallowed', 'core_contentbank'); -} - -require_capability('moodle/contentbank:upload', $context); - -$id = optional_param('id', null, PARAM_INT); -if ($id) { - $content = $cb->get_content_from_id($id); - $contenttype = $content->get_content_type_instance(); - if (!$contenttype->can_manage($content) || !$contenttype->can_upload()) { - print_error('nopermissions', 'error', $returnurl, get_string('replacecontent', 'contentbank')); - } -} - -$title = get_string('contentbank'); -\core_contentbank\helper::get_page_ready($context, $title, true); -if ($PAGE->course) { - require_login($PAGE->course->id); -} -$returnurl = new \moodle_url('/contentbank/index.php', ['contextid' => $contextid]); - -$PAGE->set_url('/contentbank/upload.php'); -$PAGE->set_context($context); -$PAGE->navbar->add(get_string('upload', 'contentbank')); -$PAGE->set_title($title); -$PAGE->set_heading($title); -$PAGE->set_pagetype('contentbank'); - -$maxbytes = $CFG->userquota; -$maxareabytes = $CFG->userquota; -if (has_capability('moodle/user:ignoreuserquota', $context)) { - $maxbytes = USER_CAN_IGNORE_FILE_SIZE_LIMITS; - $maxareabytes = FILE_AREA_MAX_BYTES_UNLIMITED; -} - -if ($id) { - $extensions = $contenttype->get_manageable_extensions(); - $accepted = implode(',', $extensions); -} else { - $accepted = $cb->get_supported_extensions_as_string($context); -} - -$data = new stdClass(); -$options = array( - 'subdirs' => 1, - 'maxbytes' => $maxbytes, - 'maxfiles' => -1, - 'accepted_types' => $accepted, - 'areamaxbytes' => $maxareabytes -); -file_prepare_standard_filemanager($data, 'files', $options, $context, 'contentbank', 'public', 0); - -$mform = new contentbank_files_form(null, ['contextid' => $contextid, 'data' => $data, 'options' => $options, 'id' => $id]); - -$error = ''; - -if ($mform->is_cancelled()) { - redirect($returnurl); -} else if ($formdata = $mform->get_data()) { - require_sesskey(); - // Get the file and create the content based on it. - $usercontext = \context_user::instance($USER->id); - $fs = get_file_storage(); - $files = $fs->get_area_files($usercontext->id, 'user', 'draft', $formdata->file, 'itemid, filepath, filename', false); - if (!empty($files)) { - $file = reset($files); - if ($id) { - $content = $contenttype->replace_content($file, $content); - } else { - $content = $cb->create_content_from_file($context, $USER->id, $file); - } - $viewurl = new \moodle_url('/contentbank/view.php', ['id' => $content->get_id(), 'contextid' => $contextid]); - redirect($viewurl); - } else { - $error = get_string('errornofile', 'contentbank'); - } -} - -echo $OUTPUT->header(); -echo $OUTPUT->box_start('generalbox'); - -if (!empty($error)) { - echo $OUTPUT->notification($error, notification::NOTIFY_ERROR); -} - -$mform->display(); - -echo $OUTPUT->box_end(); -echo $OUTPUT->footer(); diff --git a/contentbank/view.php b/contentbank/view.php index 8258fb2db09..48519f4fc21 100644 --- a/contentbank/view.php +++ b/contentbank/view.php @@ -123,11 +123,17 @@ if ($contenttype->can_manage($content)) { if ($contenttype->can_upload()) { $actionmenu->add_secondary_action(new action_menu_link( - new moodle_url('/contentbank/upload.php', ['contextid' => $context->id, 'id' => $content->get_id()]), + new moodle_url('/contentbank/view.php', ['contextid' => $context->id, 'id' => $content->get_id()]), new pix_icon('i/upload', get_string('upload')), get_string('replacecontent', 'contentbank'), - false + false, + ['data-action' => 'upload'] )); + $PAGE->requires->js_call_amd( + 'core_contentbank/upload', + 'initModal', + ['[data-action=upload]', \core_contentbank\form\upload_files::class, $context->id, $content->get_id()] + ); } } if ($contenttype->can_download($content)) {