PluginProbe
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder / 3.3.1
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder v3.3.1
3.3.1 V-3.3.0 3.2.2 3.2.1 3.2.0 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 V3.0.3 V3.0.2 -3.0.1 V_3.0.0 1.1.1 1.1.8 1.2 1.3 1.4 1.4.18 1.5.2 1.9 2.0 2.10.0 2.10.1 All 138 releases
← All changes | includes/Core/WorkFlow/Actions.php +138 -76 3.2.03.3.1 View file →
@@ -14,17 +14,45 @@
14 14 {
15 15 static::$_formID = $formId;
16 16 }
17 17
18 + /**
19 + * Resolve the field key a workflow action points at.
20 + *
21 + * Both arguments come straight out of stored workflow/form JSON, so neither shape is guaranteed
22 + * — hence the runtime checks rather than type hints.
23 + *
24 + * @param mixed $actionDetail one action row decoded from the workflow JSON
25 + * @param mixed $fieldData field map keyed by field name
26 + *
27 + * @return string|null null when the action points at a field the form no longer has
28 + */
29 + private static function resolveFieldKey($actionDetail, $fieldData)
30 + {
31 + if (!is_object($actionDetail) || !isset($actionDetail->field) || !\is_array($fieldData)) {
32 + return null;
33 + }
34 + $field = $actionDetail->field;
35 + if (!\is_string($field) && !\is_int($field)) {
36 + return null;
37 + }
38 + if (!isset($fieldData[$field]) || !\is_array($fieldData[$field]) || !isset($fieldData[$field]['key'])) {
39 + return null;
40 + }
41 + $key = $fieldData[$field]['key'];
42 +
43 + return \is_string($key) || \is_int($key) ? (string) $key : null;
44 + }
45 +
18 46 public function setValue($actionDetail, $fieldData, $fields)
19 47 {
20 - if (!empty($actionDetail->val)) {
21 - $actionValue = '';
22 - $fieldType = $fields->{$fieldData[$actionDetail->field]['key']}->typ;
23 - $evalMathExpr = preg_match('/month|date/', $fieldType);
24 - $fields->{$fieldData[$actionDetail->field]['key']}->val = '';
25 - $actionValue = Helper::replaceFieldWithValue($actionDetail->val, $fieldData, !(bool)$evalMathExpr);
26 - $fields->{$fieldData[$actionDetail->field]['key']}->val = $actionValue;
48 + $fk = self::resolveFieldKey($actionDetail, $fieldData);
49 + if (null !== $fk && isset($fields->{$fk}) && !empty($actionDetail->val)) {
50 + $fieldType = isset($fields->{$fk}->typ) ? $fields->{$fk}->typ : '';
51 + $evalMathExpr = preg_match('/month|date/', (string) $fieldType);
52 + $fields->{$fk}->val = '';
53 + $actionValue = Helper::replaceFieldWithValue($actionDetail->val, $fieldData, !(bool) $evalMathExpr);
54 + $fields->{$fk}->val = $actionValue;
27 55 $fieldData[$actionDetail->field]['value'] = $actionValue;
28 56 }
29 57 return [$fields, $fieldData];
30 58 }
@@ -31,13 +59,14 @@
31 59
32 60 public function getActionValue($actionDetail, $fieldData, $fields)
33 61 {
34 62 $actionValue = '';
35 - if (!empty($actionDetail->val)) {
36 - $fieldType = $fields->{$fieldData[$actionDetail->field]['key']}->typ;
37 - $evalMathExpr = preg_match('/month|date/', $fieldType);
38 - $fields->{$fieldData[$actionDetail->field]['key']}->val = '';
39 - $actionValue = Helper::replaceFieldWithValue($actionDetail->val, $fieldData, !(bool)$evalMathExpr);
63 + $fk = self::resolveFieldKey($actionDetail, $fieldData);
64 + if (null !== $fk && isset($fields->{$fk}) && !empty($actionDetail->val)) {
65 + $fieldType = isset($fields->{$fk}->typ) ? $fields->{$fk}->typ : '';
66 + $evalMathExpr = preg_match('/month|date/', (string) $fieldType);
67 + $fields->{$fk}->val = '';
68 + $actionValue = Helper::replaceFieldWithValue($actionDetail->val, $fieldData, !(bool) $evalMathExpr);
40 69 }
41 70 return $actionValue;
42 71 }
43 72
@@ -43,12 +72,22 @@
43 72
44 73 public function getActiveListIndex($actionDetail, $fieldData, $fields)
45 74 {
46 75 $activeList = $this->getActionValue($actionDetail, $fieldData, $fields);
47 - $optionsList = $fields->{$fieldData[$actionDetail->field]['key']}->optionsList;
48 76 $activeListIndex = 0;
77 + $fk = self::resolveFieldKey($actionDetail, $fieldData);
78 + if (null === $fk || !isset($fields->{$fk}->optionsList)) {
79 + return $activeListIndex;
80 + }
81 + $optionsList = $fields->{$fk}->optionsList;
82 + if (!\is_array($optionsList) && !\is_object($optionsList)) {
83 + return $activeListIndex;
84 + }
49 85 foreach ($optionsList as $key => $optionObj) {
50 86 $valueArr = (array) $optionObj;
87 + if (empty($valueArr)) {
88 + continue;
89 + }
51 90 $listName = array_keys($valueArr)[0];
52 91 if ($listName === $activeList) {
53 92 $activeListIndex = $key;
54 93 break;
@@ -59,12 +98,16 @@
59 98 }
60 99
61 100 public function show($fields, $fieldData, $actionDetail)
62 101 {
63 - $fields->{$fieldData[$actionDetail->field]['key']}->valid->hide = false;
64 - if ('hidden' === $fields->{$fieldData[$actionDetail->field]['key']}->typ) {
65 - $fields->{$fieldData[$actionDetail->field]['key']}->typ = 'text';
102 + $fk = self::resolveFieldKey($actionDetail, $fieldData);
103 + if (null === $fk || !isset($fields->{$fk})) {
104 + return;
66 105 }
106 + Helper::setNestedProperty($fields, "{$fk}->valid->hide", false);
107 + if (isset($fields->{$fk}->typ) && 'hidden' === $fields->{$fk}->typ) {
108 + $fields->{$fk}->typ = 'text';
109 + }
67 110 }
68 111
69 112 public function setFieldProperty($actions, $fieldData, $fields)
70 113 {
@@ -71,68 +114,74 @@
71 114 if (empty($actions)) {
72 115 return [$fields, $fieldData];
73 116 }
74 117 foreach ($actions as $actionDetail) {
75 - if (!empty($actionDetail->action) && !empty($actionDetail->field) && isset($fields->{$fieldData[$actionDetail->field]['key']}) && !empty($fields->{$fieldData[$actionDetail->field]['key']})) {
76 - $fk = $fieldData[$actionDetail->field]['key'];
77 - switch ($actionDetail->action) {
78 - case 'value':
79 - $data = $this->setValue($actionDetail, $fieldData, $fields);
80 - $fields = $data[0];
81 - $fieldData = $data[1];
82 - break;
83 - case 'hide':
84 - // $fields->{$fk}->valid->hide = true;
85 - Helper::setNestedProperty($fields, "{$fk}->valid->hide", true);
86 - break;
87 - case 'disable':
88 - $fields->{$fk}->valid->disabled = true;
89 - break;
90 - case 'show':
91 - $this->show($fields, $fieldData, $actionDetail);
92 - break;
93 - case 'enable':
94 - $fields->{$fk}->valid->disabled = false;
95 - break;
96 - case 'readonly':
97 - $fields->{$fk}->valid->readonly = true;
98 - break;
99 - case 'writeable':
100 - $fields->{$fk}->valid->readonly = false;
101 - break;
102 - case 'required':
103 - $fields->{$fk}->valid->required = true;
104 - break;
105 - case 'limit':
106 - $fields->{$fk}->valid->limit = true;
107 - break;
108 - case 'min':
109 - $fields->{$fk}->valid->min = true;
110 - break;
111 - case 'max':
112 - $fields->{$fk}->valid->max = true;
113 - break;
114 - case 'activelist':
115 - $fields->{$fk}->config->activeList = $this->getActiveListIndex($actionDetail, $fieldData, $fields);
116 - break;
117 - case 'lbl':
118 - case 'ct':
119 - $fields->{$fk}->lbl = $this->getActionValue($actionDetail, $fieldData, $fields);
120 - break;
121 - case 'sub-titl':
122 - $fields->{$fk}->subtitle = $this->getActionValue($actionDetail, $fieldData, $fields);
123 - break;
124 - case 'hlp-txt':
125 - $fields->{$fk}->helperTxt = $this->getActionValue($actionDetail, $fieldData, $fields);
126 - break;
127 - case 'placeholder':
128 - $fields->{$fk}->ph = $this->getActionValue($actionDetail, $fieldData, $fields);
129 - break;
130 - case 'title':
131 - $fields->{$fk}->title = $this->getActionValue($actionDetail, $fieldData, $fields);
132 - break;
133 - }
118 + // resolveFieldKey() must run *before* $fields is indexed: the old condition built the
119 + // dynamic property name from $fieldData[...]['key'] inside its own isset(), so the missing
120 + // key warned before isset() ever got a chance to short-circuit.
121 + $fk = self::resolveFieldKey($actionDetail, $fieldData);
122 + if (null === $fk || empty($actionDetail->action) || !isset($fields->{$fk}) || empty($fields->{$fk})) {
123 + continue;
134 124 }
125 + switch ($actionDetail->action) {
126 + case 'value':
127 + $data = $this->setValue($actionDetail, $fieldData, $fields);
128 + $fields = $data[0];
129 + $fieldData = $data[1];
130 + break;
131 + case 'hide':
132 + Helper::setNestedProperty($fields, "{$fk}->valid->hide", true);
133 + break;
134 + case 'disable':
135 + Helper::setNestedProperty($fields, "{$fk}->valid->disabled", true);
136 + break;
137 + case 'show':
138 + $this->show($fields, $fieldData, $actionDetail);
139 + break;
140 + case 'enable':
141 + Helper::setNestedProperty($fields, "{$fk}->valid->disabled", false);
142 + break;
143 + case 'readonly':
144 + Helper::setNestedProperty($fields, "{$fk}->valid->readonly", true);
145 + break;
146 + case 'writeable':
147 + Helper::setNestedProperty($fields, "{$fk}->valid->readonly", false);
148 + break;
149 + case 'required':
150 + Helper::setNestedProperty($fields, "{$fk}->valid->required", true);
151 + break;
152 + case 'limit':
153 + Helper::setNestedProperty($fields, "{$fk}->valid->limit", true);
154 + break;
155 + case 'min':
156 + Helper::setNestedProperty($fields, "{$fk}->valid->min", true);
157 + break;
158 + case 'max':
159 + Helper::setNestedProperty($fields, "{$fk}->valid->max", true);
160 + break;
161 + case 'activelist':
162 + // setNestedProperty() creates the intermediate `valid`/`config` object when a legacy
163 + // field JSON does not carry one; the direct writes here used to auto-vivify it and
164 + // emit an undefined-property warning on every run.
165 + Helper::setNestedProperty($fields, "{$fk}->config->activeList", $this->getActiveListIndex($actionDetail, $fieldData, $fields));
166 + break;
167 + case 'lbl':
168 + case 'ct':
169 + $fields->{$fk}->lbl = $this->getActionValue($actionDetail, $fieldData, $fields);
170 + break;
171 + case 'sub-titl':
172 + $fields->{$fk}->subtitle = $this->getActionValue($actionDetail, $fieldData, $fields);
173 + break;
174 + case 'hlp-txt':
175 + $fields->{$fk}->helperTxt = $this->getActionValue($actionDetail, $fieldData, $fields);
176 + break;
177 + case 'placeholder':
178 + $fields->{$fk}->ph = $this->getActionValue($actionDetail, $fieldData, $fields);
179 + break;
180 + case 'title':
181 + $fields->{$fk}->title = $this->getActionValue($actionDetail, $fieldData, $fields);
182 + break;
183 + }
135 184 }
136 185 return [$fields, $fieldData];
137 186 }
138 187
@@ -206,9 +255,15 @@
206 255 // Honor enable/disable: a disabled confirmation message is skipped so the default confirmation applies.
207 256 if (isset($msgConfig->status) && empty($msgConfig->status)) {
208 257 return $workFlowReturnable;
209 258 }
210 - $messageContent = $message[0]->message_content;
259 + // Translate before smart-tag replacement, so lookups hit the stored text.
260 + $messageContent = (string) apply_filters(
261 + 'bitform_translate_form_string',
262 + (string) $message[0]->message_content,
263 + 'msg-content-' . $message[0]->id,
264 + static::$_formID
265 + );
211 266
212 267 // replace pdf link and password
213 268 if (class_exists('\BitCode\BitFormPro\Admin\DownloadFile') && !empty($entryID)) {
214 269 $downloadFile = new \BitCode\BitFormPro\Admin\DownloadFile();
@@ -244,8 +299,15 @@
244 299 return $workFlowReturnable;
245 300 }
246 301 $url = Utilities::jsonObj($redirectPage[0]->integration_details ?? '')->url ?? '';
247 302 if (!empty($url)) {
303 + // Translated before smart-tag replacement: per-language redirect targets.
304 + $url = (string) apply_filters(
305 + 'bitform_translate_form_string',
306 + (string) $url,
307 + 'redirect-url-' . $redirectPage[0]->id,
308 + static::$_formID
309 + );
248 310 $url = Helper::replaceFieldWithValue($url, $fieldValue);
249 311 }
250 312 $workFlowReturnable['redirectPage'] = empty($url) ? false : esc_url_raw($url);
251 313 }