Add support for earliest/latest date in DateFormField
authorMatthias Schmidt <gravatronics@live.com>
Sun, 30 Jun 2019 12:01:25 +0000 (14:01 +0200)
committerMatthias Schmidt <gravatronics@live.com>
Sun, 30 Jun 2019 12:01:25 +0000 (14:01 +0200)
See #2309

com.woltlab.wcf/templates/__dateFormField.tpl
wcfsetup/install/files/acp/templates/__dateFormField.tpl
wcfsetup/install/files/lib/system/form/builder/field/DateFormField.class.php
wcfsetup/install/lang/de.xml
wcfsetup/install/lang/en.xml

index ec69fd6e559600bbce32e2d17e8981fccf0e90b4..3fe9d8d6beefb669ac4d2ed92f112908717b414d 100644 (file)
@@ -9,6 +9,8 @@
        *}{if $field->isAutofocused()} autofocus{/if}{*
        *}{if $field->isRequired()} required{/if}{*
        *}{if $field->isImmutable()} disabled{/if}{*
+       *}{if $field->getEarliestDate() !== null} min="{$dateFormFieldEarliestDate}"{/if}{*
+       *}{if $field->getLatestDate() !== null} max="{$dateFormFieldLatestDate}"{/if}{*
 *}>
 
 {include file='__formFieldFooter'}
index ec69fd6e559600bbce32e2d17e8981fccf0e90b4..3fe9d8d6beefb669ac4d2ed92f112908717b414d 100644 (file)
@@ -9,6 +9,8 @@
        *}{if $field->isAutofocused()} autofocus{/if}{*
        *}{if $field->isRequired()} required{/if}{*
        *}{if $field->isImmutable()} disabled{/if}{*
+       *}{if $field->getEarliestDate() !== null} min="{$dateFormFieldEarliestDate}"{/if}{*
+       *}{if $field->getLatestDate() !== null} max="{$dateFormFieldLatestDate}"{/if}{*
 *}>
 
 {include file='__formFieldFooter'}
index 84dcbd2363b009bec0fedbf61bb99f8d21ee63b7..6e1398477badea9f1f71c2636082557335d7d68d 100644 (file)
@@ -1,10 +1,11 @@
 <?php
 namespace wcf\system\form\builder\field;
 use wcf\system\form\builder\field\validation\FormFieldValidationError;
+use wcf\system\WCF;
 use wcf\util\DateUtil;
 
 /**
- * Implementation of a form field for to select a FontAwesome icon.
+ * Implementation of a form field for a date (with a time).
  * 
  * @author     Matthias Schmidt
  * @copyright  2001-2019 WoltLab GmbH
@@ -17,11 +18,25 @@ class DateFormField extends AbstractFormField implements IAutoFocusFormField, II
        use TImmutableFormField;
        use TNullableFormField;
        
+       /**
+        * earliest valid date in `DateFormField::$saveValueFormat` format or `null` if no earliest
+        * valid date has been set
+        * @var null|string|int
+        */
+       protected $earliestDate;
+       
        /**
         * @inheritDoc
         */
        protected $javaScriptDataHandlerModule = 'WoltLabSuite/Core/Form/Builder/Field/Date';
        
+       /**
+        * latest valid date in `DateFormField::$saveValueFormat` format or `null` if no latest valid
+        * date has been set
+        * @var null|string|int
+        */
+       protected $latestDate;
+       
        /**
         * date time format of the save value
         * @var string
@@ -39,6 +54,93 @@ class DateFormField extends AbstractFormField implements IAutoFocusFormField, II
         */
        protected $templateName = '__dateFormField';
        
