moodle_local_treestudyplan/classes/form/studyplan_editform.php
2024-04-19 16:46:30 +02:00

391 lines
16 KiB
PHP

<?php
namespace local_treestudyplan\form;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot.'/repository/lib.php');
use local_treestudyplan\aggregator;
use local_treestudyplan\studyplan;
use local_treestudyplan\premium;
use local_treestudyplan\contextinfo;
use local_treestudyplan\studyplanservice;
use local_treestudyplan\courseservice;
use local_treestudyplan\form\text_integer;
use moodle_exception;
use stdClass;
/**
* Moodleform class for the studyplan editor. A Moodleform is used here to facilitate a rich editor
* in the studyplan description
*/
class studyplan_editform extends formbase {
/**
* Capability required to edit study plans
* @var string
*/
const CAP_EDIT = "local/treestudyplan:editstudyplan";
/**
* 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->create = $params->mode=='create'?true:false;
if($customdata->create){
$customdata->context = \context::instance_by_id($params->contextid);
} else {
$customdata->plan = studyplan::find_by_id($params->studyplan_id);
$customdata->context = $customdata->plan->context();
$customdata->simplemodel = $customdata->plan->simple_model();
}
$customdata->editoroptions = [
'trusttext' => true,
'subdirs' => true,
'maxfiles' => 20,
'maxbytes' => 20*1024*1024,
'context' => \context_system::instance(), // Keep the files in system context
];
$customdata->fileoptions = [
'subdirs' => 0,
'maxbytes' => 10*1024*1024, // Max 10MiB should be sufficient for a picture.
'areamaxbytes' => 10485760,
'maxfiles' => 1, // Just one file
'accepted_types' => ['.jpg', '.png', '.jpeg', '.svg', '.svgz'],
'return_types' => \FILE_INTERNAL | \FILE_EXTERNAL,
];
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 $customdata) {
/*webservicehelper::require_capabilities(self::CAP_EDIT,$customdata->context); */
}
/**
* Generate form data from parameters
* Also validate parameters and access permissions here
*
* @param object $customdata The parameters for form initialization
* @return array Form data based on parameters
*/
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...
*/
if($customdata->create) {
$entry = new stdClass;
$entry->context_id = $customdata->context->id;
$entry->aggregation = get_config("local_treestudyplan","aggregation_mode");
$ag_cfg = json_decode(aggregator::create($entry->aggregation, "")->config_string(),true);
// Determine the next august 1st for default value purposes.
$august = strtotime("first day of august this year");
if($august < time()) {
$august = strtotime("first day of august next year");
}
$entry->startdate = $august;
$entry->enddate = $august + (364*24*60*60); // Not bothering about leap years here.
$entry->periods = 4;
} else {
$entry = $DB->get_record(studyplan::TABLE, ['id' => $customdata->plan->id()]);
$entry->startdate = strtotime($customdata->simplemodel['pages'][0]['startdate']);
$entry->enddate = strtotime($customdata->simplemodel['pages'][0]['enddate']);
$entry->periods = $customdata->simplemodel['pages'][0]['periods'];
$ag_cfg = json_decode($customdata->plan->aggregator()->config_string(),true);
}
// Prepare the editor
$entry = file_prepare_standard_editor( $entry,
'description',
$customdata->editoroptions,
\context_system::instance(),
'local_treestudyplan',
'studyplan',
($customdata->create)?null:$customdata->plan->id()
);
// Prepare file area for the icon
// Get an unused draft itemid which will be used for this form.
$draftitemid = file_get_submitted_draft_itemid('icon');
file_prepare_draft_area(
// The $draftitemid is the target location.
$draftitemid,
// The combination of contextid / component / filearea / itemid
// form the virtual bucket that files are currently stored in
// and will be copied from.
\context_system::instance()->id,
'local_treestudyplan',
'icon',
($customdata->create)?null:$customdata->plan->id(),
$customdata->fileoptions
);
$entry->icon = $draftitemid;
// Add aggregation configs to entry.
foreach ($ag_cfg as $key => $val) {
$entrykey = $entry->aggregation."_".$key;
$entry->$entrykey = $val;
}
return $entry;
}
/**
* Set up the form definition
*/
public function definition() {
$mform = $this->_form;
$customdata = (object)$this->_customdata;
// Register integer type
text_integer::Register();
// Define the form
$field = 'name';
$mform->addElement('text',$field,
get_string('studyplan_name','local_treestudyplan'),
[]);
$mform->addRule($field, null, 'required', null, 'client');
$field = 'shortname';
$mform->addElement('text',$field,
get_string('studyplan_shortname','local_treestudyplan'),
[]);
$mform->addRule($field, null, 'required', null, 'client');
$field = 'idnumber';
$mform->addElement('text',$field,
get_string('studyplan_idnumber','local_treestudyplan'),
[]);
$contextlist = [];
foreach(courseservice::list_available_categories('edit') as $c){
$contextlist[$c['context_id']] = implode(" / ",$c['category']['path']);
}
$mform->addElement('autocomplete', 'context_id',
get_string('studyplan_context','local_treestudyplan'),
$contextlist);
$mform->addRule('context_id', null, 'required', null, 'client');
$mform->addElement(
'filemanager',
'icon',
get_string('studyplan_icon', 'local_treestudyplan'),
null,
$customdata->fileoptions
);
if ($customdata->create) {
$timeless = \get_config("local_treestudyplan","timelessperiods");
if ( !$timeless) {
// Only add these fields if a new studyplan is created, to easily initialize the first page
$field = 'startdate';
$mform->addElement('date_selector',$field,
get_string('studyplan_startdate','local_treestudyplan'),
[]);
$mform->addRule($field, null, 'required', null, 'client');
$field = 'enddate';
$mform->addElement('date_selector',$field,
get_string('studyplan_enddate','local_treestudyplan'),
[]);
$mform->addRule($field, null, 'required', null, 'client');
}
$field = 'periods';
$mform->addElement('text_integer',$field,
get_string('studyplan_slots','local_treestudyplan'),
["unsigned" => true]);
$mform->setType($field, PARAM_INT);
$mform->addRule($field, null, 'required', null, 'client');
} else {
// These fields are only relevant if the studyplan is edited
$field = 'suspended';
$mform->addElement('advcheckbox',$field,
get_string('studyplan_suspend','local_treestudyplan'),
get_string('studyplan_suspend_details','local_treestudyplan'),
[],
);
if (premium::enabled()) {
$field = 'template';
$mform->addElement('advcheckbox',$field,
get_string('studyplan_template','local_treestudyplan'),
get_string('studyplan_template_details','local_treestudyplan'),
[],
);
}
}
$aggregators = [];
foreach(aggregator::list_model() as $a){
// Add method only if not deprecated or currently used
if ( $customdata->simplemodel['aggregation'] == $a['id'] || !($a['deprecated']) ) {
$aggregators[$a['id']] = $a['name'];
}
}
$field = 'aggregation';
$mform->addElement('select',$field,
get_string('choose_aggregation_style','local_treestudyplan'),
$aggregators);
/* Start Bistate aggregation specific items */
$field = 'bistate_thresh_excellent';
$mform->addElement('text_integer',$field,
get_string('setting_bistate_thresh_excellent','local_treestudyplan'),
["unsigned" => false],
);
$mform->setType($field, PARAM_INT);
$mform->hideIf($field, "aggregation", "neq", "bistate");
$field = 'bistate_thresh_good';
$mform->addElement('text_integer',$field,
get_string('setting_bistate_thresh_good','local_treestudyplan'),
["unsigned" => true],
);
$mform->setType($field, PARAM_INT);
$mform->hideIf($field, "aggregation", "neq", "bistate");
$field = 'bistate_thresh_completed';
$mform->addElement('text_integer',$field,
get_string('setting_bistate_thresh_completed','local_treestudyplan'),
["unsigned" => true],
);
$mform->setType($field, PARAM_INT);
$mform->hideIf($field, "aggregation", "neq", "bistate");
$field = 'bistate_use_failed';
$mform->addElement('checkbox',$field,
get_string('setting_bistate_support_failed','local_treestudyplan'),
[],
);
$mform->hideIf($field, "aggregation", "neq", "bistate");
$field = 'bistate_accept_pending_as_submitted';
$mform->addElement('checkbox',$field,
get_string('setting_bistate_accept_pending_submitted','local_treestudyplan'),
[],
);
$mform->hideIf($field, "aggregation", "neq", "bistate");
/* End Bistate aggregation specific items */
$mform->addElement('editor', 'description_editor',
get_string('studyplan_description', 'local_treestudyplan'),
null,
$customdata->editoroptions);
$mform->setType('description_editor', PARAM_RAW);
}
/**
* Process the submitted data and perform necessary actions
* @param object $entry The processed form data;
* @return bool false if submission not successful
* @throws \moodle_exception if an error must be given for a specific reason.
*/
protected function process_submitted_data($entry) {
$customdata = (object)$this->_customdata;
// Add aggregation configs to entry.
$ag_cfg = json_decode(aggregator::create($entry->aggregation, "")->config_string(),true); // Retrieve default config string from selected aggregation method
foreach ($ag_cfg as $key => $val) {
$entrykey = $entry->aggregation."_".$key;
$ag_cfg[$key] = $entry->$entrykey;
}
$aggregation_config = json_encode($ag_cfg);
if ($customdata->create) {
// Use our own abstraction to create the record, so caches are maintained
$plan = studyplan::add(['name' => $entry->name,
'shortname' => $entry->shortname,
'idnumber' => $entry->idnumber,
'context_id' => $entry->context_id,
'aggregation' => $entry->aggregation,
'aggregation_config' => $aggregation_config,
'startdate' => date("Y-m-d",$entry->startdate),
'enddate' => date("Y-m-d",$entry->enddate),
'periods' => $entry->periods,
]);
// Process the provided files in the description editor
$entry = file_postupdate_standard_editor($entry,
'description',
$customdata->editoroptions,
\context_system::instance(),
'local_treestudyplan',
'studyplan',
$plan->id());
// Update the description
$plan->edit([
'description' => $entry->description,
'descriptionformat' => $entry->descriptionformat,
]);
} else {
$plan = $customdata->plan;
// Process the provided files in the description editor
$entry = file_postupdate_standard_editor($entry,
'description',
$customdata->editoroptions,
\context_system::instance(),
'local_treestudyplan',
'studyplan',
$plan->id());
// Use our own abstraction to update the record, so caches are maintained
$plan->edit(['name' => $entry->name,
'shortname' => $entry->shortname,
'idnumber' => $entry->idnumber,
'context_id' => $entry->context_id,
'description' => $entry->description,
'descriptionformat' => $entry->descriptionformat,
'aggregation' => $entry->aggregation,
'aggregation_config' => $aggregation_config,
'suspended' => $entry->suspended,
'template' => $entry->template,
]);
}
// Now save the icon file in correct part of the File API.
file_save_draft_area_files(
// The $entry->icon property contains the itemid of the draft file area.
$entry->icon,
// The combination of contextid / component / filearea / itemid
// form the virtual bucket that file are stored in.
\context_system::instance()->id,
'local_treestudyplan',
'icon',
$plan->id(),
$customdata->fileoptions
);
/* Return the simple model of the plan to make sure we can update stuff.
Parse it through the clean_returnvalue function of exernal api (of which studyplanservice is a subclass)
so we return it in a consistent way
*/
return studyplanservice::clean_returnvalue(studyplan::simple_structure(),$plan->simple_model());
}
}