From 87554981f0ec10cc076bfa345cfd13c5c37a39e0 Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Mon, 28 Jul 2014 10:01:23 -0400 Subject: [PATCH] MDL-46542 formslib: duration field option for which units to show Based on the original work of Itamar Tzadok. --- lib/form/duration.php | 86 +++++++++++++++------ lib/form/tests/duration_test.php | 126 +++++++++++++++++-------------- 2 files changed, 133 insertions(+), 79 deletions(-) diff --git a/lib/form/duration.php b/lib/form/duration.php index 91ed7686450..ee04ff07eaf 100644 --- a/lib/form/duration.php +++ b/lib/form/duration.php @@ -42,15 +42,15 @@ require_once($CFG->libdir . '/form/text.php'); * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class MoodleQuickForm_duration extends MoodleQuickForm_group { - /** - * Control the fieldnames for form elements - * optional => if true, show a checkbox beside the element to turn it on (or off) - * @var array - */ - protected $_options = array('optional' => false, 'defaultunit' => 60); + /** + * Control the fieldnames for form elements + * optional => if true, show a checkbox beside the element to turn it on (or off) + * @var array + */ + protected $_options = array('optional' => false, 'defaultunit' => MINSECS); - /** @var array associative array of time units (days, hours, minutes, seconds) */ - private $_units = null; + /** @var array associative array of time units (days, hours, minutes, seconds) */ + private $_units = null; /** * constructor @@ -58,12 +58,15 @@ class MoodleQuickForm_duration extends MoodleQuickForm_group { * @param string $elementName Element's name * @param mixed $elementLabel Label(s) for an element * @param array $options Options to control the element's display. Recognised values are - * 'optional' => true/false - whether to display an 'enabled' checkbox next to the element. - * 'defaultunit' => 1|60|3600|86400|604800 - the default unit to display when the time is blank. - * If not specified, minutes is used. + * 'optional' => true/false - whether to display an 'enabled' checkbox next to the element. + * 'defaultunit' => 1|MINSECS|HOURSECS|DAYSECS|WEEKSECS - the default unit to display when + * the time is blank. If not specified, minutes is used. + * 'units' => array containing some or all of 1, MINSECS, HOURSECS, DAYSECS and WEEKSECS + * which unit choices to offer. * @param mixed $attributes Either a typical HTML attribute string or an associative array */ - public function __construct($elementName = null, $elementLabel = null, $options = array(), $attributes = null) { + public function __construct($elementName = null, $elementLabel = null, + $options = array(), $attributes = null) { // TODO MDL-52313 Replace with the call to parent::__construct(). HTML_QuickForm_element::__construct($elementName, $elementLabel, $attributes); $this->_persistantFreeze = true; @@ -82,6 +85,24 @@ class MoodleQuickForm_duration extends MoodleQuickForm_group { } $this->_options['defaultunit'] = $options['defaultunit']; } + if (isset($options['units'])) { + if (!is_array($options['units'])) { + throw new coding_exception( + 'When creating a duration form field, units option must be an array.'); + } + // Validate and register requested units. + $availableunits = $this->get_units(); + $displayunits = []; + foreach ($options['units'] as $requestedunit) { + if (!isset($availableunits[$requestedunit])) { + throw new coding_exception($requestedunit . + ' is not a recognised unit in MoodleQuickForm_duration.'); + } + $displayunits[$requestedunit] = $availableunits[$requestedunit]; + } + krsort($displayunits, SORT_NUMERIC); + $this->_options['units'] = $displayunits; + } } /** @@ -89,7 +110,8 @@ class MoodleQuickForm_duration extends MoodleQuickForm_group { * * @deprecated since Moodle 3.1 */ - public function MoodleQuickForm_duration($elementName = null, $elementLabel = null, $options = array(), $attributes = null) { + public function MoodleQuickForm_duration($elementName = null, $elementLabel = null, + $options = array(), $attributes = null) { debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER); self::__construct($elementName, $elementLabel, $options, $attributes); } @@ -102,19 +124,34 @@ class MoodleQuickForm_duration extends MoodleQuickForm_group { public function get_units() { if (is_null($this->_units)) { $this->_units = array( - 604800 => get_string('weeks'), - 86400 => get_string('days'), - 3600 => get_string('hours'), - 60 => get_string('minutes'), + WEEKSECS => get_string('weeks'), + DAYSECS => get_string('days'), + HOURSECS => get_string('hours'), + MINSECS => get_string('minutes'), 1 => get_string('seconds'), ); } return $this->_units; } + /** + * Get the units to be used for this field. + * + * The ones specified in the options passed to the constructor, or all by default. + * + * @return array number of seconds => lang string. + */ + protected function get_units_used() { + if (!empty($this->_options['units'])) { + return $this->_options['units']; + } else { + return $this->get_units(); + } + } + /** * Converts seconds to the best possible time unit. for example - * 1800 -> array(30, 60) = 30 minutes. + * 1800 -> [30, MINSECS] = 30 minutes. * * @param int $seconds an amout of time in seconds. * @return array associative array ($number => $unit) @@ -123,7 +160,7 @@ class MoodleQuickForm_duration extends MoodleQuickForm_group { if ($seconds == 0) { return array(0, $this->_options['defaultunit']); } - foreach ($this->get_units() as $unit => $notused) { + foreach ($this->get_units_used() as $unit => $notused) { if (fmod($seconds, $unit) == 0) { return array($seconds / $unit, $unit); } @@ -144,14 +181,17 @@ class MoodleQuickForm_duration extends MoodleQuickForm_group { } $this->_elements = array(); // E_STRICT creating elements without forms is nasty because it internally uses $this - $number = $this->createFormElement('text', 'number', get_string('time', 'form'), $attributes, true); + $number = $this->createFormElement('text', 'number', + get_string('time', 'form'), $attributes, true); $number->set_force_ltr(true); $this->_elements[] = $number; unset($attributes['size']); - $this->_elements[] = $this->createFormElement('select', 'timeunit', get_string('timeunit', 'form'), $this->get_units(), $attributes, true); + $this->_elements[] = $this->createFormElement('select', 'timeunit', + get_string('timeunit', 'form'), $this->get_units_used(), $attributes, true); // If optional we add a checkbox which the user can use to turn if on if($this->_options['optional']) { - $this->_elements[] = $this->createFormElement('checkbox', 'enabled', null, get_string('enable'), $this->getAttributes(), true); + $this->_elements[] = $this->createFormElement('checkbox', 'enabled', null, + get_string('enable'), $this->getAttributes(), true); } foreach ($this->_elements as $element){ if (method_exists($element, 'setHiddenLabel')){ @@ -165,7 +205,7 @@ class MoodleQuickForm_duration extends MoodleQuickForm_group { * * @param string $event Name of event * @param mixed $arg event arguments - * @param object $caller calling object + * @param MoodleQuickForm $caller calling object * @return bool */ function onQuickFormEvent($event, $arg, &$caller) { diff --git a/lib/form/tests/duration_test.php b/lib/form/tests/duration_test.php index af5bba5d521..4ce92804dd2 100644 --- a/lib/form/tests/duration_test.php +++ b/lib/form/tests/duration_test.php @@ -41,109 +41,122 @@ require_once($CFG->libdir . '/form/duration.php'); * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class core_form_duration_testcase extends basic_testcase { - /** @var MoodleQuickForm Keeps reference of dummy form object */ - private $mform; - /** @var MoodleQuickForm_duration Keeps reference of MoodleQuickForm_duration object */ - private $element; /** - * Initalize test wide variable, it is called in start of the testcase + * Get a form that can be used for testing. + * + * @return MoodleQuickForm */ - protected function setUp() { - parent::setUp(); - - // Get form data. + protected function get_test_form() { $form = new temp_form_duration(); - $this->mform = $form->getform(); - $this->element = $this->mform->addElement('duration', 'duration'); + return $form->getform(); } /** - * Clears the data set in the setUp() method call. - * @see duration_form_element_test::setUp() + * Get a form with a duration element that can be used for testing. + * + * @return array with two elements, a MoodleQuickForm and a MoodleQuickForm_duration. */ - protected function tearDown() { - $this->element = null; + protected function get_test_form_and_element() { + $mform = $this->get_test_form(); + $element = $mform->addElement('duration', 'duration'); + return [$mform, $element]; } /** * Testcase for testing contructor. + * * @expectedException coding_exception - * @retrun void */ public function test_constructor() { // Test trying to create with an invalid unit. - $this->element = $this->mform->addElement('duration', 'testel', null, array('defaultunit' => 123, 'optional' => false)); + $mform = $this->get_test_form(); + $mform->addElement('duration', 'testel', null, ['defaultunit' => 123, 'optional' => false]); + } + + /** + * Test contructor only some units. + */ + public function test_constructor_limited_units() { + $mform = $this->get_test_form(); + $mform->addElement('duration', 'testel', null, ['units' => [MINSECS, 1], 'optional' => false]); + $html = $mform->toHtml(); + $html = preg_replace('~ +>~', '>', $html); // Clean HTML to avoid spurious errors. + $this->assertContains('', $html); + $this->assertContains('', $html); + $this->assertNotContains('value="3600"', $html); } /** * Testcase for testing units (seconds, minutes, hours and days) */ public function test_get_units() { - $units = $this->element->get_units(); - ksort($units); - $this->assertEquals($units, array(1 => get_string('seconds'), 60 => get_string('minutes'), - 3600 => get_string('hours'), 86400 => get_string('days'), 604800 => get_string('weeks'))); + [$mform, $element] = $this->get_test_form_and_element(); + $units = $element->get_units(); + $this->assertEquals($units, [1 => get_string('seconds'), 60 => get_string('minutes'), + 3600 => get_string('hours'), 86400 => get_string('days'), 604800 => get_string('weeks')]); } /** * Testcase for testing conversion of seconds to the best possible unit */ public function test_seconds_to_unit() { - $this->assertEquals(array(0, 60), $this->element->seconds_to_unit(0)); // Zero minutes, for a nice default unit. - $this->assertEquals(array(1, 1), $this->element->seconds_to_unit(1)); - $this->assertEquals(array(3601, 1), $this->element->seconds_to_unit(3601)); - $this->assertEquals(array(1, 60), $this->element->seconds_to_unit(60)); - $this->assertEquals(array(3, 60), $this->element->seconds_to_unit(180)); - $this->assertEquals(array(1, 3600), $this->element->seconds_to_unit(3600)); - $this->assertEquals(array(2, 3600), $this->element->seconds_to_unit(7200)); - $this->assertEquals(array(1, 86400), $this->element->seconds_to_unit(86400)); - $this->assertEquals(array(25, 3600), $this->element->seconds_to_unit(90000)); + [$mform, $element] = $this->get_test_form_and_element(); + $this->assertEquals([0, MINSECS], $element->seconds_to_unit(0)); // Zero minutes, for a nice default unit. + $this->assertEquals([1, 1], $element->seconds_to_unit(1)); + $this->assertEquals([3601, 1], $element->seconds_to_unit(3601)); + $this->assertEquals([1, MINSECS], $element->seconds_to_unit(60)); + $this->assertEquals([3, MINSECS], $element->seconds_to_unit(180)); + $this->assertEquals([1, HOURSECS], $element->seconds_to_unit(3600)); + $this->assertEquals([2, HOURSECS], $element->seconds_to_unit(7200)); + $this->assertEquals([1, DAYSECS], $element->seconds_to_unit(86400)); + $this->assertEquals([25, HOURSECS], $element->seconds_to_unit(90000)); - $this->element = $this->mform->addElement('duration', 'testel', null, array('defaultunit' => 86400, 'optional' => false)); - $this->assertEquals(array(0, 86400), $this->element->seconds_to_unit(0)); // Zero minutes, for a nice default unit. + $element = $mform->addElement('duration', 'testel', null, + ['defaultunit' => DAYSECS, 'optional' => false]); + $this->assertEquals([0, DAYSECS], $element->seconds_to_unit(0)); // Zero minutes, for a nice default unit. } /** * Testcase to check generated timestamp */ public function test_exportValue() { - /** @var MoodleQuickForm_duration $el */ - $el = $this->mform->addElement('duration', 'testel'); - $values = array('testel' => array('number' => 10, 'timeunit' => 1)); - $this->assertEquals(array('testel' => 10), $el->exportValue($values, true)); + $mform = $this->get_test_form(); + $el = $mform->addElement('duration', 'testel'); + $values = ['testel' => ['number' => 10, 'timeunit' => 1]]; + $this->assertEquals(['testel' => 10], $el->exportValue($values, true)); $this->assertEquals(10, $el->exportValue($values)); - $values = array('testel' => array('number' => 3, 'timeunit' => 60)); - $this->assertEquals(array('testel' => 180), $el->exportValue($values, true)); + $values = ['testel' => ['number' => 3, 'timeunit' => MINSECS]]; + $this->assertEquals(['testel' => 180], $el->exportValue($values, true)); $this->assertEquals(180, $el->exportValue($values)); - $values = array('testel' => array('number' => 1.5, 'timeunit' => 60)); - $this->assertEquals(array('testel' => 90), $el->exportValue($values, true)); + $values = ['testel' => ['number' => 1.5, 'timeunit' => MINSECS]]; + $this->assertEquals(['testel' => 90], $el->exportValue($values, true)); $this->assertEquals(90, $el->exportValue($values)); - $values = array('testel' => array('number' => 2, 'timeunit' => 3600)); - $this->assertEquals(array('testel' => 7200), $el->exportValue($values, true)); + $values = ['testel' => ['number' => 2, 'timeunit' => HOURSECS]]; + $this->assertEquals(['testel' => 7200], $el->exportValue($values, true)); $this->assertEquals(7200, $el->exportValue($values)); - $values = array('testel' => array('number' => 1, 'timeunit' => 86400)); - $this->assertEquals(array('testel' => 86400), $el->exportValue($values, true)); + $values = ['testel' => ['number' => 1, 'timeunit' => DAYSECS]]; + $this->assertEquals(['testel' => 86400], $el->exportValue($values, true)); $this->assertEquals(86400, $el->exportValue($values)); - $values = array('testel' => array('number' => 0, 'timeunit' => 3600)); - $this->assertEquals(array('testel' => 0), $el->exportValue($values, true)); + $values = ['testel' => ['number' => 0, 'timeunit' => HOURSECS]]; + $this->assertEquals(['testel' => 0], $el->exportValue($values, true)); $this->assertEquals(0, $el->exportValue($values)); - $el = $this->mform->addElement('duration', 'testel', null, array('optional' => true)); - $values = array('testel' => array('number' => 10, 'timeunit' => 1)); - $this->assertEquals(array('testel' => 0), $el->exportValue($values, true)); + $el = $mform->addElement('duration', 'testel', null, ['optional' => true]); + $values = ['testel' => ['number' => 10, 'timeunit' => 1]]; + $this->assertEquals(['testel' => 0], $el->exportValue($values, true)); $this->assertEquals(0, $el->exportValue($values)); - $values = array('testel' => array('number' => 20, 'timeunit' => 1, 'enabled' => 1)); - $this->assertEquals(array('testel' => 20), $el->exportValue($values, true)); + $values = ['testel' => ['number' => 20, 'timeunit' => 1, 'enabled' => 1]]; + $this->assertEquals(['testel' => 20], $el->exportValue($values, true)); $this->assertEquals(20, $el->exportValue($values)); // Optional element. - $el2 = $this->mform->addElement('duration', 'testel', '', ['optional' => true]); - $values = array('testel' => array('number' => 10, 'timeunit' => 1, 'enabled' => 1)); - $this->assertEquals(array('testel' => 10), $el2->exportValue($values, true)); + $el2 = $mform->addElement('duration', 'testel', '', ['optional' => true]); + $values = ['testel' => ['number' => 10, 'timeunit' => 1, 'enabled' => 1]]; + $this->assertEquals(['testel' => 10], $el2->exportValue($values, true)); $this->assertEquals(10, $el2->exportValue($values)); - $values = array('testel' => array('number' => 10, 'timeunit' => 1, 'enabled' => 0)); - $this->assertEquals(array('testel' => 0), $el2->exportValue($values, true)); + $values = ['testel' => ['number' => 10, 'timeunit' => 1, 'enabled' => 0]]; + $this->assertEquals(['testel' => 0], $el2->exportValue($values, true)); $this->assertEquals(null, $el2->exportValue($values)); } } @@ -158,6 +171,7 @@ class temp_form_duration extends moodleform { public function definition() { // No definition required. } + /** * Returns form reference * @return MoodleQuickForm