Skip to content

Commit

Permalink
Merge branch 'MDL-53140-master' of git://github.com/rezaies/moodle
Browse files Browse the repository at this point in the history
  • Loading branch information
abgreeve committed May 1, 2019
2 parents f22d015 + 47538a6 commit eee677c
Show file tree
Hide file tree
Showing 27 changed files with 767 additions and 54 deletions.
187 changes: 187 additions & 0 deletions lib/form/float.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
<?php
// 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 <http://www.gnu.org/licenses/>.


/**
* Float type form element
*
* Contains HTML class for a float type element
*
* @package core_form
* @category form
* @copyright 2019 Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/

defined('MOODLE_INTERNAL') || die();

global $CFG;
require_once($CFG->libdir . '/form/text.php');

/**
* Float type form element.
*
* This is preferred over the text element when working with float numbers, and takes care of the fact that different languages
* may use different symbols as the decimal separator.
* Using this element, submitted float numbers will be automatically translated from the localised format into the computer format,
* and vice versa when they are being displayed.
*
* @copyright 2019 Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class MoodleQuickForm_float extends MoodleQuickForm_text {

/**
* MoodleQuickForm_float constructor.
*
* @param string $elementName (optional) name of the float field
* @param string $elementLabel (optional) float field label
* @param string $attributes (optional) Either a typical HTML attribute string or an associative array
*/
public function __construct($elementName = null, $elementLabel = null, $attributes = null) {
parent::__construct($elementName, $elementLabel, $attributes);
$this->_type = 'float';
}

/**
* Called by HTML_QuickForm whenever form event is made on this element.
*
* @param string $event Name of event
* @param mixed $arg event arguments
* @param object $caller calling object
* @return bool
*/
public function onQuickFormEvent($event, $arg, &$caller) {
switch ($event) {
case 'updateValue':
if ($value = $this->_findValue($caller->_constantValues)) {
$value = $this->format_float($value);
}
if (null === $value) {
$value = $this->_findValue($caller->_submitValues);
if (null === $value) {
if ($value = $this->_findValue($caller->_defaultValues)) {
$value = $this->format_float($value);
}
}
}
if (null !== $value) {
parent::setValue($value);
}
return true;
case 'createElement':
$caller->setType($arg[0], PARAM_RAW_TRIMMED);
default:
return parent::onQuickFormEvent($event, $arg, $caller);
}
}

/**
* Checks that the submitted value is a valid float number.
*
* @param string $value The localised float number that is submitted.
* @return string|null Validation error message or null.
*/
public function validateSubmitValue($value) {
if (false === unformat_float($value, true)) {
return get_string('err_numeric', 'core_form');
}
}

/**
* Sets the value of the form element.
*
* @param string $value Default value of the form element
*/
public function setValue($value) {
$value = $this->format_float($value);
parent::setValue($value);
}

/**
* Returns the value of the form element.
*
* @return false|float
*/
public function getValue() {
$value = parent::getValue();
if ($value) {
$value = unformat_float($value, true);
}
return $value;
}

/**
* Returns a 'safe' element's value.
*
* @param array $submitValues array of submitted values to search
* @param bool $assoc whether to return the value as associative array
* @return mixed
*/
public function exportValue(&$submitValues, $assoc = false) {
$value = $this->_findValue($submitValues);
if (null === $value) {
$value = $this->getValue();
} else if ($value) {
$value = unformat_float($value, true);
}
return $this->_prepareValue($value, $assoc);
}

/**
* Used by getFrozenHtml() to pass the element's value if _persistantFreeze is on.
*
* @return string
*/
public function _getPersistantData() {
if (!$this->_persistantFreeze) {
return '';
} else {
$id = $this->getAttribute('id');
if (isset($id)) {
// Id of persistant input is different then the actual input.
$id = array('id' => $id . '_persistant');
} else {
$id = array();
}

return '<input' . $this->_getAttrString(array(
'type' => 'hidden',
'name' => $this->getAttribute('name'),
'value' => $this->getAttribute('value')
) + $id) . ' />';
}
}

/**
* Given a float, prints it nicely.
* This function reserves the number of decimal places.
*
* @param float|null $value The float number to format
* @return string Localised float
*/
private function format_float($value) {
if (is_numeric($value)) {
if ($value > 0) {
$decimals = strlen($value) - strlen(floor($value)) - 1;
} else {
$decimals = strlen($value) - strlen(ceil($value)) - 1;
}
$value = format_float($value, $decimals);
}
return $value;
}
}
18 changes: 18 additions & 0 deletions lib/form/group.php
Original file line number Diff line number Diff line change
Expand Up @@ -257,4 +257,22 @@ public function accept(&$renderer, $required = false, $error = null) {
}
$renderer->finishGroup($this);
}

/**
* Calls the validateSubmitValue function for the containing elements and returns an error string as soon as it finds one.
*
* @param array $values Values of the containing elements.
* @return string|null Validation error message or null.
*/
public function validateSubmitValue($values) {
foreach ($this->_elements as $element) {
if (method_exists($element, 'validateSubmitValue')) {
$value = $values[$element->getName()] ?? null;
$result = $element->validateSubmitValue($value);
if (!empty($result) && is_string($result)) {
return $result;
}
}
}
}
}
99 changes: 99 additions & 0 deletions lib/form/tests/float_test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<?php
// 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 <http://www.gnu.org/licenses/>.