+       const DATE_FORMAT = 'Y-m-d';
+       const TIME_FORMAT = 'Y-m-d\TH:i:sP';
+       
+       /**
+        * Sets the earliest valid date in `DateFormField::$saveValueFormat` format and returns this
+        * field. If `null` is given, the previously set earliest valid date is unset.
+        * 
+        * @param       null|string|int         $earliestDate
+        * @return      static
+        */
+       public function earliestDate($earliestDate = null) {
+               $this->earliestDate = $earliestDate;
+               
+               if ($this->earliestDate !== null) {
+                       $earliestDateTime = \DateTime::createFromFormat($this->getSaveValueFormat(), $this->earliestDate);
+                       if ($earliestDateTime === false) {
+                               throw new \InvalidArgumentException("Earliest date '{$this->earliestDate}' does not have save value format '{$this->getSaveValueFormat()}'.");
+                       }
+                       
+                       if ($this->getLatestDate() !== null) {
+                               $latestDateTime = \DateTime::createFromFormat($this->getSaveValueFormat(), $this->getLatestDate());
+                               
+                               if ($latestDateTime < $earliestDateTime) {
+                                       throw new \InvalidArgumentException("Earliest date '{$this->earliestDate}' cannot be later than latest date '{$this->getLatestDate()}'.");
+                               }
+                       }
+               }
+               
+               return $this;
+       }
+       
+       /**
+        * Returns the earliest valid date in `DateFormField::getSaveValueFormat()` format.
+        * 
+        * If no earliest valid date has been set, `null` is returned.
+        * 
+        * @return      null|string|int
+        */
+       public function getEarliestDate() {
+               return $this->earliestDate;
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function getHtmlVariables() {
+               // the date picker JavaScript code requires the `min` and `max` value to have a
+               // specific format which is easier to create in PHP than in the template itself
+               
+               $format = static::DATE_FORMAT;
+               if ($this->supportsTime()) {
+                       $format = static::TIME_FORMAT; 
+               }
+               
+               $formattedEarliestDate = '';
+               if ($this->getEarliestDate() !== null) {
+                       $formattedEarliestDate = \DateTime::createFromFormat(
+                               $this->getSaveValueFormat(),
+                               $this->getEarliestDate()
+                       )->format($format);
+               }
+               
+               $formattedLatestDate = '';
+               if ($this->getLatestDate() !== null) {
+                       $formattedLatestDate = \DateTime::createFromFormat(
+                               $this->getSaveValueFormat(),
+                               $this->getLatestDate()
+                       )->format($format);
+               }
+               
+               return [
+                       'dateFormFieldEarliestDate' => $formattedEarliestDate,
+                       'dateFormFieldLatestDate' => $formattedLatestDate
+               ];
+       }
+       
+       /**
+        * Returns the latest valid date in `DateFormField::getSaveValueFormat()` format.
+        * 
+        * If no latest valid date has been set, `null` is returned.
+        * 
+        * @return      null|string|int
+        */
+       public function getLatestDate() {
+               return $this->latestDate;
+       }
+       
        /**
         * Returns the type of the returned save value.
         * 
@@ -62,10 +164,10 @@ class DateFormField extends AbstractFormField implements IAutoFocusFormField, II
         */
        protected function getValueDateTimeObject() {
                if ($this->supportsTime()) {
-                       $dateTime = \DateTime::createFromFormat('Y-m-d\TH:i:sP', $this->getValue());
+                       $dateTime = \DateTime::createFromFormat(static::TIME_FORMAT, $this->getValue());
                }
                else {
-                       $dateTime = \DateTime::createFromFormat('Y-m-d', $this->getValue());
+                       $dateTime = \DateTime::createFromFormat(static::DATE_FORMAT, $this->getValue());
                }
                
                if ($dateTime === false) {
@@ -91,6 +193,35 @@ class DateFormField extends AbstractFormField implements IAutoFocusFormField, II
                return $this->getValueDateTimeObject()->format($this->getSaveValueFormat());
        }
        
+       /**
+        * Sets the latest valid date in `DateFormField::$saveValueFormat` format and returns this
+        * field. If `null` is given, the previously set latest valid date is unset.
+        *
+        * @param       null|string|int         $latestDate
+        * @return      static
+        */
+       public function latestDate($latestDate = null) {
+               $this->latestDate = $latestDate;
+               
+               if ($this->latestDate !== null) {
+                       $latestDateTime = \DateTime::createFromFormat($this->getSaveValueFormat(), $this->latestDate);
+                       
+                       if ($latestDateTime === false) {
+                               throw new \InvalidArgumentException("Latest date '{$this->latestDate}' does not have save value format '{$this->getSaveValueFormat()}'.");
+                       }
+                       
+                       if ($this->getEarliestDate() !== null) {
+                               $earliestDateTime = \DateTime::createFromFormat($this->getSaveValueFormat(), $this->getEarliestDate());
+                               
+                               if ($latestDateTime < $earliestDateTime) {
+                                       throw new \InvalidArgumentException("Latest date '{$this->latestDate}' cannot be earlier than earliest date '{$this->getEarliestDate()}'.");
+                               }
+                       }
+               }
+               
+               return $this;
+       }
+       
        /**
         * @inheritDoc
         */
@@ -117,13 +248,6 @@ class DateFormField extends AbstractFormField implements IAutoFocusFormField, II
                        throw new \BadMethodCallException("Save value type has already been set.");
                }
                
-               try {
-                       \DateTime::createFromFormat($saveValueFormat, TIME_NOW);
-               }
-               catch (\Exception $e) {
-                       throw new \InvalidArgumentException("Invalid date time format '{$saveValueFormat}'.", 0, $e);
-               }
-               
                $this->saveValueFormat = $saveValueFormat;
                
                return $this;
@@ -161,12 +285,59 @@ class DateFormField extends AbstractFormField implements IAutoFocusFormField, II
                        }
                }
                else {
-                       if ($this->getValueDateTimeObject() === null) {
+                       $dateTime = $this->getValueDateTimeObject();
+                       if ($dateTime === null) {
                                $this->addValidationError(new FormFieldValidationError(
                                        'format',
                                        'wcf.form.field.date.error.format'
                                ));
                        }
+                       else if ($this->getEarliestDate() !== null) {
+                               $earliestDateTime = \DateTime::createFromFormat($this->getSaveValueFormat(), $this->getEarliestDate());
+                               
+                               if ($dateTime < $earliestDateTime) {
+                                       $format = DateUtil::DATE_FORMAT;
+                                       if ($this->supportsTime()) {
+                                               $format = str_replace(
+                                                       ['%date%', '%time%'],
+                                                       [
+                                                               WCF::getLanguage()->get(DateUtil::DATE_FORMAT),
+                                                               WCF::getLanguage()->get(DateUtil::TIME_FORMAT)
+                                                       ],
+                                                       WCF::getLanguage()->get('wcf.date.dateTimeFormat')
+                                               );
+                                       }
+                                       
+                                       $this->addValidationError(new FormFieldValidationError(
+                                               'minimum',
+                                               'wcf.form.field.date.error.earliestDate',
+                                               ['earliestDate' => DateUtil::format($earliestDateTime, $format)]
+                                       ));
+                               }
+                       }
+                       else if ($this->getLatestDate() !== null) {
+                               $latestDateTime = \DateTime::createFromFormat($this->getSaveValueFormat(), $this->getLatestDate());
+                               
+                               if ($dateTime > $latestDateTime) {
+                                       $format = DateUtil::DATE_FORMAT;
+                                       if ($this->supportsTime()) {
+                                               $format = str_replace(
+                                                       ['%date%', '%time%'],
+                                                       [
+                                                               WCF::getLanguage()->get(DateUtil::DATE_FORMAT),
+                                                               WCF::getLanguage()->get(DateUtil::TIME_FORMAT)
+                                                       ],
+                                                       WCF::getLanguage()->get('wcf.date.dateTimeFormat')
+                                               );
+                                       }
+                                       
+                                       $this->addValidationError(new FormFieldValidationError(
+                                               'minimum',
+                                               'wcf.form.field.date.error.latestDate',
+                                               ['latestDateTime' => DateUtil::format($latestDateTime, $format)]
+                                       ));
+                               }
+                       }
                }
        }
        
