From 241fa420d8a9d5347d0fa38b23f597c11cb9467a Mon Sep 17 00:00:00 2001 From: Carlos Castillo Date: Thu, 8 Aug 2024 16:58:37 +0200 Subject: [PATCH] MDL-82571 customfield_number: Display format to number custom field Add extra settings to the new number custom field in order to allow it format the exported data using placeholders plus supporting multi language. --- course/tests/externallib_test.php | 43 +++++++++ .../field/number/classes/data_controller.php | 10 +- .../field/number/classes/field_controller.php | 56 +++++++++++ .../number/lang/en/customfield_number.php | 12 +++ .../field/number/tests/behat/field.feature | 93 +++++++++++++++++++ .../number/tests/data_controller_test.php | 54 ++++++++++- .../number/tests/field_controller_test.php | 15 ++- 7 files changed, 266 insertions(+), 17 deletions(-) create mode 100644 customfield/field/number/tests/behat/field.feature diff --git a/course/tests/externallib_test.php b/course/tests/externallib_test.php index a73238ff7be..d18579e95f2 100644 --- a/course/tests/externallib_test.php +++ b/course/tests/externallib_test.php @@ -892,6 +892,49 @@ final class externallib_test extends externallib_advanced_testcase { 'value' => userdate(1580389200), 'valueraw' => 1580389200, ], reset($course['customfields'])); + + // Set the multilang filter to apply to strings + reset filer caches. + filter_set_global_state('multilang', TEXTFILTER_ON); + filter_set_applies_to_strings('multilang', true); + \filter_manager::reset_caches(); + + // Let's create a custom field (number), and test the placeholders/multilang display. + /** @var core_customfield_generator $cfgenerator */ + $cfgenerator = $this->getDataGenerator()->get_plugin_generator('core_customfield'); + $numberfieldata = [ + 'categoryid' => $fieldcategory->get('id'), + 'name' => 'Price', + 'shortname' => 'price', + 'type' => 'number', + 'configdata' => [ + 'display' => '{value}', + 'decimalplaces' => 2, + ], + ]; + + // Create a number custom field with default display template. + $numberfield = $cfgenerator->create_field($numberfieldata); + $cfgenerator->add_instance_data($numberfield, $newcourse->id, 15); + + // Create a number custom field with multilang display template. + $numberfieldata['name'] = 'Price (multilang)'; + $numberfieldata['shortname'] = 'pricemultilang'; + $numberfieldata['configdata']['display'] = '$ {value}' + . '€ {value}'; + $numberfield1 = $cfgenerator->create_field($numberfieldata); + $cfgenerator->add_instance_data($numberfield1, $newcourse->id, 20); + + $courses = external_api::clean_returnvalue( + core_course_external::get_courses_returns(), + core_course_external::get_courses(['ids' => [$newcourse->id]]) + ); + + $course = reset($courses); + $this->assertCount(3, $course['customfields']); + + // Assert the received number custom fields display placeholders correctly with multilang filter when applied. + $this->assertEquals('15.00', $course['customfields'][1]['value']); + $this->assertEquals('$ 20.00', $course['customfields'][2]['value']); } /** diff --git a/customfield/field/number/classes/data_controller.php b/customfield/field/number/classes/data_controller.php index 52be04a3007..16d2bb0bd95 100644 --- a/customfield/field/number/classes/data_controller.php +++ b/customfield/field/number/classes/data_controller.php @@ -120,12 +120,8 @@ class data_controller extends \core_customfield\data_controller { * @return string|null */ public function export_value(): ?string { - $value = $this->get_value(); - if ($this->is_empty($value)) { - return null; - } - - $decimalplaces = (int) $this->get_field()->get_configdata_property('decimalplaces'); - return format_float((float) $value, $decimalplaces); + /** @var field_controller $field */ + $field = $this->get_field(); + return $field->prepare_field_for_display($this->get_value(), $this->get_context()); } } diff --git a/customfield/field/number/classes/field_controller.php b/customfield/field/number/classes/field_controller.php index 0bd0413b593..c282f725eaf 100644 --- a/customfield/field/number/classes/field_controller.php +++ b/customfield/field/number/classes/field_controller.php @@ -18,6 +18,9 @@ declare(strict_types=1); namespace customfield_number; +use core\context\system; +use core\context; +use html_writer; use MoodleQuickForm; /** @@ -62,6 +65,29 @@ class field_controller extends \core_customfield\field_controller { $mform->setDefault('configdata[decimalplaces]', 0); } $mform->setType('configdata[decimalplaces]', PARAM_INT); + + // Display format settings. + // TODO: Change this after MDL-82996 fixed. + $randelname = 'str_' . random_string(); + $mform->addGroup([], $randelname, html_writer::tag('h4', get_string('headerdisplaysettings', 'customfield_number'))); + + // Display template. + $mform->addElement('text', 'configdata[display]', get_string('display', 'customfield_number'), + ['size' => 50]); + $mform->setType('configdata[display]', PARAM_TEXT); + $mform->addHelpButton('configdata[display]', 'display', 'customfield_number'); + if ($this->get_configdata_property('display') === null) { + $mform->setDefault('configdata[display]', '{value}'); + } + + // Display when zero. + $mform->addElement('text', 'configdata[displaywhenzero]', get_string('displaywhenzero', 'customfield_number'), + ['size' => 50]); + $mform->setType('configdata[displaywhenzero]', PARAM_TEXT); + $mform->addHelpButton('configdata[displaywhenzero]', 'displaywhenzero', 'customfield_number'); + if ($this->get_configdata_property('displaywhenzero') === null) { + $mform->setDefault('configdata[displaywhenzero]', 0); + } } /** @@ -74,6 +100,11 @@ class field_controller extends \core_customfield\field_controller { public function config_form_validation(array $data, $files = []): array { $errors = parent::config_form_validation($data, $files); + $display = $data['configdata']['display']; + if (!preg_match('/\{value}/', $display)) { + $errors['configdata[display]'] = get_string('displayvalueconfigerror', 'customfield_number'); + } + // Each of these configuration fields are optional. $defaultvalue = $data['configdata']['defaultvalue'] ?? ''; $minimumvalue = $data['configdata']['minimumvalue'] ?? ''; @@ -103,4 +134,29 @@ class field_controller extends \core_customfield\field_controller { return $errors; } + + /** + * Prepares a value for export + * + * @param mixed $value + * @param context|null $context + * @return string|null + */ + public function prepare_field_for_display(mixed $value, ?context $context = null): ?string { + if ((float)$value == 0) { + $value = $this->get_configdata_property('displaywhenzero'); + if ((string) $value === '') { + return null; + } + } else { + // Let's format the value. + $decimalplaces = (int) $this->get_configdata_property('decimalplaces'); + $value = format_float((float) $value, $decimalplaces); + + // Apply the display format. + $format = $this->get_configdata_property('display'); + $value = str_replace('{value}', $value, $format); + } + return format_string($value, true, ['context' => $context ?? system::instance()]); + } } diff --git a/customfield/field/number/lang/en/customfield_number.php b/customfield/field/number/lang/en/customfield_number.php index aff194f18c8..dec2ae0cd0c 100644 --- a/customfield/field/number/lang/en/customfield_number.php +++ b/customfield/field/number/lang/en/customfield_number.php @@ -26,6 +26,18 @@ defined('MOODLE_INTERNAL') || die; $string['decimalplaces'] = 'Decimal places'; $string['defaultvalueconfigerror'] = 'Default value must be between minimum and maximum'; +$string['display'] = 'Display template'; +$string['display_help'] = 'How to display the value of the field. Use the following placeholders: + +* **{value}** - display value in a general format (float with decimals configured in the field) +* **$ {value}** - price in dollars +* **{value} hrs** - duration in hours'; +$string['displayvalueconfigerror'] = 'The placeholder is not invalid'; +$string['displaywhenzero'] = 'Display when zero'; +$string['displaywhenzero_help'] = 'How to display the field value when the value is "0". For example, in case of a price you can display the word "Free" but in case of the duration you may want to leave it empty since it means that the duration was not estimated. + +Leave empty if you do not want to display anything at all when the value is set to "0".'; +$string['headerdisplaysettings'] = 'Display format'; $string['maximumvalue'] = 'Maximum value'; $string['maximumvalueerror'] = 'Value must be less than or equal to {$a}'; $string['minimumvalue'] = 'Minimum value'; diff --git a/customfield/field/number/tests/behat/field.feature b/customfield/field/number/tests/behat/field.feature new file mode 100644 index 00000000000..37576b41d65 --- /dev/null +++ b/customfield/field/number/tests/behat/field.feature @@ -0,0 +1,93 @@ +@customfield @customfield_number @javascript +Feature: Managers can manage course custom fields number + In order to have additional data on the course + As a manager + I need to create, edit, remove and display number custom fields + + Background: + Given the following "custom field categories" exist: + | name | component | area | itemid | + | Category for test | core_course | course | 0 | + And I log in as "admin" + And I navigate to "Courses > Default settings > Course custom fields" in site administration + + Scenario: Create a custom course number field + When I click on "Add a new custom field" "link" + And I click on "Number" "link" + When I set the following fields to these values: + | Name | Number field | + | Short name | numberfield | + | Display template | test | + And I click on "Save changes" "button" in the "Adding a new Number" "dialogue" + Then I should see "The placeholder is not invalid" + And I set the following fields to these values: + | Name | Number field | + | Short name | numberfield | + | Display template | {value} | + And I click on "Save changes" "button" in the "Adding a new Number" "dialogue" + And I should see "Number field" + And I log out + + Scenario: Edit a custom course number field + When I click on "Add a new custom field" "link" + And I click on "Number" "link" + And I set the following fields to these values: + | Name | Number field | + | Short name | numberfield | + And I click on "Save changes" "button" in the "Adding a new Number" "dialogue" + Then I should see "Number field" + And I click on "Edit" "link" in the "Number field" "table_row" + And I set the following fields to these values: + | Name | Edited number field | + And I click on "Save changes" "button" in the "Updating Number field" "dialogue" + Then I should see "Edited number field" + And I log out + + Scenario: Delete a custom course number field + When I click on "Add a new custom field" "link" + And I click on "Number" "link" + And I set the following fields to these values: + | Name | Number field | + | Short name | numberfield | + And I click on "Save changes" "button" in the "Adding a new Number" "dialogue" + And I click on "Delete" "link" in the "Number field" "table_row" + And I click on "Yes" "button" in the "Confirm" "dialogue" + And I wait until the page is ready + And I wait until "Number field" "text" does not exist + Then I should not see "Number field" + And I log out + + Scenario Outline: A number field must shown correctly on course listing + Given the following "users" exist: + | username | firstname | lastname | email | + | teacher1 | Teacher | Example 1 | teacher1@example.com | + And the following "courses" exist: + | fullname | shortname | format | + | Course 1 | C1 | topics | + And the following "course enrolments" exist: + | user | course | role | + | teacher1 | C1 | editingteacher | + And I navigate to "Courses > Default settings > Course custom fields" in site administration + And I click on "Add a new custom field" "link" + And I click on "Number" "link" + When I set the following fields to these values: + | Name | Test number | + | Short name | testnumber | + | Decimal places | 2 | + | Display template |