/**
* Unit tests for float form element.
*
* @package core_form
* @category test
* @copyright 2019 Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/

defined('MOODLE_INTERNAL') || die();

global $CFG;
require_once($CFG->libdir . '/form/float.php');

/**
* Unit tests for MoodleQuickForm_float
*
* Contains test cases for testing MoodleQuickForm_float
*
* @package core_form
* @copyright 2019 Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class core_form_float_testcase extends advanced_testcase {

/**
* Define a local decimal separator.
*
* It is not possible to directly change the result of get_string in
* a unit test. Instead, we create a language pack for language 'xx' in
* dataroot and make langconfig.php with the string we need to change.
* The example separator used here is 'X'.
*/
protected function define_local_decimal_separator() {
global $SESSION, $CFG;

$SESSION->lang = 'xx';
$langconfig = "<?php\n\$string['decsep'] = 'X';";
$langfolder = $CFG->dataroot . '/lang/xx';
check_dir_exists($langfolder);
file_put_contents($langfolder . '/langconfig.php', $langconfig);
}

/**
* Testcase to check generated timestamp
*/
public function test_exportValue() {
$element = new MoodleQuickForm_float('testel');

$value = ['testel' => 3.14];
$this->assertEquals(3.14, $element->exportValue($value));

$value = ['testel' => '3.14'];
$this->assertEquals(3.14, $element->exportValue($value));

$value = ['testel' => '-3.14'];
$this->assertEquals(-3.14, $element->exportValue($value));

$value = ['testel' => '3.14blah'];
$this->assertEquals(false, $element->exportValue($value));

$value = ['testel' => 'blah'];
$this->assertEquals(false, $element->exportValue($value));

// Tests with a localised decimal separator.
$this->define_local_decimal_separator();

$value = ['testel' => 3.14];
$this->assertEquals(3.14, $element->exportValue($value));

$value = ['testel' => '3X14'];
$this->assertEquals(3.14, $element->exportValue($value));

$value = ['testel' => '-3X14'];
$this->assertEquals(-3.14, $element->exportValue($value));

$value = ['testel' => '3X14blah'];
$this->assertEquals(false, $element->exportValue($value));

$value = ['testel' => 'blah'];
$this->assertEquals(false, $element->exportValue($value));
}
}
1 change: 1 addition & 0 deletions lib/formslib.php
Original file line number Diff line number Diff line change
Expand Up @@ -3253,6 +3253,7 @@ function getValidationScript($format = null) {
MoodleQuickForm::registerElementType('filemanager', "$CFG->libdir/form/filemanager.php", 'MoodleQuickForm_filemanager');
MoodleQuickForm::registerElementType('filepicker', "$CFG->libdir/form/filepicker.php", 'MoodleQuickForm_filepicker');
MoodleQuickForm::registerElementType('filetypes', "$CFG->libdir/form/filetypes.php", 'MoodleQuickForm_filetypes');
MoodleQuickForm::registerElementType('float', "$CFG->libdir/form/float.php", 'MoodleQuickForm_float');
MoodleQuickForm::registerElementType('grading', "$CFG->libdir/form/grading.php", 'MoodleQuickForm_grading');
MoodleQuickForm::registerElementType('group', "$CFG->libdir/form/group.php", 'MoodleQuickForm_group');
MoodleQuickForm::registerElementType('header', "$CFG->libdir/form/header.php", 'MoodleQuickForm_header');
Expand Down
17 changes: 12 additions & 5 deletions lib/moodlelib.php
Original file line number Diff line number Diff line change
Expand Up @@ -137,14 +137,17 @@
*
* Note that you should not use PARAM_FLOAT for numbers typed in by the user.
* It does not work for languages that use , as a decimal separator.
* Instead, do something like
* $rawvalue = required_param('name', PARAM_RAW);
* // ... other code including require_login, which sets current lang ...
* $realvalue = unformat_float($rawvalue);
* // ... then use $realvalue
* Use PARAM_LOCALISEDFLOAT instead.
*/
define('PARAM_FLOAT', 'float');

/**
* PARAM_LOCALISEDFLOAT - a localised real/floating point number.
* This is preferred over PARAM_FLOAT for numbers typed in by the user.
* Cleans localised numbers to computer readable numbers; false for invalid numbers.
*/
define('PARAM_LOCALISEDFLOAT', 'localisedfloat');

/**
* PARAM_HOST - expected fully qualified domain name (FQDN) or an IPv4 dotted quad (IP address)
*/
Expand Down Expand Up @@ -843,6 +846,10 @@ function clean_param($param, $type) {
// Convert to float.
return (float)$param;

case PARAM_LOCALISEDFLOAT:
// Convert to float.
return unformat_float($param, true);

case PARAM_ALPHA:
// Remove everything not `a-z`.
return preg_replace('/[^a-zA-Z]/i', '', $param);
Expand Down
2 changes: 1 addition & 1 deletion lib/questionlib.php
Original file line number Diff line number Diff line change
Expand Up @@ -806,7 +806,7 @@ function question_preview_url($questionid, $preferredbehaviour = null,
}

if (!is_null($maxmark)) {
$params['maxmark'] = $maxmark;
$params['maxmark'] = format_float($maxmark, strlen($maxmark), true, true);
}

if (!is_null($displayoptions)) {
Expand Down
Loading

0 comments on commit eee677c

Please sign in to comment.