index 42c82e02eda90ef7d2c175ccd979b7752478b053..6aac3554b12eebaf9348a00e9808c5a019732f63 100644 (file)
@@ -3908,6 +3908,8 @@ Dateianhänge:
                <item name="wcf.form.field.rating.ratingTitle"><![CDATA[{#$rating}/{#$maximumRating}]]></item>
                <item name="wcf.form.field.rating.removeRating"><![CDATA[Bewertung zurücksetzen]]></item>
                <item name="wcf.form.field.date.error.format"><![CDATA[Der angegebene Wert hat ein ungültiges Format.]]></item>
+               <item name="wcf.form.field.date.error.earliestDate"><![CDATA[Der angegebene Wert darf nicht früher sein als {$earliestDate}.]]></item>
+               <item name="wcf.form.field.date.error.latestDate"><![CDATA[Der angegebene Wert darf nicht später sein als {$latestDate}.]]></item>
        </category>
        <category name="wcf.image">
                <item name="wcf.image.coverPhoto"><![CDATA[Titelbild]]></item>
index a0a0281b5faea2cbb314393536e0e38045d3a662..5a84208943148580aed7ed951113f66d056d9682 100644 (file)
@@ -3854,6 +3854,8 @@ Attachments:
                <item name="wcf.form.field.rating.ratingTitle"><![CDATA[{#$rating}/{#$maximumRating}]]></item>
                <item name="wcf.form.field.rating.removeRating"><![CDATA[Reset Rating]]></item>
                <item name="wcf.form.field.date.error.format"><![CDATA[The format of the entered value is invalid.]]></item>
+               <item name="wcf.form.field.date.error.earliestDate"><![CDATA[The entered value may not be earlier than {$earliestDate}.]]></item>
+               <item name="wcf.form.field.date.error.latestDate"><![CDATA[The entered value may not be later than {$latestDate}.]]></item>
        </category>
        <category name="wcf.image">
                <item name="wcf.image.coverPhoto"><![CDATA[Cover Photo]]></item>