return $this->errors;
}
+ /**
+ * Return array of valid fields for default values
+ *
+ * @return array
+ */
+ protected function get_valid_fields() {
+ return array_merge(self::$validfields, \tool_uploadcourse_helper::get_custom_course_field_names());
+ }
+
/**
* Assemble the course data based on defaults.
*
* @return array
*/
protected function get_final_create_data($data) {
- foreach (self::$validfields as $field) {
+ foreach ($this->get_valid_fields() as $field) {
if (!isset($data[$field]) && isset($this->defaults[$field])) {
$data[$field] = $this->defaults[$field];
}
global $DB;
$newdata = array();
$existingdata = $DB->get_record('course', array('shortname' => $this->shortname));
- foreach (self::$validfields as $field) {
+ foreach ($this->get_valid_fields() as $field) {
if ($missingonly) {
- if (!is_null($existingdata->$field) and $existingdata->$field !== '') {
+ if (isset($existingdata->$field) and $existingdata->$field !== '') {
continue;
}
}
$coursedata[$rolekey] = $rolename;
}
+ // Custom fields. If the course already exists and mode isn't set to force creation, we can use its context.
+ if ($exists && $mode !== tool_uploadcourse_processor::MODE_CREATE_ALL) {
+ $context = context_course::instance($coursedata['id']);
+ } else {
+ // The category ID is taken from the defaults if it exists, otherwise from course data.
+ $context = context_coursecat::instance($this->defaults['category'] ?? $coursedata['category']);
+ }
+ $customfielddata = tool_uploadcourse_helper::get_custom_course_field_data($this->rawdata, $this->defaults, $context,
+ $errors);
+ if (!empty($errors)) {
+ foreach ($errors as $key => $message) {
+ $this->error($key, $message);
+ }
+
+ return false;
+ }
+
+ foreach ($customfielddata as $name => $value) {
+ $coursedata[$name] = $value;
+ }
+
// Some validation.
if (!empty($coursedata['format']) && !in_array($coursedata['format'], tool_uploadcourse_helper::get_course_formats())) {
$this->error('invalidcourseformat', new lang_string('invalidcourseformat', 'tool_uploadcourse'));
return $rolenames;
}
+ /**
+ * Return array of all custom course fields indexed by their shortname
+ *
+ * @return \core_customfield\field_controller[]
+ */
+ public static function get_custom_course_fields(): array {
+ $result = [];
+
+ $fields = \core_course\customfield\course_handler::create()->get_fields();
+ foreach ($fields as $field) {
+ $result[$field->get('shortname')] = $field;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Return array of custom field element names
+ *
+ * @return string[]
+ */
+ public static function get_custom_course_field_names(): array {
+ $result = [];
+
+ $fields = self::get_custom_course_fields();
+ foreach ($fields as $field) {
+ $controller = \core_customfield\data_controller::create(0, null, $field);
+ $result[] = $controller->get_form_element_name();
+ }
+
+ return $result;
+ }
+
+ /**
+ * Return any elements from passed $data whose key matches one of the custom course fields defined for the site
+ *
+ * @param array $data
+ * @param array $defaults
+ * @param context $context
+ * @param array $errors Will be populated with any errors
+ * @return array
+ */
+ public static function get_custom_course_field_data(array $data, array $defaults, context $context,
+ array &$errors = []): array {
+
+ $fields = self::get_custom_course_fields();
+ $result = [];
+
+ $canchangelockedfields = guess_if_creator_will_have_course_capability('moodle/course:changelockedcustomfields', $context);
+
+ foreach ($data as $name => $originalvalue) {
+ if (preg_match('/^customfield_(?<name>.*)?$/', $name, $matches)
+ && isset($fields[$matches['name']])) {
+
+ $fieldname = $matches['name'];
+ $field = $fields[$fieldname];
+
+ // Skip field if it's locked and user doesn't have capability to change locked fields.
+ if ($field->get_configdata_property('locked') && !$canchangelockedfields) {
+ continue;
+ }
+
+ // Create field data controller.
+ $controller = \core_customfield\data_controller::create(0, null, $field);
+ $controller->set('id', 1);
+
+ $defaultvalue = $defaults["customfield_{$fieldname}"] ?? $controller->get_default_value();
+ $value = (empty($originalvalue) ? $defaultvalue : $field->parse_value($originalvalue));
+
+ // If we initially had a value, but now don't, then reset it to the default.
+ if (!empty($originalvalue) && empty($value)) {
+ $value = $defaultvalue;
+ }
+
+ // Validate data with controller.
+ $fieldformdata = [$controller->get_form_element_name() => $value];
+ $validationerrors = $controller->instance_form_validation($fieldformdata, []);
+ if (count($validationerrors) > 0) {
+ $errors['customfieldinvalid'] = new lang_string('customfieldinvalid', 'tool_uploadcourse',
+ $field->get_formatted_name());
+
+ continue;
+ }
+
+ $controller->set($controller->datafield(), $value);
+
+ // Pass an empty object to the data controller, which will transform it to a correct name/value pair.
+ $instance = new stdClass();
+ $controller->instance_form_before_set_data($instance);
+
+ $result = array_merge($result, (array) $instance);
+ }
+ }
+
+ return $result;
+ }
+
/**
* Helper to increment an ID number.
*
}
return $id;
}
-
-}
+}
\ No newline at end of file
$mform->addHelpButton('defaults[enablecompletion]', 'enablecompletion', 'completion');
}
+ // Add custom fields to the form.
+ $handler = \core_course\customfield\course_handler::create();
+ $handler->instance_form_definition($mform, 0, 'defaultvaluescustomfieldcategory', 'tool_uploadcourse');
+
// Hidden fields.
$mform->addElement('hidden', 'importid');
$mform->setType('importid', PARAM_INT);
$this->add_action_buttons(true, get_string('uploadcourses', 'tool_uploadcourse'));
+ // Prepare custom fields data.
+ $data = (object) $data;
+ $handler->instance_form_before_set_data($data);
+
$this->set_data($data);
}
$enddate = $format->get_default_course_enddate($mform, array('startdate' => 'defaults[startdate]'));
$mform->setDefault('defaults[enddate]', $enddate);
}
+
+ // Tweak the form with values provided by custom fields in use.
+ \core_course\customfield\course_handler::create()->instance_form_definition_after_data($mform);
}
/**
$errors['defaults[enddate]'] = get_string($errorcode, 'error');
}
+ // Custom fields validation.
+ array_merge($errors, \core_course\customfield\course_handler::create()->instance_form_validation($data, $files));
+
return $errors;
}
}
$options = (array) $form2data->options;
$defaults = (array) $form2data->defaults;
+ // Custom field defaults.
+ $customfields = tool_uploadcourse_helper::get_custom_course_field_names();
+ foreach ($customfields as $customfield) {
+ $defaults[$customfield] = $form2data->{$customfield};
+ }
+
// Restorefile deserves its own logic because formslib does not really appreciate
// when the name of a filepicker is an array...
$options['restorefile'] = '';
$string['csvfileerror'] = 'There is something wrong with the format of the CSV file. Please check the number of headings and columns match, and that the delimiter and file encoding are correct: {$a}';
$string['csvline'] = 'Line';
$string['defaultvalues'] = 'Default course values';
+$string['defaultvaluescustomfieldcategory'] = 'Default values for \'{$a}\'';
$string['encoding'] = 'Encoding';
$string['encoding_help'] = 'Encoding of the CSV file.';
$string['errorwhilerestoringcourse'] = 'Error while restoring the course';
$string['nochanges'] = 'No changes';
$string['pluginname'] = 'Course upload';
$string['preview'] = 'Preview';
+$string['customfieldinvalid'] = 'Custom field \'{$a}\' is empty or contains invalid data';
$string['reset'] = 'Reset course after upload';
$string['reset_help'] = 'Whether to reset the course after creating/updating it.';
$string['result'] = 'Result';
And I should see "Course 1"
And I should see "Course 2"
And I should see "Course 3"
+
+ @javascript
+ Scenario: Creation of new courses with custom fields
+ Given the following "custom field categories" exist:
+ | name | component | area | itemid |
+ | Other | core_course | course | 0 |
+ And the following "custom fields" exist:
+ | name | category | type | shortname | configdata |
+ | Field 1 | Other | checkbox | checkbox | |
+ | Field 2 | Other | date | date | |
+ | Field 3 | Other | select | select | {"options":"a\nb\nc"} |
+ | Field 4 | Other | text | text | |
+ | Field 5 | Other | textarea | textarea | |
+ When I upload "admin/tool/uploadcourse/tests/fixtures/courses_custom_fields.csv" file to "File" filemanager
+ And I set the field "Upload mode" to "Create new courses only, skip existing ones"
+ And I click on "Preview" "button"
+ And I click on "Upload courses" "button"
+ Then I should see "Course created"
+ And I should see "Courses created: 1"
+ And I am on site homepage
+ And I should see "Course fields 1"
+ And I should see "Field 1: Yes"
+ And I should see "Field 2: Tuesday, 1 October 2019, 2:00"
+ And I should see "Field 3: b"
+ And I should see "Field 4: Hello"
+ And I should see "Field 5: Goodbye"
+
+ @javascript
+ Scenario: Creation of new courses with custom fields using defaults
+ Given the following "custom field categories" exist:
+ | name | component | area | itemid |
+ | Other | core_course | course | 0 |
+ And the following "custom fields" exist:
+ | name | category | type | shortname | configdata |
+ | Field 1 | Other | checkbox | checkbox | {"checkbydefault":1} |
+ | Field 2 | Other | date | date | {"includetime":0} |
+ | Field 3 | Other | select | select | {"options":"a\nb\nc","defaultvalue":"b"} |
+ | Field 4 | Other | text | text | {"defaultvalue":"Hello"} |
+ | Field 5 | Other | textarea | textarea | {"defaultvalue":"Some text","defaultvalueformat":1} |
+ When I upload "admin/tool/uploadcourse/tests/fixtures/courses.csv" file to "File" filemanager
+ And I set the field "Upload mode" to "Create all, increment shortname if needed"
+ And I click on "Preview" "button"
+ And I expand all fieldsets
+ And the field "Field 1" matches value "1"
+ And the field "Field 3" matches value "b"
+ And the field "Field 4" matches value "Hello"
+ And the field "Field 5" matches value "Some text"
+ # We have to enable the date field manually.
+ And I set the following fields to these values:
+ | customfield_date[enabled] | 1 |
+ | customfield_date[day] | 1 |
+ | customfield_date[month] | June |
+ | customfield_date[year] | 2020 |
+ And I click on "Upload courses" "button"
+ Then I should see "Course created"
+ And I should see "Courses created: 3"
+ And I am on site homepage
+ And I should see "Course 1"
+ And I should see "Field 1: Yes"
+ And I should see "Field 2: 1 June 2020"
+ And I should see "Field 3: b"
+ And I should see "Field 4: Hello"
+ And I should see "Field 5: Some text"
\ No newline at end of file
Background:
Given the following "courses" exist:
| fullname | shortname | category |
- | Some random name | C1 | 0 |
+ | Some random name | C1 | 0 |
+ | Another course | CF1 | 0 |
And I log in as "admin"
And I navigate to "Courses > Upload courses" in site administration
And I should see "Course 1"
And I should not see "Course 2"
And I should not see "Course 3"
+
+ @javascript
+ Scenario: Updating a course with custom fields
+ Given the following "custom field categories" exist:
+ | name | component | area | itemid |
+ | Other | core_course | course | 0 |
+ And the following "custom fields" exist:
+ | name | category | type | shortname | configdata |
+ | Field 1 | Other | checkbox | checkbox | |
+ | Field 2 | Other | date | date | |
+ | Field 3 | Other | select | select | {"options":"a\nb\nc"} |
+ | Field 4 | Other | text | text | |
+ | Field 5 | Other | textarea | textarea | |
+ When I upload "admin/tool/uploadcourse/tests/fixtures/courses_custom_fields.csv" file to "File" filemanager
+ And I set the following fields to these values:
+ | Upload mode | Only update existing courses |
+ | Update mode | Update with CSV data only |
+ And I click on "Preview" "button"
+ And I click on "Upload courses" "button"
+ Then I should see "Course updated"
+ And I should see "Courses updated: 1"
+ And I am on site homepage
+ And I should see "Course fields 1"
+ And I should see "Field 1: Yes"
+ And I should see "Field 2: Tuesday, 1 October 2019, 2:00"
+ And I should see "Field 3: b"
+ And I should see "Field 4: Hello"
+ And I should see "Field 5: Goodbye"
\ No newline at end of file
$this->assertEquals(strtotime('12th July 2013'), $enroldata['manual']->enrolenddate);
}
+ /**
+ * Test upload processing of course custom fields
+ */
+ public function test_custom_fields_data() {
+ $this->resetAfterTest();
+ $this->setAdminUser();
+
+ $course = $this->getDataGenerator()->create_course(['shortname' => 'C1']);
+
+ // Create our custom fields.
+ $category = $this->get_customfield_generator()->create_category();
+ $this->create_custom_field($category, 'date', 'mydatefield');
+ $this->create_custom_field($category, 'text', 'mytextfield');
+ $this->create_custom_field($category, 'textarea', 'mytextareafield');
+
+ // Perform upload.
+ $mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
+ $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
+ $dataupload = [
+ 'shortname' => $course->shortname,
+ 'customfield_mydatefield' => '2020-04-01 16:00',
+ 'customfield_mytextfield' => 'Hello',
+ 'customfield_mytextareafield' => 'Is it me you\'re looking for?',
+ ];
+
+ $uploader = new tool_uploadcourse_course($mode, $updatemode, $dataupload);
+ $this->assertTrue($uploader->prepare());
+ $uploader->proceed();
+
+ // Confirm presence of course custom fields.
+ $data = \core_course\customfield\course_handler::create()->export_instance_data_object($course->id);
+ $this->assertEquals('Wednesday, 1 April 2020, 4:00 PM', $data->mydatefield);
+ $this->assertEquals($dataupload['customfield_mytextfield'], $data->mytextfield);
+ $this->assertContains($dataupload['customfield_mytextareafield'], $data->mytextareafield);
+ }
+
+ /**
+ * Test upload processing of course custom field that is required but empty
+ */
+ public function test_custom_fields_data_required() {
+ $this->resetAfterTest();
+ $this->setAdminUser();
+
+ $course = $this->getDataGenerator()->create_course(['shortname' => 'C1']);
+
+ // Create our custom field.
+ $category = $this->get_customfield_generator()->create_category();
+ $this->create_custom_field($category, 'select', 'myselect', ['required' => true, 'options' => "Cat\nDog"]);
+
+ $mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
+ $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
+ $dataupload = [
+ 'shortname' => $course->shortname,
+ 'customfield_myselect' => null,
+ ];
+
+ $uploader = new tool_uploadcourse_course($mode, $updatemode, $dataupload);
+ $this->assertFalse($uploader->prepare());
+ $this->assertArrayHasKey('customfieldinvalid', $uploader->get_errors());
+
+ // Try again with a default value.
+ $defaults = [
+ 'customfield_myselect' => 2, // Our second option: Dog.
+ ];
+
+ $uploader = new tool_uploadcourse_course($mode, $updatemode, $dataupload, $defaults);
+ $this->assertTrue($uploader->prepare());
+ $uploader->proceed();
+
+ // Confirm presence of course custom fields.
+ $data = \core_course\customfield\course_handler::create()->export_instance_data_object($course->id);
+ $this->assertEquals('Dog', $data->myselect);
+ }
+
+ /**
+ * Test upload processing of course custom field with an invalid select option
+ */
+ public function test_custom_fields_data_invalid_select_option() {
+ $this->resetAfterTest();
+ $this->setAdminUser();
+
+ $course = $this->getDataGenerator()->create_course(['shortname' => 'C1']);
+
+ // Create our custom field.
+ $category = $this->get_customfield_generator()->create_category();
+ $this->create_custom_field($category, 'select', 'myselect',
+ ['required' => true, 'options' => "Cat\nDog", 'defaultvalue' => 'Cat']);
+
+ $mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
+ $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
+ $dataupload = [
+ 'shortname' => $course->shortname,
+ 'customfield_myselect' => 'Fish', // No, invalid.
+ ];
+
+ $uploader = new tool_uploadcourse_course($mode, $updatemode, $dataupload);
+ $this->assertTrue($uploader->prepare());
+ $uploader->proceed();
+
+ // Confirm presence of course custom fields.
+ $data = \core_course\customfield\course_handler::create()->export_instance_data_object($course->id);
+ $this->assertEquals('Cat', $data->myselect);
+ }
+
+ /**
+ * Test upload processing of course custom field with an out of range date
+ */
+ public function test_custom_fields_data_invalid_date() {
+ $this->resetAfterTest();
+ $this->setAdminUser();
+
+ $course = $this->getDataGenerator()->create_course(['shortname' => 'C1']);
+
+ // Create our custom field.
+ $category = $this->get_customfield_generator()->create_category();
+ $this->create_custom_field($category, 'date', 'mydate',
+ ['mindate' => strtotime('2020-04-01'), 'maxdate' => '2020-04-30']);
+
+ $mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
+ $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
+ $dataupload = [
+ 'shortname' => $course->shortname,
+ 'customfield_mydate' => '2020-05-06', // Out of range.
+ ];
+
+ $uploader = new tool_uploadcourse_course($mode, $updatemode, $dataupload);
+ $this->assertFalse($uploader->prepare());
+ $this->assertArrayHasKey('customfieldinvalid', $uploader->get_errors());
+ }
+
public function test_idnumber_problems() {
$this->resetAfterTest(true);
$co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions);
$this->assertFalse($co->prepare());
$this->assertArrayHasKey('cannotrenameshortnamealreadyinuse', $co->get_errors());
+ }
+ /**
+ * Get custom field plugin generator
+ *
+ * @return core_customfield_generator
+ */
+ protected function get_customfield_generator() : core_customfield_generator {
+ return $this->getDataGenerator()->get_plugin_generator('core_customfield');
}
-}
+ /**
+ * Helper method to create custom course field
+ *
+ * @param \core_customfield\category_controller $category
+ * @param string $type
+ * @param string $shortname
+ * @param array $configdata
+ * @return \core_customfield\field_controller
+ */
+ protected function create_custom_field(\core_customfield\category_controller $category, string $type, string $shortname,
+ array $configdata = []) : \core_customfield\field_controller {
+
+ return $this->get_customfield_generator()->create_field([
+ 'categoryid' => $category->get('id'),
+ 'type' => $type,
+ 'shortname' => $shortname,
+ 'configdata' => $configdata,
+ ]);
+ }
+}
\ No newline at end of file
--- /dev/null
+shortname,fullname,summary,category,customfield_checkbox,customfield_date,customfield_select,customfield_text,customfield_textarea
+CF1,Course fields 1,Testing course fields,1,1,2019-10-01 14:00,b,Hello,Goodbye
\ No newline at end of file
$this->assertArrayHasKey('invalidroles', $errors);
}
+ /**
+ * Test custom field data processing
+ */
+ public function test_get_custom_course_field_data() {
+ global $DB;
+
+ $this->resetAfterTest();
+
+ // Create all the fields!
+ $category = $this->get_customfield_generator()->create_category();
+
+ $checkboxfield = $this->create_custom_field($category, 'checkbox', 'mycheckbox');
+ $datefield = $this->create_custom_field($category, 'date', 'mydate');
+ $selectfield = $this->create_custom_field($category, 'select', 'myselect', ['options' => "Red\nGreen\nBlue"]);
+ $textfield = $this->create_custom_field($category, 'text', 'mytext', ['locked' => 1]);
+ $textareafield = $this->create_custom_field($category, 'textarea', 'mytextarea');
+
+ $fields = tool_uploadcourse_helper::get_custom_course_fields();
+ $this->assertCount(5, $fields);
+
+ $this->assertArrayHasKey($checkboxfield->get('shortname'), $fields);
+ $this->assertInstanceOf(customfield_checkbox\field_controller::class, $fields[$checkboxfield->get('shortname')]);
+
+ $this->assertArrayHasKey($datefield->get('shortname'), $fields);
+ $this->assertInstanceOf(customfield_date\field_controller::class, $fields[$datefield->get('shortname')]);
+
+ $this->assertArrayHasKey($selectfield->get('shortname'), $fields);
+ $this->assertInstanceOf(customfield_select\field_controller::class, $fields[$selectfield->get('shortname')]);
+
+ $this->assertArrayHasKey($textfield->get('shortname'), $fields);
+ $this->assertInstanceOf(customfield_text\field_controller::class, $fields[$textfield->get('shortname')]);
+
+ $this->assertArrayHasKey($textareafield->get('shortname'), $fields);
+ $this->assertInstanceOf(customfield_textarea\field_controller::class, $fields[$textareafield->get('shortname')]);
+
+ $data = [
+ 'customfield_mycheckbox' => '1',
+ 'customfield_mydate' => '2019-10-01',
+ 'customfield_myselect' => 'Green',
+ 'customfield_mytext' => 'Hello',
+ 'customfield_myunknownfield' => 'Goodbye',
+ ];
+
+ $expected = [
+ 'customfield_mycheckbox' => '1',
+ 'customfield_mydate' => strtotime('2019-10-01'),
+ 'customfield_myselect' => 2,
+ 'customfield_mytext' => 'Hello',
+ ];
+
+ $course = $this->getDataGenerator()->create_course();
+ $user = $this->getDataGenerator()->create_and_enrol($course, 'manager');
+ $this->setUser($user);
+
+ $context = context_course::instance($course->id);
+
+ $this->assertEquals($expected, tool_uploadcourse_helper::get_custom_course_field_data($data, [], $context));
+
+ // Now add our custom textarea field (separately because the value of it's 'itemid' element is unknown).
+ $data['customfield_mytextarea'] = 'Something';
+ $fields = tool_uploadcourse_helper::get_custom_course_field_data($data, [], $context);
+ $this->assertArrayHasKey('customfield_mytextarea_editor', $fields);
+ $this->assertArrayHasKey('text', $fields['customfield_mytextarea_editor']);
+ $this->assertEquals('Something', $fields['customfield_mytextarea_editor']['text']);
+
+ // Now prohibit the capability to change locked fields for the manager role.
+ $managerrole = $DB->get_record('role', ['shortname' => 'manager']);
+ role_change_permission($managerrole->id, $context, 'moodle/course:changelockedcustomfields', CAP_PROHIBIT);
+
+ // The locked 'mytext' custom field should not be returned.
+ $fields = tool_uploadcourse_helper::get_custom_course_field_data($data, [], $context);
+ $this->assertCount(4, $fields);
+ $this->assertArrayNotHasKey('customfield_mytext', $fields);
+ }
+
public function test_increment_idnumber() {
$this->resetAfterTest(true);
$this->assertEquals($cat3_fakedouble->id, tool_uploadcourse_helper::resolve_category_by_path($path));
$this->assertEquals($cat3_fakedouble->id, tool_uploadcourse_helper::resolve_category_by_path($path));
}
-}
+
+ /**
+ * Get custom field plugin generator
+ *
+ * @return core_customfield_generator
+ */
+ protected function get_customfield_generator() : core_customfield_generator {
+ return $this->getDataGenerator()->get_plugin_generator('core_customfield');
+ }
+
+ /**
+ * Helper method to create custom course field
+ *
+ * @param \core_customfield\category_controller $category
+ * @param string $type
+ * @param string $shortname
+ * @param array $configdata
+ * @return \core_customfield\field_controller
+ */
+ protected function create_custom_field(\core_customfield\category_controller $category, string $type, string $shortname,
+ array $configdata = []) : \core_customfield\field_controller {
+
+ return $this->get_customfield_generator()->create_field([
+ 'categoryid' => $category->get('id'),
+ 'type' => $type,
+ 'shortname' => $shortname,
+ 'configdata' => $configdata,
+ ]);
+ }
+}
\ No newline at end of file
*
* @return string
*/
- protected function get_form_element_name() : string {
+ public function get_form_element_name() : string {
return 'customfield_' . $this->get_field()->get('shortname');
}
return $fieldcontroller;
}
+ /**
+ * Perform pre-processing of field values, for example those that originate from an external source (e.g. upload course tool)
+ *
+ * Override in plugin classes as necessary
+ *
+ * @param string $value
+ * @return mixed
+ */
+ public function parse_value(string $value) {
+ return $value;
+ }
+
/**
* Validate the data on the field configuration form
*
*
* @param \MoodleQuickForm $mform
* @param int $instanceid id of the instance, can be null when instance is being created
+ * @param string $headerlangidentifier If specified, a lang string will be used for field category headings
+ * @param string $headerlangcomponent
*/
- public function instance_form_definition(\MoodleQuickForm $mform, int $instanceid = 0) {
+ public function instance_form_definition(\MoodleQuickForm $mform, int $instanceid = 0,
+ ?string $headerlangidentifier = null, ?string $headerlangcomponent = null) {
$editablefields = $this->get_editable_fields($instanceid);
$fieldswithdata = api::get_instance_fields_data($editablefields, $instanceid);
foreach ($fieldswithdata as $data) {
$categoryid = $data->get_field()->get_category()->get('id');
if ($categoryid != $lastcategoryid) {
- $mform->addElement('header', 'category_' . $categoryid,
- format_string($data->get_field()->get_category()->get('name')));
+ $categoryname = format_string($data->get_field()->get_category()->get('name'));
+
+ // Load category header lang string if specified.
+ if (!empty($headerlangidentifier)) {
+ $categoryname = get_string($headerlangidentifier, $headerlangcomponent, $categoryname);
+ }
+
+ $mform->addElement('header', 'category_' . $categoryid, $categoryname);
$lastcategoryid = $categoryid;
}
$data->instance_form_definition($mform);
$this->get_formatted_name());
return $ret;
}
-}
+
+ /**
+ * Convert given value into appropriate timestamp
+ *
+ * @param string $value
+ * @return int
+ */
+ public function parse_value(string $value) {
+ $timestamp = strtotime($value);
+
+ // If we have a valid, positive timestamp then return it.
+ return $timestamp > 0 ? $timestamp : 0;
+ }
+}
\ No newline at end of file
$this->assertEquals(null, $d->export_value());
}
+ /**
+ * Data provider for {@see test_parse_value}
+ *
+ * @return array
+ */
+ public function parse_value_provider() : array {
+ return [
+ // Valid times.
+ ['2019-10-01', strtotime('2019-10-01')],
+ ['2019-10-01 14:00', strtotime('2019-10-01 14:00')],
+ // Invalid times.
+ ['ZZZZZ', 0],
+ ['202-04-01', 0],
+ ['2019-15-15', 0],
+ ];
+ }
+ /**
+ * Test field parse_value method
+ *
+ * @param string $value
+ * @param int $expected
+ * @return void
+ *
+ * @dataProvider parse_value_provider
+ */
+ public function test_parse_value(string $value, int $expected) {
+ $this->assertSame($expected, $this->cfields[1]->parse_value($value));
+ }
+
/**
* Deleting fields and data
*/
public function test_delete() {
$this->cfcat->get_handler()->delete_all();
}
-}
+}
\ No newline at end of file
$this->get_formatted_name());
return $ret;
}
-}
+
+ /**
+ * Locate the value parameter in the field options array, and return it's index
+ *
+ * @param string $value
+ * @return int
+ */
+ public function parse_value(string $value) {
+ return (int) array_search($value, self::get_options_array($this));
+ }
+}
\ No newline at end of file
$this->assertEquals('b', $d->export_value());
}
+ /**
+ * Data provider for {@see test_parse_value}
+ *
+ * @return array
+ */
+ public function parse_value_provider() : array {
+ return [
+ ['Red', 1],
+ ['Blue', 2],
+ ['Green', 3],
+ ['Mauve', 0],
+ ];
+ }
+
+ /**
+ * Test field parse_value method
+ *
+ * @param string $value
+ * @param int $expected
+ * @return void
+ *
+ * @dataProvider parse_value_provider
+ */
+ public function test_parse_value(string $value, int $expected) {
+ $field = $this->get_generator()->create_field([
+ 'categoryid' => $this->cfcat->get('id'),
+ 'type' => 'select',
+ 'shortname' => 'myselect',
+ 'configdata' => [
+ 'options' => "Red\nBlue\nGreen",
+ ],
+ ]);
+
+ $this->assertSame($expected, $field->parse_value($value));
+ }
+
/**
* Deleting fields and data
*/
public function test_delete() {
$this->cfcat->get_handler()->delete_all();
}
-}
+}
\ No newline at end of file
*
* @return string
*/
- protected function get_form_element_name() : string {
+ public function get_form_element_name() : string {
return parent::get_form_element_name() . '_editor';
}