diff --git a/docs/DataStructure.md b/docs/DataStructure.md index 28946a8a9..dc3e59d9f 100644 --- a/docs/DataStructure.md +++ b/docs/DataStructure.md @@ -307,6 +307,7 @@ Follow-up questions (subquestions) are questions of their own with additional pr | `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. @@ -368,6 +369,14 @@ All options in `optionIds` must be selected for the condition to match. `min` and `max` use the answer format of the question type and are optional. +#### file_uploaded (for file) + +```json +{ "type": "file_uploaded", "fileUploaded": true } +``` + +`"fileUploaded": false` matches when no file was uploaded. + ### Example A dropdown question with a branch of follow-up questions: diff --git a/lib/Constants.php b/lib/Constants.php index e46b83143..d98aa875e 100644 --- a/lib/Constants.php +++ b/lib/Constants.php @@ -245,6 +245,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'; /** * Question types that can have branches with follow-up questions in `extraSettings.branches`, @@ -259,6 +260,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 86568ec4d..4e8f8599f 100644 --- a/lib/Service/SubmissionService.php +++ b/lib/Service/SubmissionService.php @@ -1049,8 +1049,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; } @@ -1077,6 +1077,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" /> + + +