diff --git a/classes/local/forms/formbase.php b/classes/local/forms/formbase.php index a06ca13..eb348c8 100644 --- a/classes/local/forms/formbase.php +++ b/classes/local/forms/formbase.php @@ -9,20 +9,37 @@ abstract class formbase extends moodleform { /** * Create the form while providing the correct defaults for our type of forms * @param array $ajaxformdata Provide submitted form data through Ajax here - * + * @throws \moodle_exception if parameters are invalid or access is denied for a specific reason. */ - public function __construct($ajaxformdata=null) { - parent::__construct(null, null, 'post', '', null, true, $ajaxformdata); + public function __construct($params, $ajaxformdata=null) { + $customdata = static::init_customdata($params); + if (static::check_security($customdata)) { + + parent::__construct(null, (array)$customdata, 'post', '', null, true, $ajaxformdata); + // Initialize the initial data based on the parameter validation + $this->set_data($this->init_formdata((object)$this->_customdata)); + } else { + throw new \moodle_exception('accessexception', 'core'); + } } - + + /** + * Generate custom data from parameters + * Also validate parameters and access permissions here + * + * @param object $params The parameters for form initialization + * @return object Form data based on parameters + */ + abstract public static function init_customdata(object $params); + /** * Generate form data from parameters * Also validate parameters and access permissions here * - * @param object $params The parameters for form initialization + * @param object $customdata The parameters for form initialization * @return array Form data based on parameters */ - abstract public static function init_data(object $params); + abstract public function init_formdata(object $customdata); /** * Validate security access for this form based on the provided parameters @@ -32,14 +49,28 @@ abstract class formbase extends moodleform { * @return bool True if security validation passes. * @throws \moodle_exception if access denied for a specific reason. */ - abstract public static function check_security(object $params); + abstract public static function check_security(object $customdata); + + /** + * Process the submission and perform necessary actions + * @param object $entry The processed form data + * @return bool True if submission successful + * @throws \moodle_exception if an error must be given for a specific reason. + */ + abstract protected function process_submitted_data(object $entry); /** * Process the submission and perform necessary actions * @return bool True if submission successful * @throws \moodle_exception if an error must be given for a specific reason. */ - abstract public function process_submission(); + public function process_submission() { + $data = $this->get_data(); + if($data) { + $this->process_submitted_data($data); + } + } + } \ No newline at end of file diff --git a/classes/local/forms/studyplan_editform.php b/classes/local/forms/studyplan_editform.php index 9e06a96..4898d10 100644 --- a/classes/local/forms/studyplan_editform.php +++ b/classes/local/forms/studyplan_editform.php @@ -3,6 +3,8 @@ namespace local_treestudyplan\local\forms; use local_treestudyplan\studyplan; use local_treestudyplan\local\helpers\webservicehelper; +use stdClass; + /** * Moodleform class for the studyplan editor. A Moodleform is used here to facilitate a rich editor * in the studyplan description @@ -13,53 +15,98 @@ class studyplan_editform extends formbase { * @var string */ const CAP_EDIT = "local/treestudyplan:editstudyplan"; + /** - * Validate security access for this form based on the provided parameters - * Return true if validation passes, false or throw an exception if it does not. + * Translate parameters into customdata. * * @param object $params The parameters for form initialization + * @return array Form data based on parameters + */ + public static function init_customdata(object $params) { + $customdata = new stdClass; + $customdata->plan = studyplan::find_by_id($params->studyplanid); + $customdata->context = $customdata->plan->context(); + $customdata->editoroptions = [ + 'trusttext' => true, + 'subdirs' => true, + 'maxfiles' => 20, + 'maxbytes' => 20*1024*1024, + 'context' => \context_system::instance(), + ]; + return $customdata; + } + + /** + * Validate security access for this form based on the customdata generated by init_customdata + * Return true if validation passes, false or throw an exception if it does not. + * + * @param object $customdata The customdata for this form * @return bool True if security validation passes. * @throws \moodle_exception if access denied for a specific reason. */ - public static function check_security(object $params) { - $plan = studyplan::find_by_id($params->studyplanid); - webservicehelper::require_capabilities(self::CAP_EDIT,$plan->context()); + public static function check_security(object $customdata) { + webservicehelper::require_capabilities(self::CAP_EDIT,$customdata->context); } /** * Generate form data from parameters * Also validate parameters and access permissions here * - * @param object $params The parameters for form initialization + * @param object $customdata The parameters for form initialization * @return array Form data based on parameters */ - public static function init_data(object $params) { - $plan = studyplan::find_by_id($params->studyplanid); - + public function init_formdata(object $customdata) { + global $DB; + /* Use direct database retrieval to avoid our abstractions causing trouble + with existing moodle code assumptions. + The form API does seem needlessly convoluted in it's use, but we need the editor... + */ + $entry = $DB->get_record(studyplan::TABLE, ['id' => $customdata->plan->id()]); + $entry = file_prepare_standard_editor( $entry, + 'description', + $customdata->editoroptions, + \context_system::instance(), + 'local_treestudyplan', + 'studyplan', + $entry->id); + return $entry; } - /** * Set up the form definition */ public function definition() { $mform = $this->_form; - + $customdata = (object)$this->_customdata; // Define the form - $mform->addElement('editor', 'activityeditor', - get_string('activityeditor', 'assign'), array('rows' => 10), array('maxfiles' => EDITOR_UNLIMITED_FILES, - 'noclean' => true, 'context' => $this->context, 'subdirs' => true)); - $mform->addHelpButton('activityeditor', 'activityeditor', 'assign'); - $mform->setType('activityeditor', PARAM_RAW); + $mform->addElement('editor', 'description_editor', + get_string('studyplan_description', 'local_treestudyplan'), + null, + $customdata->editoroptions); + $mform->setType('description_editor', PARAM_RAW); } /** - * Process the submission and perform necessary actions + * Process the submitted data and perform necessary actions + * @param object $entry The processed form data; + * @return bool True if submission successful * @throws \moodle_exception if an error must be given for a specific reason. */ - public function process_submission() - { + protected function process_submitted_data($entry) { + $customdata = (object)$this->_customdata; + // Process the provided files in the description editor + $entry = file_postupdate_standard_editor($entry, + 'description', + $customdata->editoroptions, + \context_system::instance(), + 'local_treestudyplan', + 'studyplan', + $entry->id); + // Use our own abstraction to update the record, so caches are maintained + $customdata->plan->edit(['description' => $entry->description, + 'descriptionformat' => $entry->descriptionformat]); + } diff --git a/classes/utilityservice.php b/classes/utilityservice.php index a58447c..ef54273 100644 --- a/classes/utilityservice.php +++ b/classes/utilityservice.php @@ -56,14 +56,8 @@ class utilityservice extends \external_api { $mformclassname = "\\local_treestudyplan\\local\\forms\\{$formname}"; $jsonparams = json_decode($params); - - if($mformclassname::security_validate($params)){ - $mform = new $mformclassname($ajaxformdata); - $mform->set_data($mformclassname::init_data($jsonparams)); - return $mform; - } else { - throw new \moodle_exception('accessexception', 'core'); - } + $mform = new $mformclassname( $jsonparams, $ajaxformdata); + return $mform; } /** diff --git a/lib.php b/lib.php index af74e5e..d2ff918 100644 --- a/lib.php +++ b/lib.php @@ -380,3 +380,76 @@ function local_treestudyplan_output_fragment_mod_edit_form($args) { return $mform->render(); } + +/** + * Serve the files from the myplugin file areas. + * + * @param stdClass $course the course object + * @param stdClass $cm the course module object + * @param stdClass $context the context + * @param string $filearea the name of the file area + * @param array $args extra arguments (itemid, path) + * @param bool $forcedownload whether or not force download + * @param array $options additional options affecting the file serving + * @return bool false if the file not found, just send the file otherwise and do not return anything + */ +function local_treestudyplan_pluginfile( + $course, + $cm, + $context, + string $filearea, + array $args, + bool $forcedownload, + array $options = [] +): bool { + global $DB,$USER; + + // Check the contextlevel is as expected - the studyplan plugin only uses system context for storing files. + // This avoids headaches when moving studyplans, while the security impact is minimal... + if ($context->contextlevel != CONTEXT_SYSTEM) { + return false; + } + + // Make sure the filearea is one of those used by the plugin. + if ($filearea == "studyplan") { + // The args is an array containing [itemid, path]. + // Fetch the itemid from the path. + $itemid = array_shift($args); + + $plan = studyplan::find_by_id($itemid); + $planctx = $plan->context(); + // Check if the current user has access to this studyplan + if (webservicehelper::has_capabilities(['',''],$planctx) || $plan->has_linked_user($USER)) { + // Extract the filename / filepath from the $args array. + $filename = array_pop($args); // The last item in the $args array. + if (empty($args)) { + // $args is empty => the path is '/'. + $filepath = '/'; + } else { + // $args contains the remaining elements of the filepath. + $filepath = '/' . implode('/', $args) . '/'; + } + + // Retrieve the file from the Files API. + $fs = get_file_storage(); + $file = $fs->get_file(\context_system::instance(), 'local_treestudyplan', $filearea, $itemid, $filepath, $filename); + if (!$file) { + // The file does not exist. + return false; + } + + // We can now send the file back to the browser - in this case with a cache lifetime of 1 day and no filtering. + send_stored_file($file, 24*60*60, 0, $forcedownload, $options); + } else { + return false; + } + + + + } else { + return false; + } + + + +} \ No newline at end of file