diff --git a/docs/DataStructure.md b/docs/DataStructure.md index 29588473d..f1d1361db 100644 --- a/docs/DataStructure.md +++ b/docs/DataStructure.md @@ -229,3 +229,6 @@ Optional extra settings for some [Question Types](#question-types) | `dateMax` | `date` | Integer | - | Maximum allowed date to be chosen (as Unix timestamp) | | `dateMin` | `date` | Integer | - | Minimum allowed date to be chosen (as Unix timestamp) | | `dateRange` | `date` | Boolean | `true/false` | The date picker should query a date range | +| `timeMin` | `time` | Integer | - | Maximum allowed time to be chosen (as `HH:mm` string) | +| `timeMax` | `time` | Integer | - | Minimum allowed time to be chosen (as `HH:mm` string) | +| `timeRange` | `time` | Boolean | `true/false` | The time picker should query a time range | diff --git a/img/clock_arrow_down.svg b/img/clock_arrow_down.svg new file mode 100644 index 000000000..d761ef15c --- /dev/null +++ b/img/clock_arrow_down.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/img/clock_arrow_up.svg b/img/clock_arrow_up.svg new file mode 100644 index 000000000..b6dd1b58c --- /dev/null +++ b/img/clock_arrow_up.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/lib/Constants.php b/lib/Constants.php index cdee36930..a18ebc2e8 100644 --- a/lib/Constants.php +++ b/lib/Constants.php @@ -153,6 +153,12 @@ class Constants { 'dateRange' => ['boolean', 'NULL'], ]; + public const EXTRA_SETTINGS_TIME = [ + 'timeMax' => ['string', 'NULL'], + 'timeMin' => ['string', 'NULL'], + 'timeRange' => ['boolean', 'NULL'], + ]; + // should be in sync with FileTypes.js public const EXTRA_SETTINGS_ALLOWED_FILE_TYPES = [ 'image', diff --git a/lib/ResponseDefinitions.php b/lib/ResponseDefinitions.php index c87c0d274..d92124aca 100644 --- a/lib/ResponseDefinitions.php +++ b/lib/ResponseDefinitions.php @@ -31,6 +31,9 @@ * optionsLimitMax?: int, * optionsLimitMin?: int, * shuffleOptions?: bool, + * timeMax?: string, + * timeMin?: string, + * timeRange?: bool, * validationRegex?: string, * validationType?: string * } diff --git a/lib/Service/FormsService.php b/lib/Service/FormsService.php index db0a547b5..12e268b62 100644 --- a/lib/Service/FormsService.php +++ b/lib/Service/FormsService.php @@ -632,6 +632,9 @@ public function areExtraSettingsValid(array $extraSettings, string $questionType case Constants::ANSWER_TYPE_DATE: $allowed = Constants::EXTRA_SETTINGS_DATE; break; + case Constants::ANSWER_TYPE_TIME: + $allowed = Constants::EXTRA_SETTINGS_TIME; + break; default: $allowed = []; } @@ -656,6 +659,12 @@ public function areExtraSettingsValid(array $extraSettings, string $questionType && $extraSettings['dateMin'] > $extraSettings['dateMax']) { return false; } + } elseif ($questionType === Constants::ANSWER_TYPE_TIME) { + // Ensure timeMin and timeMax don't overlap + if (isset($extraSettings['timeMin']) && isset($extraSettings['timeMax']) + && $extraSettings['timeMin'] > $extraSettings['timeMax']) { + return false; + } } elseif ($questionType === Constants::ANSWER_TYPE_MULTIPLE) { // Ensure limits are sane if (isset($extraSettings['optionsLimitMax']) && isset($extraSettings['optionsLimitMin']) diff --git a/lib/Service/SubmissionService.php b/lib/Service/SubmissionService.php index baea59590..7c5190251 100644 --- a/lib/Service/SubmissionService.php +++ b/lib/Service/SubmissionService.php @@ -457,15 +457,17 @@ private function validateDateTime(array $answers, string $format, ?string $text } if ($previousDate !== null && $d < $previousDate) { - throw new \InvalidArgumentException(sprintf('Dates for question "%s" must be in ascending order.', $text)); + throw new \InvalidArgumentException(sprintf('Dates/times for question "%s" must be in ascending order.', $text)); } $previousDate = $d; if ($extraSettings) { if ((isset($extraSettings['dateMin']) && $d < (new DateTime())->setTimestamp($extraSettings['dateMin'])) || - (isset($extraSettings['dateMax']) && $d > (new DateTime())->setTimestamp($extraSettings['dateMax'])) + (isset($extraSettings['dateMax']) && $d > (new DateTime())->setTimestamp($extraSettings['dateMax'])) || + (isset($extraSettings['timeMin']) && $d > (new DateTime())->setTimestamp($extraSettings['timeMin'])) || + (isset($extraSettings['timeMax']) && $d > (new DateTime())->setTimestamp($extraSettings['timeMax'])) ) { - throw new \InvalidArgumentException(sprintf('Date is not in the allowed range for question "%s".', $text)); + throw new \InvalidArgumentException(sprintf('Date/time is not in the allowed range for question "%s".', $text)); } } } diff --git a/openapi.json b/openapi.json index 0dc887668..17f99de10 100644 --- a/openapi.json +++ b/openapi.json @@ -448,6 +448,15 @@ "shuffleOptions": { "type": "boolean" }, + "timeMax": { + "type": "string" + }, + "timeMin": { + "type": "string" + }, + "timeRange": { + "type": "boolean" + }, "validationRegex": { "type": "string" }, diff --git a/src/components/Questions/QuestionDate.vue b/src/components/Questions/QuestionDate.vue index ea0ff51a8..65bc4977a 100644 --- a/src/components/Questions/QuestionDate.vue +++ b/src/components/Questions/QuestionDate.vue @@ -42,6 +42,39 @@ +