diff --git a/blocks/private_files/block_private_files.php b/blocks/private_files/block_private_files.php index dcebb0121ab..a299c229bdd 100644 --- a/blocks/private_files/block_private_files.php +++ b/blocks/private_files/block_private_files.php @@ -61,8 +61,14 @@ class block_private_files extends block_base { $this->content->text = $renderer->private_files_tree(); if (has_capability('moodle/user:manageownfiles', $this->context)) { $this->content->footer = html_writer::link( - new moodle_url('/user/files.php', array('returnurl' => $this->page->url->out())), - get_string('privatefilesmanage') . '...'); + new moodle_url('/user/files.php'), + get_string('privatefilesmanage') . '...', + ['data-action' => 'manageprivatefiles']); + $this->page->requires->js_call_amd( + 'core_user/private_files', + 'initModal', + ['[data-action=manageprivatefiles]', \core_user\form\private_files::class] + ); } } diff --git a/files/tests/behat/license_help_modal.feature b/files/tests/behat/license_help_modal.feature index 3aa3de0e2ac..38cf2e24ce2 100644 --- a/files/tests/behat/license_help_modal.feature +++ b/files/tests/behat/license_help_modal.feature @@ -17,10 +17,10 @@ Feature: View licence links @javascript @_file_upload Scenario: Altering a file should display licence list modal Given I log in as "admin" - And I follow "Manage private files..." + And I follow "Private files" And I upload "lib/tests/fixtures/empty.txt" file to "Files" filemanager And I press "Save changes" - And I follow "Manage private files..." + And I follow "Private files" And I click on "empty.txt" "link" And I click on "Help with Choose licence" "icon" Then I should see "Follow these links for further information on the available licence options:" diff --git a/lib/behat/classes/partial_named_selector.php b/lib/behat/classes/partial_named_selector.php index 3689733441e..c6677728888 100644 --- a/lib/behat/classes/partial_named_selector.php +++ b/lib/behat/classes/partial_named_selector.php @@ -144,6 +144,7 @@ XPATH XPATH , 'dialogue' => <<.\n\n/**\n * Module to handle AJAX interactions with user private files\n *\n * @module core_user/private_files\n * @copyright 2020 Marina Glancy\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport DynamicForm from 'core_form/dynamicform';\nimport ModalForm from 'core_form/modalform';\nimport {get_string as getString} from 'core/str';\nimport {add as addToast} from 'core/toast';\n\n/**\n * Initialize private files form as AJAX form\n *\n * @param {String} containerSelector\n * @param {String} formClass\n */\nexport const initDynamicForm = (containerSelector, formClass) => {\n const form = new DynamicForm(document.querySelector(containerSelector), formClass);\n // When form is saved, refresh it to remove validation errors, if any:\n form.addEventListener(form.events.FORM_SUBMITTED, () => {\n form.load();\n getString('changessaved')\n .then(addToast)\n .catch(null);\n });\n};\n\n/**\n * Initialize private files form as Modal form\n *\n * @param {String} elementSelector\n * @param {String} formClass\n */\nexport const initModal = (elementSelector, formClass) => {\n document.querySelector(elementSelector).addEventListener('click', function(e) {\n e.preventDefault();\n const form = new ModalForm({\n formClass,\n args: {nosubmit: true},\n modalConfig: {title: getString('privatefilesmanage')},\n returnFocus: e.target,\n });\n form.addEventListener(form.events.FORM_SUBMITTED, () => window.location.reload());\n form.show();\n });\n};\n"],"file":"private_files.min.js"} \ No newline at end of file diff --git a/user/amd/src/private_files.js b/user/amd/src/private_files.js new file mode 100644 index 00000000000..adde6cb9391 --- /dev/null +++ b/user/amd/src/private_files.js @@ -0,0 +1,63 @@ +// 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 user private files + * + * @module core_user/private_files + * @copyright 2020 Marina Glancy + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +import DynamicForm from 'core_form/dynamicform'; +import ModalForm from 'core_form/modalform'; +import {get_string as getString} from 'core/str'; +import {add as addToast} from 'core/toast'; + +/** + * Initialize private files form as AJAX form + * + * @param {String} containerSelector + * @param {String} formClass + */ +export const initDynamicForm = (containerSelector, formClass) => { + const form = new DynamicForm(document.querySelector(containerSelector), formClass); + // When form is saved, refresh it to remove validation errors, if any: + form.addEventListener(form.events.FORM_SUBMITTED, () => { + form.load(); + getString('changessaved') + .then(addToast) + .catch(null); + }); +}; + +/** + * Initialize private files form as Modal form + * + * @param {String} elementSelector + * @param {String} formClass + */ +export const initModal = (elementSelector, formClass) => { + document.querySelector(elementSelector).addEventListener('click', function(e) { + e.preventDefault(); + const form = new ModalForm({ + formClass, + args: {nosubmit: true}, + modalConfig: {title: getString('privatefilesmanage')}, + returnFocus: e.target, + }); + form.addEventListener(form.events.FORM_SUBMITTED, () => window.location.reload()); + form.show(); + }); +}; diff --git a/user/classes/form/private_files.php b/user/classes/form/private_files.php new file mode 100644 index 00000000000..2522f78449d --- /dev/null +++ b/user/classes/form/private_files.php @@ -0,0 +1,190 @@ +. + +namespace core_user\form; + +use html_writer; +use moodle_url; + +/** + * Manage user private area files form + * + * @package core_user + * @copyright 2010 Petr Skoda (http://skodak.org) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class private_files extends \core_form\dynamic_form { + + /** + * Add elements to this form. + */ + public function definition() { + global $OUTPUT; + $mform = $this->_form; + $options = $this->get_options(); + + // Show file area space usage. + $maxareabytes = $options['areamaxbytes']; + if ($maxareabytes != FILE_AREA_MAX_BYTES_UNLIMITED) { + $fileareainfo = file_get_file_area_info($this->get_context_for_dynamic_submission()->id, 'user', 'private'); + // Display message only if we have files. + if ($fileareainfo['filecount']) { + $a = (object) [ + 'used' => display_size($fileareainfo['filesize_without_references']), + 'total' => display_size($maxareabytes) + ]; + $quotamsg = get_string('quotausage', 'moodle', $a); + $notification = new \core\output\notification($quotamsg, \core\output\notification::NOTIFY_INFO); + $mform->addElement('static', 'areabytes', '', $OUTPUT->render($notification)); + } + } + + $mform->addElement('filemanager', 'files_filemanager', get_string('files'), null, $options); + if ($link = $this->get_emaillink()) { + $emaillink = html_writer::link(new moodle_url('mailto:' . $link), $link); + $mform->addElement('static', 'emailaddress', '', + get_string('emailtoprivatefiles', 'moodle', $emaillink)); + } + $mform->setType('returnurl', PARAM_LOCALURL); + + if (!$this->optional_param('nosubmit', false, PARAM_BOOL)) { + $this->add_action_buttons(false, get_string('savechanges')); + } + } + + /** + * Validate incoming data. + * + * @param array $data + * @param array $files + * @return array + */ + public function validation($data, $files) { + $errors = array(); + $draftitemid = $data['files_filemanager']; + $options = $this->get_options(); + if (file_is_draft_area_limit_reached($draftitemid, $options['areamaxbytes'])) { + $errors['files_filemanager'] = get_string('userquotalimit', 'error'); + } + + return $errors; + } + + /** + * Link to email private files + * + * @return string|null + * @throws \coding_exception + */ + protected function get_emaillink() { + global $USER; + + // Attempt to generate an inbound message address to support e-mail to private files. + $generator = new \core\message\inbound\address_manager(); + $generator->set_handler('\core\message\inbound\private_files_handler'); + $generator->set_data(-1); + return $generator->generate($USER->id); + } + + /** + * 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/user:manageownfiles', $this->get_context_for_dynamic_submission()); + } + + /** + * 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 { + global $USER; + return \context_user::instance($USER->id); + } + + /** + * 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; + } + + return ['subdirs' => 1, 'maxbytes' => $maxbytes, 'maxfiles' => -1, 'accepted_types' => '*', + '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() { + file_postupdate_standard_filemanager($this->get_data(), 'files', + $this->get_options(), $this->get_context_for_dynamic_submission(), 'user', 'private', 0); + 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 = new \stdClass(); + file_prepare_standard_filemanager($data, 'files', $this->get_options(), + $this->get_context_for_dynamic_submission(), 'user', 'private', 0); + $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 { + return new moodle_url('/user/files.php'); + } +} \ No newline at end of file diff --git a/user/files.php b/user/files.php index dadaf619d94..1c645cd661a 100644 --- a/user/files.php +++ b/user/files.php @@ -24,25 +24,16 @@ */ require('../config.php'); -require_once("$CFG->dirroot/user/files_form.php"); -require_once("$CFG->dirroot/repository/lib.php"); require_login(); if (isguestuser()) { die(); } -$returnurl = optional_param('returnurl', '', PARAM_LOCALURL); - -if (empty($returnurl)) { - $returnurl = new moodle_url('/user/files.php'); -} - $context = context_user::instance($USER->id); require_capability('moodle/user:manageownfiles', $context); $title = get_string('privatefiles'); -$struser = get_string('user'); $PAGE->set_url('/user/files.php'); $PAGE->set_context($context); @@ -51,52 +42,16 @@ $PAGE->set_heading(fullname($USER)); $PAGE->set_pagelayout('standard'); $PAGE->set_pagetype('user-files'); -$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; -} - -$data = new stdClass(); -$data->returnurl = $returnurl; -$options = array('subdirs' => 1, 'maxbytes' => $maxbytes, 'maxfiles' => -1, 'accepted_types' => '*', - 'areamaxbytes' => $maxareabytes); -file_prepare_standard_filemanager($data, 'files', $options, $context, 'user', 'private', 0); - -// Attempt to generate an inbound message address to support e-mail to private files. -$generator = new \core\message\inbound\address_manager(); -$generator->set_handler('\core\message\inbound\private_files_handler'); -$generator->set_data(-1); -$data->emaillink = $generator->generate($USER->id); - -$mform = new user_files_form(null, array('data' => $data, 'options' => $options)); - -if ($mform->is_cancelled()) { - redirect($returnurl); -} else if ($formdata = $mform->get_data()) { - $formdata = file_postupdate_standard_filemanager($formdata, 'files', $options, $context, 'user', 'private', 0); - redirect($returnurl); -} - echo $OUTPUT->header(); echo $OUTPUT->box_start('generalbox'); -// Show file area space usage. -if ($maxareabytes != FILE_AREA_MAX_BYTES_UNLIMITED) { - $fileareainfo = file_get_file_area_info($context->id, 'user', 'private'); - // Display message only if we have files. - if ($fileareainfo['filecount']) { - $a = (object) [ - 'used' => display_size($fileareainfo['filesize_without_references']), - 'total' => display_size($maxareabytes) - ]; - $quotamsg = get_string('quotausage', 'moodle', $a); - $notification = new \core\output\notification($quotamsg, \core\output\notification::NOTIFY_INFO); - echo $OUTPUT->render($notification); - } -} +echo html_writer::start_div('', ['id' => 'userfilesform']); +$form = new \core_user\form\private_files(); +$form->set_data_for_dynamic_submission(); +$form->display(); +echo html_writer::end_div(); +$PAGE->requires->js_call_amd('core_user/private_files', 'initDynamicForm', + ['#userfilesform', \core_user\form\private_files::class]); -$mform->display(); echo $OUTPUT->box_end(); echo $OUTPUT->footer(); diff --git a/user/files_form.php b/user/files_form.php deleted file mode 100644 index 709b3c9a86d..00000000000 --- a/user/files_form.php +++ /dev/null @@ -1,76 +0,0 @@ -. - -/** - * minimalistic edit form - * - * @package core_user - * @category files - * @copyright 2010 Petr Skoda (http://skodak.org) - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -defined('MOODLE_INTERNAL') || die(); - -require_once("$CFG->libdir/formslib.php"); - -/** - * Class user_files_form - * @copyright 2010 Petr Skoda (http://skodak.org) - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class user_files_form extends moodleform { - - /** - * Add elements to this form. - */ - public function definition() { - $mform = $this->_form; - - $data = $this->_customdata['data']; - $options = $this->_customdata['options']; - - $mform->addElement('filemanager', 'files_filemanager', get_string('files'), null, $options); - $mform->addElement('hidden', 'returnurl', $data->returnurl); - if (isset($data->emaillink)) { - $emaillink = html_writer::link(new moodle_url('mailto:' . $data->emaillink), $data->emaillink); - $mform->addElement('static', 'emailaddress', '', - get_string('emailtoprivatefiles', 'moodle', $emaillink)); - } - $mform->setType('returnurl', PARAM_LOCALURL); - - $this->add_action_buttons(true, get_string('savechanges')); - - $this->set_data($data); - } - - /** - * Validate incoming data. - * - * @param array $data - * @param array $files - * @return array - */ - public function validation($data, $files) { - $errors = array(); - $draftitemid = $data['files_filemanager']; - if (file_is_draft_area_limit_reached($draftitemid, $this->_customdata['options']['areamaxbytes'])) { - $errors['files_filemanager'] = get_string('userquotalimit', 'error'); - } - - return $errors; - } -}