diff --git a/docs/DataStructure.md b/docs/DataStructure.md index 909706f04..a70715abf 100644 --- a/docs/DataStructure.md +++ b/docs/DataStructure.md @@ -311,6 +311,7 @@ Supported trigger types for conditional questions: | `date` | `date_range` | Date - range including its bounds (`YYYY-MM-DD`) | | `datetime` | `date_range` | Deprecated date and time (`YYYY-MM-DD HH:mm`) | | `time` | `date_range` | Time - range including its bounds (`HH:mm`) | +| `file` | `file_uploaded` | File - whether a file was uploaded | A branch is shown when any of its conditions matches. Several branches can be shown at once. @@ -372,6 +373,14 @@ All options in `optionIds` must be selected for the condition to match. `min` and `max` use the answer format of the trigger type and are optional. +#### file_uploaded (for file) + +```json +{ "type": "file_uploaded", "fileUploaded": true } +``` + +`"fileUploaded": false` matches when no file was uploaded. + ### Conditional Question Example A complete conditional question structure: diff --git a/lib/Constants.php b/lib/Constants.php index e4352a230..f156f6039 100644 --- a/lib/Constants.php +++ b/lib/Constants.php @@ -262,6 +262,7 @@ class Constants { public const CONDITION_TYPE_OPTION_SELECTED = 'option_selected'; public const CONDITION_TYPE_OPTIONS_COMBINATION = 'options_combination'; public const CONDITION_TYPE_DATE_RANGE = 'date_range'; + public const CONDITION_TYPE_FILE_UPLOADED = 'file_uploaded'; /** * Trigger types allowed for conditional questions @@ -274,6 +275,7 @@ class Constants { self::ANSWER_TYPE_DATE => [self::CONDITION_TYPE_DATE_RANGE], self::ANSWER_TYPE_DATETIME => [self::CONDITION_TYPE_DATE_RANGE], self::ANSWER_TYPE_TIME => [self::CONDITION_TYPE_DATE_RANGE], + self::ANSWER_TYPE_FILE => [self::CONDITION_TYPE_FILE_UPLOADED], ]; /** diff --git a/lib/Service/SubmissionService.php b/lib/Service/SubmissionService.php index 56623edfe..4a796402c 100644 --- a/lib/Service/SubmissionService.php +++ b/lib/Service/SubmissionService.php @@ -1131,8 +1131,8 @@ private function evaluateBranchConditions(string $triggerType, array $triggerAns // Option ids arrive as numbers or strings depending on the trigger component $selectedOptionIds = array_map('strval', array_filter($triggerAnswer, 'is_scalar')); - // An unanswered trigger activates no branch - if ($triggerAnswer === [] || ($triggerAnswer[0] ?? '') === '') { + // An unanswered trigger activates no branch, except "no file uploaded" for file triggers + if ($triggerType !== Constants::ANSWER_TYPE_FILE && ($triggerAnswer === [] || ($triggerAnswer[0] ?? '') === '')) { return false; } @@ -1159,6 +1159,14 @@ private function evaluateBranchConditions(string $triggerType, array $triggerAns } } return false; + case Constants::ANSWER_TYPE_FILE: + $hasFile = !empty($triggerAnswer); + foreach ($conditions as $condition) { + if (($condition['fileUploaded'] ?? true) === $hasFile) { + return true; + } + } + return false; case Constants::ANSWER_TYPE_DATE: case Constants::ANSWER_TYPE_DATETIME: case Constants::ANSWER_TYPE_TIME: diff --git a/src/components/Conditional/BranchConditionEditor.vue b/src/components/Conditional/BranchConditionEditor.vue index 9b3b938a7..26c5959c5 100644 --- a/src/components/Conditional/BranchConditionEditor.vue +++ b/src/components/Conditional/BranchConditionEditor.vue @@ -50,12 +50,24 @@ class="condition-date-input" /> + + +