MDL-46542 formslib: duration field option for which units to show
authorTim Hunt <T.J.Hunt@open.ac.uk>
Mon, 28 Jul 2014 14:01:23 +0000 (10:01 -0400)
committerTim Hunt <T.J.Hunt@open.ac.uk>
Thu, 30 Jan 2020 17:08:29 +0000 (17:08 +0000)
Based on the original work of Itamar Tzadok.

lib/form/duration.php
lib/form/tests/duration_test.php

index 91ed768..ee04ff0 100644 (file)
@@ -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) {
index af5bba5..4ce9280 100644 (file)
@@ -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('<option value="60" selected>minutes</option>', $html);
+        $this->assertContains('<option value="1">seconds</option>', $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));
-
-        $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.
+        [$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));
+
+        $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