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/Integration/IntegrationHandler.php +681 -148 1.1.83.3.1 View file →
@@ -1,189 +1,722 @@
1 1 <?php
2 2
3 3 namespace BitCode\BitForm\Core\Integration;
4 4
5 +use BitCode\BitForm\Admin\Form\AdminFormManager;
6 +use BitCode\BitForm\Admin\Form\Helpers;
7 +use BitCode\BitForm\Core\Database\FormEntryLogModel;
8 +use BitCode\BitForm\Core\Database\FormEntryModel;
5 9 use BitCode\BitForm\Core\Database\IntegrationModel;
10 +use BitCode\BitForm\Core\Form\FormManager;
11 +use BitCode\BitForm\Core\Util\FieldValueHandler;
12 +use BitCode\BitForm\Core\Util\Log;
13 +use BitCode\BitForm\Core\Util\MailNotifier;
14 +use BitCode\BitForm\Frontend\Form\FrontendFormManager;
6 15
7 16 final class IntegrationHandler
8 17 {
9 - private static $_formID;
10 - private static $_integrationModel;
11 - private $_user_details;
18 + private $_formID;
19 + private static $_integrationModel;
20 + private $_user_details;
12 21
13 - /**
14 - * Constructor of Integration Handler
15 - *
16 - * @param Integer $formID If Integration is accessible globally then
17 - * $formID will be 0 and category app
18 - * @param Array $user_details Details of user accessing data
19 - * @return void
20 - */
21 - public function __construct($formID, $user_details = null)
22 - {
23 - static::$_formID = $formID;
24 - static::$_integrationModel = new IntegrationModel();
25 - $this->_user_details = $user_details;
22 + /**
23 + * Constructor of Integration Handler
24 + *
25 + * @param Integer $formID If Integration is accessible globally then
26 + * $formID will be 0 and category app
27 + * @param Array $user_details Details of user accessing data
28 + * @return void
29 + */
30 + public function __construct($formID, $user_details = null)
31 + {
32 + $this->_formID = $formID;
33 + static::$_integrationModel = new IntegrationModel();
34 + $this->_user_details = $user_details;
35 + }
36 +
37 + public function getAIntegration($integrationID, $integrationCategory = null, $integrationType = null)
38 + {
39 + $conditions = [
40 + 'form_id' => $this->_formID,
41 + 'id' => $integrationID,
42 + ];
43 + if (!is_null($integrationType)) {
44 + $conditions = array_merge($conditions, ['integration_type' => $integrationType]);
26 45 }
46 + if (!is_null($integrationCategory)) {
47 + $conditions = array_merge($conditions, ['category' => $integrationCategory]);
48 + }
49 + return static::$_integrationModel->get(
50 + [
51 + 'id',
52 + 'integration_name',
53 + 'integration_type',
54 + 'integration_details',
55 + 'status',
56 + 'form_id',
57 + ],
58 + $conditions
59 + );
60 + }
27 61
62 + public function getAllIntegration($integrationCategory = null, $integrationType = null, $status = null, $id = null)
63 + {
64 + $conditions = [
65 + 'form_id' => $this->_formID,
66 + ];
67 + if (!is_null($integrationType)) {
68 + $conditions = array_merge($conditions, ['integration_type' => $integrationType]);
69 + }
28 70
29 - public function getAIntegration($integrationID, $integrationCategory = null, $integrationType = null)
30 - {
31 - $conditions = array(
32 - 'form_id' => static::$_formID,
33 - 'id' => $integrationID,
34 - );
35 - if (!is_null($integrationType)) {
36 - $conditions = array_merge($conditions, ['integration_type' => $integrationType]);
71 + if (!is_null($integrationCategory)) {
72 + $conditions = array_merge($conditions, ['category' => $integrationCategory]);
73 + }
74 + if (!is_null($status)) {
75 + $conditions = array_merge($conditions, ['status' => 1]);
76 + }
77 + if (!is_null($id)) {
78 + $conditions = array_merge($conditions, ['id' => $id]);
79 + }
80 + return static::$_integrationModel->get(
81 + [
82 + 'id',
83 + 'form_id',
84 + 'integration_name',
85 + 'integration_type',
86 + 'integration_details',
87 + 'status',
88 + ],
89 + $conditions
90 + );
91 + }
92 +
93 + public function getIntegrationWithoutFormId($integrationCategory = null, $integrationType = null, $status = null, $id = null)
94 + {
95 + $conditions = [];
96 + if (!is_null($integrationType)) {
97 + $conditions = array_merge($conditions, ['integration_type' => $integrationType]);
98 + }
99 +
100 + if (!is_null($integrationCategory)) {
101 + $conditions = array_merge($conditions, ['category' => $integrationCategory]);
102 + }
103 + if (!is_null($status)) {
104 + $conditions = array_merge($conditions, ['status' => 1]);
105 + }
106 + if (!is_null($id)) {
107 + $conditions = array_merge($conditions, ['id' => $id]);
108 + }
109 + return static::$_integrationModel->get(
110 + [
111 + 'id',
112 + 'form_id',
113 + 'integration_name',
114 + 'integration_type',
115 + 'integration_details',
116 + 'status',
117 + ],
118 + $conditions
119 + );
120 + }
121 +
122 + public function saveIntegration($integrationName, $integrationType, $integrationDetails, $integrationCategory, $status = null)
123 + {
124 + if (null === $status) {
125 + $status = 1;
126 + }
127 + return static::$_integrationModel->insert(
128 + [
129 + 'integration_name' => $integrationName,
130 + 'integration_type' => $integrationType,
131 + 'integration_details' => $integrationDetails,
132 + 'category' => $integrationCategory,
133 + 'form_id' => $this->_formID,
134 + 'user_id' => $this->_user_details['id'],
135 + 'user_ip' => $this->_user_details['ip'],
136 + 'status' => $status,
137 + 'created_at' => $this->_user_details['time'],
138 + 'updated_at' => $this->_user_details['time'],
139 + ]
140 + );
141 + }
142 +
143 + public function updateIntegration($integrationID, $integrationName, $integrationType, $integrationDetails, $integrationCategory, $status = null)
144 + {
145 + if (null === $status) {
146 + $status = 1;
147 + }
148 + return static::$_integrationModel->update(
149 + [
150 + 'integration_name' => $integrationName,
151 + 'integration_type' => $integrationType,
152 + 'integration_details' => $integrationDetails,
153 + 'category' => $integrationCategory,
154 + 'form_id' => $this->_formID,
155 + 'user_id' => $this->_user_details['id'],
156 + 'user_ip' => $this->_user_details['ip'],
157 + 'status' => $status,
158 + 'updated_at' => $this->_user_details['time'],
159 + ],
160 + [
161 + 'id' => $integrationID,
162 + ]
163 + );
164 + }
165 +
166 + /**
167 + * Name-only rename of a connected account row, scoped to
168 + * category=connected_integration_apps and this handler's form id (0).
169 + * updateIntegration() is unusable here: it overwrites details/type/category/status.
170 + */
171 + public function renameConnectedApp($integrationID, $integrationName)
172 + {
173 + $data = ['integration_name' => $integrationName];
174 + if (!empty($this->_user_details['time'])) {
175 + $data['updated_at'] = $this->_user_details['time'];
176 + }
177 + return static::$_integrationModel->update(
178 + $data,
179 + [
180 + 'id' => $integrationID,
181 + 'form_id' => $this->_formID,
182 + 'category' => 'connected_integration_apps',
183 + ]
184 + );
185 + }
186 +
187 + public function updateIntegrationStatus($integrationID, $status)
188 + {
189 + $data = ['status' => (int) $status];
190 + if (!empty($this->_user_details['time'])) {
191 + $data['updated_at'] = $this->_user_details['time'];
192 + }
193 + return static::$_integrationModel->update(
194 + $data,
195 + [
196 + 'id' => $integrationID,
197 + 'form_id' => $this->_formID,
198 + ]
199 + );
200 + }
201 +
202 + public function duplicateAllInAForm($oldFormId)
203 + {
204 + $integCols = ['integration_name', 'integration_type', 'integration_details', 'category', 'form_id', 'user_id', 'user_ip', 'status', 'created_at', 'updated_at'];
205 + $integDupData = [
206 + 'integration_name',
207 + 'integration_type',
208 + 'integration_details',
209 + 'category',
210 + $this->_formID,
211 + $this->_user_details['id'],
212 + $this->_user_details['ip'],
213 + 'status',
214 + $this->_user_details['time'],
215 + $this->_user_details['time'],
216 + ];
217 + return static::$_integrationModel->duplicate($integCols, $integDupData, ['form_id' => $oldFormId]);
218 + }
219 +
220 + public function deleteIntegration($integrationID)
221 + {
222 + return static::$_integrationModel->delete(
223 + [
224 + 'id' => $integrationID,
225 + 'form_id' => $this->_formID,
226 + ]
227 + );
228 + }
229 +
230 + public static function replaceFieldWithValue($dataToReplaceField, $fieldValues)
231 + {
232 + if (empty($dataToReplaceField)) {
233 + return false;
234 + }
235 + if (is_string($dataToReplaceField)) {
236 + $dataToReplaceField = static::replaceFieldWithValueHelper($dataToReplaceField, $fieldValues);
237 + } elseif (is_array($dataToReplaceField)) {
238 + foreach ($dataToReplaceField as $field => $value) {
239 + if (is_array($value) && 1 === count($value)) {
240 + $dataToReplaceField[$field] = static::replaceFieldWithValueHelper($value[0], $fieldValues);
241 + } elseif (is_array($value)) {
242 + $dataToReplaceField[$field] = static::replaceFieldWithValue($value, $fieldValues);
243 + } else {
244 + $dataToReplaceField[$field] = static::replaceFieldWithValueHelper($value, $fieldValues);
37 245 }
38 - if (!is_null($integrationCategory)) {
39 - $conditions = array_merge($conditions, ['category' => $integrationCategory]);
246 + }
247 + }
248 + return $dataToReplaceField;
249 + }
250 +
251 + private static function replaceFieldWithValueHelper($stringToReplaceField, $fieldValues)
252 + {
253 + if (empty($stringToReplaceField)) {
254 + return $stringToReplaceField;
255 + }
256 + $fieldPattern = '/\${\w[^ ${}]*}/';
257 + preg_match_all($fieldPattern, $stringToReplaceField, $matchedField);
258 + $uniqueFieldsInStr = array_unique($matchedField[0]);
259 +
260 + foreach ($uniqueFieldsInStr as $value) {
261 + $fieldName = substr($value, 2, strlen($value) - 3);
262 +
263 + $repeaterArr = explode('.', $fieldName);
264 + if (isset($fieldValues[$fieldName])) {
265 + $stringToReplaceField = is_string($fieldValues[$fieldName])
266 + ? str_replace($value, $fieldValues[$fieldName], $stringToReplaceField)
267 + : wp_json_encode($fieldValues[$fieldName]);
268 + } elseif (2 === count($repeaterArr) && isset($fieldValues[$repeaterArr[0]])) {
269 + $repeaterValues = [];
270 + $repeaterFieldValues = $fieldValues[$repeaterArr[0]];
271 + foreach ($repeaterFieldValues as $value) {
272 + if (isset($value[$repeaterArr[1]])) {
273 + $repeaterValues[] = $value[$repeaterArr[1]];
274 + }
40 275 }
41 - return static::$_integrationModel->get(
42 - array(
43 - 'id',
44 - 'integration_name',
45 - 'integration_type',
46 - 'integration_details'
47 - ),
48 - $conditions
49 - );
276 +
277 + $stringToReplaceField = $repeaterValues;
278 + } else {
279 + $stringToReplaceField = FieldValueHandler::replaceSmartTagWithValue($value);
280 + }
50 281 }
282 + return $stringToReplaceField;
283 + }
51 284
52 - public function getAllIntegration($integrationCategory = null, $integrationType = null ,$status = null)
53 - {
54 - $conditions = array(
55 - 'form_id' => static::$_formID
285 + public static function updatedHiddenFieldValue($formID)
286 + {
287 + $hiddenFields = [];
288 + $frontendFormManger = FrontendFormManager::getInstance($formID);
289 +
290 + if ($frontendFormManger->isHoneypotActive()) {
291 + $str = '_bitform' . '_' . $formID . '_' . time() . '_';
292 + $honpotToken = Helpers::honeypotEncryptedToken($str);
293 + $hiddenFields[] = [
294 + 'name' => 'b_h_t',
295 + 'value' => $honpotToken
296 + ];
297 + }
298 + $csrfTokens = Helpers::csrfEecrypted();
299 + $hiddenFields[] = [
300 + 'name' => 't_identity',
301 + 'value' => $csrfTokens['t_identity'],
302 + ];
303 + $hiddenFields[] = [
304 + 'name' => 'csrf',
305 + 'value' => $csrfTokens['csrf']
306 + ];
307 + return $hiddenFields;
308 + }
309 +
310 + public static function maybeSetCronForIntegration($workFlowReturnedData, $opType, $isFormRequest = true)
311 + {
312 + $responseData = [];
313 + $entryId = $workFlowReturnedData['triggerData']['entryID'];
314 + $responseData['entry_id'] = $entryId;
315 + if (isset($workFlowReturnedData['message'])) {
316 + $formID = $workFlowReturnedData['triggerData']['formID'];
317 + $responseData['message'] = $workFlowReturnedData['message'];
318 + $responseData['hidden_fields'] = self::updatedHiddenFieldValue($formID);
319 + $responseData['msg_id'] = isset($workFlowReturnedData['msg_id']) ? $workFlowReturnedData['msg_id'] : 0;
320 + if (isset($workFlowReturnedData['msg_duration'])) {
321 + $responseData['msg_duration'] = $workFlowReturnedData['msg_duration'];
322 + }
323 + if (isset($workFlowReturnedData['afterSubmit'])) {
324 + $responseData['afterSubmit'] = $workFlowReturnedData['afterSubmit'];
325 + }
326 + }
327 + if (isset($workFlowReturnedData['redirectPage'])) {
328 + $responseData['redirectPage'] = $workFlowReturnedData['redirectPage'];
329 + }
330 + if ('update' === $opType && isset($workFlowReturnedData['updatedData'])) {
331 + $responseData['updatedData'] = $workFlowReturnedData['updatedData'];
332 + }
333 + if (isset($workFlowReturnedData['new_nonce'])) {
334 + $responseData['new_nonce'] = $workFlowReturnedData['new_nonce'];
335 + }
336 + if (!isset($workFlowReturnedData['triggerData'])) {
337 + return $responseData;
338 + }
339 +
340 + $triggerData = $workFlowReturnedData['triggerData'];
341 +
342 + if (!isset($workFlowReturnedData['fields'])) {
343 + $workFlowReturnedData['fields'] = isset($workFlowReturnedData['updatedData'])
344 + ? $workFlowReturnedData['updatedData']
345 + : [];
346 + }
347 +
348 + $trnasientData = get_transient("bitform_trigger_transient_{$entryId}");
349 + $trnasientData = is_string($trnasientData) ? json_decode($trnasientData) : $trnasientData;
350 +
351 + if (!empty($trnasientData['fields'])) {
352 + $workFlowReturnedData['fields'] = array_merge($workFlowReturnedData['fields'], $trnasientData['fields']);
353 + }
354 +
355 + if (function_exists('fastcgi_finish_request') || !wp_doing_ajax()) {
356 + if (!headers_sent()) {
357 + header('Connection: close');
358 + $contentType = wp_doing_ajax() || defined('REST_REQUEST') ? 'application/json' : 'text/html';
359 + header('Content-Type: ' . $contentType . '; charset=' . get_option('blog_charset'));
360 + status_header(200);
361 + if (!$isFormRequest) {
362 + $response = [
363 + 'status' => 200,
364 + 'code' => 4000,
365 + 'message' => 'Data Added Successfully!!',
366 + 'success' => true
367 + ];
368 + } else {
369 + $response = [
370 + 'success' => true,
371 + 'data' => $responseData,
372 + ];
373 + }
374 + }
375 + ob_start();
376 + echo wp_doing_ajax() || defined('REST_REQUEST') ? wp_json_encode($response) : esc_html($workFlowReturnedData['message']);
377 + ob_end_flush();
378 + flush();
379 + session_write_close();
380 +
381 + if (isset($workFlowReturnedData['integrationRun']) && !$workFlowReturnedData['integrationRun']) {
382 + $entryModel = new FormEntryModel();
383 + $updatedStatus = $entryModel->update(
384 + [
385 + 'status' => 2,
386 + ],
387 + [
388 + 'form_id' => $triggerData['formID'],
389 + 'id' => $triggerData['entryID'],
390 + ]
56 391 );
57 - if (!is_null($integrationType)) {
58 - $conditions = array_merge($conditions, ['integration_type' => $integrationType]);
392 +
393 + $triggerData['fields'] = $workFlowReturnedData['fields'];
394 + if (!is_wp_error($updatedStatus)) {
395 + if ($workFlowReturnedData['dflt_template']) {
396 + // $triggerData['fields'] = $workFlowReturnedData['fields'];
397 + do_action('bitform_double_optin_confirmation', $workFlowReturnedData['integrationDetails'], $triggerData);
398 + } elseif (isset($triggerData['dblOptin'])) {
399 + foreach ($triggerData['dblOptin'] as $value) {
400 + try {
401 + MailNotifier::notify($value, $triggerData['formID'], $triggerData['fields'], $triggerData['entryID'], true, $triggerData['logID']);
402 + } catch (\Throwable $mailError) {
403 + // response already flushed — a mail failure here must not kill the rest of the run
404 + Log::debug_log('[+] Double opt-in mail failed: ' . $mailError->getMessage());
405 + }
406 + }
407 + }
59 408 }
60 -
61 - if (!is_null($integrationCategory)) {
62 - $conditions = array_merge($conditions, ['category' => $integrationCategory]);
409 + if (function_exists('fastcgi_finish_request')) {
410 + fastcgi_finish_request();
63 411 }
64 - if (!is_null($status)) {
65 - $conditions = array_merge($conditions, ['status' => 1]);
412 +
413 + if (defined('REST_REQUEST') || wp_doing_ajax()) {
414 + die;
66 415 }
67 - return static::$_integrationModel->get(
68 - array(
69 - 'id',
70 - 'integration_name',
71 - 'integration_type',
72 - 'integration_details',
73 - 'status'
74 - ),
75 - $conditions
416 + return $workFlowReturnedData;
417 + }
418 + if (isset($triggerData['mail']) && !empty($triggerData['mail'])) {
419 + $formManager = new AdminFormManager($triggerData['formID']);
420 + $formContent = $formManager->getFormContent();
421 + $submitted_fields = $formContent->fields;
422 + $fieldValueForMail = FieldValueHandler::formatFieldValueForMail($submitted_fields, $workFlowReturnedData['fields']);
423 + foreach ($triggerData['mail'] as $value) {
424 + try {
425 + MailNotifier::notify($value, $triggerData['formID'], $fieldValueForMail, $triggerData['entryID'], false, $triggerData['logID']);
426 + } catch (\Throwable $mailError) {
427 + // response already flushed — a mail failure here must not skip the integrations below
428 + Log::debug_log('[+] Mail notification failed: ' . $mailError->getMessage());
429 + }
430 + }
431 + }
432 + do_action('bitforms_exec_integrations', $triggerData['integrations'], $workFlowReturnedData['fields'], $triggerData['formID'], $triggerData['entryID'], $triggerData['logID']);
433 +
434 + if (function_exists('fastcgi_finish_request')) {
435 + fastcgi_finish_request();
436 + }
437 +
438 + if (defined('REST_REQUEST') || wp_doing_ajax()) {
439 + die;
440 + }
441 + return $workFlowReturnedData;
442 + }
443 +
444 + $entryID = $triggerData['entryID'];
445 + if (isset($workFlowReturnedData['cron']) && $workFlowReturnedData['cron'] && empty($triggerData['mail']) && isset($workFlowReturnedData['integrationRun']) && $workFlowReturnedData['integrationRun']) {
446 + $eventScheuled = wp_schedule_single_event(time(), 'bitforms_exec_integrations', [$triggerData['integrations'], $workFlowReturnedData['fields'], $triggerData['formID'], $triggerData['entryID'], $triggerData['logID']]);
447 + $scheduleTime = wp_next_scheduled('bitforms_exec_integrations', [$triggerData['integrations'], $workFlowReturnedData['fields'], $triggerData['formID'], $triggerData['entryID'], $triggerData['logID']]);
448 + $responseData['cron'] = get_site_url(null, "/wp-cron.php?doing_wp_cron&{$scheduleTime}");
449 + } elseif (!empty($triggerData['integrations']) || !empty($triggerData['mail']) || isset($workFlowReturnedData['integrationRun']) && !$workFlowReturnedData['integrationRun']) {
450 + $triggerData['fields'] = $workFlowReturnedData['fields'];
451 + if (!$workFlowReturnedData['integrationRun'] && isset($workFlowReturnedData['integrationDetails'])) {
452 + $triggerData['dbl_opt_donf'] = $workFlowReturnedData['integrationDetails'];
453 + $triggerData['dbl_opt_dflt_template'] = $workFlowReturnedData['dflt_template'];
454 + $triggerData['integrationRun'] = $workFlowReturnedData['integrationRun'];
455 + }
456 +
457 + // Generate one-time trigger token for security
458 + $triggerToken = wp_hash($triggerData['entryID'] . $triggerData['logID'] . time() . wp_rand(), 'nonce');
459 + $triggerData['trigger_token'] = $triggerToken;
460 +
461 + set_transient("bitform_trigger_transient_{$entryID}", $triggerData, HOUR_IN_SECONDS);
462 + $queuedAt = current_time('mysql');
463 + $entryLog = new FormEntryLogModel();
464 + $queueuEntry = $entryLog->log_history_insert(
465 + [
466 + 'log_id' => $triggerData['logID'],
467 + 'integration_id' => 0,
468 + 'api_type' => wp_json_encode(['type' => 'trigger', 'type_name' => 'Workflow', 'on' => $opType]),
469 + 'response_type' => 'queued',
470 + // 'trigger' is a durable copy of the transient payload: object-cache hosts can
471 + // evict the transient (and it expires after 1h), which would otherwise lose the run
472 + 'response_obj' => wp_json_encode(['status' => 'queued', 'queued_at' => $queuedAt, 'trigger' => $triggerData]),
473 + 'created_at' => $queuedAt,
474 + ]
475 + );
476 + if (\is_int($queueuEntry)) {
477 + // Safety net: the browser-fired bitforms_trigger_workflow request can be lost
478 + // (navigation abort, WAF, network drop). This event re-runs the queued workflow;
479 + // it no-ops if the browser trigger already claimed the run.
480 + wp_schedule_single_event(
481 + time() + 2 * MINUTE_IN_SECONDS,
482 + 'bitforms_reclaim_workflow',
483 + [(int) $entryID, (int) $triggerData['formID'], (int) $triggerData['logID'], $queueuEntry]
76 484 );
485 + if (!wp_next_scheduled('bitforms_reclaim_sweep')) {
486 + wp_schedule_event(time() + HOUR_IN_SECONDS, 'hourly', 'bitforms_reclaim_sweep');
487 + }
488 + }
489 + $responseData['cronNotOk'] = [
490 + $triggerData['entryID'],
491 + $triggerData['logID'],
492 + $queueuEntry,
493 + $triggerToken, // Add one-time token as 4th element
494 + ];
77 495 }
496 + return $responseData;
497 + }
78 498
79 - public function saveIntegration($integrationName, $integrationType, $integrationDetails, $integrationCategory,$status = null)
80 - {
81 - if($status == null ){
82 - $status = 1;
499 + public static function repeaterFieldValueFormatter($fieldValues)
500 + {
501 + $formattedArray = [];
502 +
503 + foreach ($fieldValues as $key => $value) {
504 + if (is_array($value)) {
505 + foreach ($value as $index => $v) {
506 + if (is_array($v)) {
507 + foreach ($v as $k1 => $v1) {
508 + $formattedArray[$k1][] = $v1;
509 + }
510 + } else {
511 + $formattedArray[$key][] = $v;
512 + }
83 513 }
84 - return static::$_integrationModel->insert(
85 - array(
86 - "integration_name"=> $integrationName,
87 - "integration_type"=> $integrationType,
88 - "integration_details"=> $integrationDetails,
89 - 'category' => $integrationCategory,
90 - 'form_id' => static::$_formID,
91 - "user_id"=> $this->_user_details['id'],
92 - "user_ip"=> $this->_user_details['ip'],
93 - "status"=> $status,
94 - "created_at"=> $this->_user_details['time'],
95 - "updated_at"=> $this->_user_details['time']
96 - )
97 - );
514 + } else {
515 + $formattedArray[$key] = $value;
516 + }
98 517 }
99 518
100 - public function updateIntegration($integrationID, $integrationName, $integrationType, $integrationDetails, $integrationCategory,$status=null)
101 - {
102 - if($status == null ){
103 - $status = 1;
519 + return $formattedArray;
520 + }
521 +
522 + /**
523 + *
524 + * @param mixed $fieldValues
525 + * @param mixed $returnRepeaterValue default is array
526 + * @param mixed $formID form ID required for string formatting
527 + * @return array and repeater field value as array or string
528 + */
529 + public static function formattedRepeaterValue($fieldValues, $returnRepeaterValue = '', $formID = null)
530 + {
531 + // get the repeater field and store it in an array
532 + $repeaterField = [];
533 + foreach ($fieldValues as $key => $value) {
534 + if (!preg_match('/\./', $key)) {
535 + continue;
536 + }
537 + $repeaterField[] = $key;
538 + }
539 +
540 + // put the value of the child key to the parent repeater field\
541 + foreach ($repeaterField as $key) {
542 + list($parentKey, $childKey) = explode('.', $key);
543 + $repeatValues = isset($fieldValues[$parentKey]) ? $fieldValues[$parentKey] : $key;
544 +
545 + if (!is_array($repeatValues)) {
546 + $fieldValues[$key] = $fieldValues[$childKey];
547 + continue;
548 + }
549 +
550 + foreach ($repeatValues as $value) {
551 + if (isset($value[$childKey])) {
552 + if (!isset($fieldValues[$key]) || !is_array($fieldValues[$key])) {
553 + $fieldValues[$key] = [];
554 + }
555 + if (!empty($value[$childKey])) {
556 + $fieldValues[$key][] = $value[$childKey];
557 + }
104 558 }
105 - return static::$_integrationModel->update(
106 - array(
107 - "integration_name"=> $integrationName,
108 - "integration_type"=> $integrationType,
109 - "integration_details"=> $integrationDetails,
110 - 'category' => $integrationCategory,
111 - 'form_id' => static::$_formID,
112 - "user_id"=> $this->_user_details['id'],
113 - "user_ip"=> $this->_user_details['ip'],
114 - "status"=> $status,
115 - "updated_at"=> $this->_user_details['time']
116 - ),
117 - array(
118 - "id" => $integrationID
119 - )
120 - );
559 + }
121 560 }
122 561
123 - public function duplicateAllInAForm($oldFormId)
124 - {
125 - $integCols = ["integration_name","integration_type","integration_details",'category' ,'form_id' ,"user_id","user_ip","status","created_at","updated_at"];
126 - $integDupData = array(
127 - "integration_name",
128 - "integration_type",
129 - "integration_details",
130 - 'category',
131 - static::$_formID,
132 - $this->_user_details['id'],
133 - $this->_user_details['ip'],
134 - "status",
135 - $this->_user_details['time'],
136 - $this->_user_details['time']
137 - );
138 - return static::$_integrationModel->duplicate($integCols, $integDupData, ['form_id' => $oldFormId]);
562 + // convert the array to string repeater field value
563 + if ('string' === $returnRepeaterValue) {
564 + $form = new FormManager($formID);
565 + foreach ($repeaterField as $key) {
566 + if (isset($fieldValues[$key]) && is_array($fieldValues[$key])) {
567 + $fieldValues[$key] = implode(', ', self::flattenArray($fieldValues[$key]));
568 + }
569 + }
570 +
571 + foreach ($fieldValues as $key=>$value) {
572 + if (!$form->isRepeaterField($key)) {
573 + continue;
574 + }
575 + $repeaterValues = $fieldValues[$key];
576 +
577 + $newFieldValues = self::constructRepeaterValue($repeaterValues, $formID);
578 +
579 + $fieldValues[$key] = is_array($newFieldValues) ? json_encode(array_values($newFieldValues), JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) : $fieldValues[$key];
580 + }
139 581 }
140 582
141 - public function deleteIntegration($integrationID)
142 - {
143 - return static::$_integrationModel->delete(
144 - array(
145 - 'id' => $integrationID,
146 - 'form_id' => static::$_formID,
147 - )
148 - );
583 + return $fieldValues;
584 + }
585 +
586 + private static function flattenArray($array)
587 + {
588 + return array_map(function ($val) {
589 + if (is_array($val)) {
590 + return count($val) > 1 ? ('[' . implode(', ') . ']') : implode(', ', $val);
591 + } else {
592 + return $val;
593 + }
594 + }, $array);
595 + }
596 +
597 + /**
598 + * Replace file field value with web url
599 + * @param mixed $fieldValues
600 + * @param number $formId
601 + * @param number $entryId
602 + * @return mixed $fieldValues
603 + */
604 + public static function handleFileUrl($fieldValues, $formId, $entryId)
605 + {
606 + if (!$formId || !$entryId) {
607 + return $fieldValues;
149 608 }
609 + $form = FormManager::getInstance($formId);
610 + $formFields = $form->getFields();
150 611
151 - public static function replaceFieldWithValue($dataToReplaceField, $fieldValues)
152 - {
153 - if (empty($dataToReplaceField)) {
154 - return false;
612 + $fldsIncludeFile = ['file-up', 'advanced-file-up', 'repeater', 'signature'];
613 + foreach ($fieldValues as $key =>$value) {
614 + $fldTyp = $formFields[$key]['type'] ?? null;
615 + if (!in_array($fldTyp, $fldsIncludeFile)) {
616 + continue;
617 + }
618 +
619 + if ('repeater' === $fldTyp && is_array($value)) {
620 + foreach ($value as $rIndex => $rValue) {
621 + foreach ($rValue as $subKey=>$subValue) {
622 + if (!in_array($formFields[$subKey]['type'], $fldsIncludeFile)) {
623 + continue;
624 + }
625 + // for file fields inside repeater
626 + if (is_array($subValue)) {
627 + foreach ($subValue as $fileIndex=>$fileValue) {
628 + $fieldValues[$key][$rIndex][$subKey][$fileIndex] = self::constructFileUrl($formId, $entryId, $fileValue);
629 + }
630 + } else {
631 + // for signature field inside repeater;
632 + $fieldValues[$key][$rIndex][$subKey] = self::constructFileUrl($formId, $entryId, $subValue);
633 + }
634 + }
155 635 }
156 - if (is_string($dataToReplaceField)) {
157 - $dataToReplaceField = static::replaceFieldWithValueHelper($dataToReplaceField, $fieldValues);
158 - } else if (is_array($dataToReplaceField)) {
159 - foreach ($dataToReplaceField as $field => $value) {
160 - if (is_array($value) && count($value) === 1 ) {
161 - $dataToReplaceField[$field] = static::replaceFieldWithValueHelper($value[0], $fieldValues);
162 - } else if(is_array($value)){
163 - $dataToReplaceField[$field] = static::replaceFieldWithValue($value, $fieldValues);
164 - } else {
165 - $dataToReplaceField[$field] = static::replaceFieldWithValueHelper($value, $fieldValues);
166 - }
167 - }
636 + }
637 +
638 + if (in_array($fldTyp, ['file-up', 'advanced-file-up', 'signature'], true)) {
639 + if (is_array($value)) {
640 + foreach ($value as $fldIndex=>$fldValue) {
641 + $fieldValues[$key][$fldIndex] = self::constructFileUrl($formId, $entryId, $fldValue);
642 + }
643 + } else {
644 + $fieldValues[$key] = self::constructFileUrl($formId, $entryId, $value);
168 645 }
169 - return $dataToReplaceField;
646 + }
170 647 }
171 648
172 - private static function replaceFieldWithValueHelper($stringToReplaceField, $fieldValues)
173 - {
174 - if (empty($stringToReplaceField)) {
175 - return $stringToReplaceField;
176 - }
177 - $fieldPattern = '/\${\w[^ ${}]*}/';
178 - preg_match_all($fieldPattern, $stringToReplaceField, $matchedField);
179 - $uniqueFieldsInStr = array_unique($matchedField[0]);
180 - foreach ($uniqueFieldsInStr as $key => $value) {
181 - $fieldName = substr($value, 2, strlen($value)-3);
182 - if (!empty($fieldValues[$fieldName])) {
183 - $stringToReplaceField = is_string($fieldValues[$fieldName])? str_replace($value, $fieldValues[$fieldName], $stringToReplaceField) :
184 - wp_json_encode($fieldValues[$fieldName]);
649 + return $fieldValues;
650 + }
651 +
652 + public static function assignRepeaterFieldValue($fieldValues, $formId)
653 + {
654 + if (!$formId) {
655 + return $fieldValues;
656 + }
657 + $form = new FormManager($formId);
658 + $formFields = $form->getFields();
659 +
660 + foreach ($fieldValues as $key=>$value) {
661 + if (!$form->isRepeaterField($key)) {
662 + continue;
663 + }
664 +
665 + if ('repeater' === $formFields[$key]['type']) {
666 + if (is_array($value)) {
667 + foreach ($value as $rIndex=>$rValue) {
668 + foreach ($rValue as $subKey =>$subValue) {
669 + $fieldValues[$subKey] = $subValue;
185 670 }
671 + }
186 672 }
187 - return $stringToReplaceField;
673 + }
188 674 }
675 +
676 + return $fieldValues;
677 + }
678 +
679 + public static function constructRepeaterValue($repeaterValues, $formId)
680 + {
681 + if (!$formId || !is_array($repeaterValues)) {
682 + return $repeaterValues;
683 + }
684 + $form = FormManager::getInstance($formId);
685 + $formFields = $form->getFields();
686 + $newRptrValue = [];
687 + foreach ($repeaterValues as $key=>$value) {
688 + $newRepeatedValue = [];
689 + foreach ($value as $rKey =>$rValue) {
690 + $fldLbl = isset($formFields[$rKey]) ? $formFields[$rKey]['label'] : $rKey;
691 + $newRepeatedValue[$fldLbl] = $rValue;
692 + }
693 + $newRptrValue[$key] = $newRepeatedValue;
694 + }
695 +
696 + return $newRptrValue;
697 + }
698 +
699 + private static function constructFileUrl($formId, $entryId, $fileName)
700 + {
701 + $webPath = Helpers::getWebPathWithEncryptedEntryId($formId, $entryId);
702 +
703 + return rtrim($webPath, '/') . '/' . ltrim($fileName, '/');
704 + }
705 +
706 + // private static function constructFileUrl($formId, $entryId, $fileName)
707 + // {
708 + // $restBaseUrl = get_rest_url();
709 + // if (!$restBaseUrl) {
710 + // return $fileName;
711 + // }
712 + // //http://bitlocal.local/wp-json/bitform/v1/bitform-file-download/?formID=1&entryID=16&fileID=bit-form-1005.pdf
713 + // $queryParam = build_query([
714 + // 'formID' => $formId,
715 + // 'entryID' => $entryId,
716 + // 'fileID' => $fileName
717 + // ]);
718 +
719 + // $path = $restBaseUrl . 'bitform' . '/' . 'v1' . '/' . 'bitform-file-download' . '/' . '?' . $queryParam;
720 + // return rtrim($path, '/');
721 + // }
189 722 }