PluginProbe
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder / 2.15.3
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder v2.15.3
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 +27 -240 3.2.22.15.3 View file →
@@ -6,17 +6,15 @@
6 6 use BitCode\BitForm\Admin\Form\Helpers;
7 7 use BitCode\BitForm\Core\Database\FormEntryLogModel;
8 8 use BitCode\BitForm\Core\Database\FormEntryModel;
9 9 use BitCode\BitForm\Core\Database\IntegrationModel;
10 -use BitCode\BitForm\Core\Form\FormManager;
11 10 use BitCode\BitForm\Core\Util\FieldValueHandler;
12 -use BitCode\BitForm\Core\Util\Log;
13 11 use BitCode\BitForm\Core\Util\MailNotifier;
14 12 use BitCode\BitForm\Frontend\Form\FrontendFormManager;
15 13
16 14 final class IntegrationHandler
17 15 {
18 - private $_formID;
16 + private static $_formID;
19 17 private static $_integrationModel;
20 18 private $_user_details;
21 19
22 20 /**
@@ -28,9 +26,9 @@
28 26 * @return void
29 27 */
30 28 public function __construct($formID, $user_details = null)
31 29 {
32 - $this->_formID = $formID;
30 + static::$_formID = $formID;
33 31 static::$_integrationModel = new IntegrationModel();
34 32 $this->_user_details = $user_details;
35 33 }
36 34
@@ -36,9 +34,9 @@
36 34
37 35 public function getAIntegration($integrationID, $integrationCategory = null, $integrationType = null)
38 36 {
39 37 $conditions = [
40 - 'form_id' => $this->_formID,
38 + 'form_id' => static::$_formID,
41 39 'id' => $integrationID,
42 40 ];
43 41 if (!is_null($integrationType)) {
44 42 $conditions = array_merge($conditions, ['integration_type' => $integrationType]);
@@ -51,9 +49,8 @@
51 49 'id',
52 50 'integration_name',
53 51 'integration_type',
54 52 'integration_details',
55 - 'status',
56 53 'form_id',
57 54 ],
58 55 $conditions
59 56 );
@@ -61,9 +58,9 @@
61 58
62 59 public function getAllIntegration($integrationCategory = null, $integrationType = null, $status = null, $id = null)
63 60 {
64 61 $conditions = [
65 - 'form_id' => $this->_formID,
62 + 'form_id' => static::$_formID,
66 63 ];
67 64 if (!is_null($integrationType)) {
68 65 $conditions = array_merge($conditions, ['integration_type' => $integrationType]);
69 66 }
@@ -129,9 +126,9 @@
129 126 'integration_name' => $integrationName,
130 127 'integration_type' => $integrationType,
131 128 'integration_details' => $integrationDetails,
132 129 'category' => $integrationCategory,
133 - 'form_id' => $this->_formID,
130 + 'form_id' => static::$_formID,
134 131 'user_id' => $this->_user_details['id'],
135 132 'user_ip' => $this->_user_details['ip'],
136 133 'status' => $status,
137 134 'created_at' => $this->_user_details['time'],
@@ -150,9 +147,9 @@
150 147 'integration_name' => $integrationName,
151 148 'integration_type' => $integrationType,
152 149 'integration_details' => $integrationDetails,
153 150 'category' => $integrationCategory,
154 - 'form_id' => $this->_formID,
151 + 'form_id' => static::$_formID,
155 152 'user_id' => $this->_user_details['id'],
156 153 'user_ip' => $this->_user_details['ip'],
157 154 'status' => $status,
158 155 'updated_at' => $this->_user_details['time'],
@@ -162,23 +159,8 @@
162 159 ]
163 160 );
164 161 }
165 162
166 - public function updateIntegrationStatus($integrationID, $status)
167 - {
168 - $data = ['status' => (int) $status];
169 - if (!empty($this->_user_details['time'])) {
170 - $data['updated_at'] = $this->_user_details['time'];
171 - }
172 - return static::$_integrationModel->update(
173 - $data,
174 - [
175 - 'id' => $integrationID,
176 - 'form_id' => $this->_formID,
177 - ]
178 - );
179 - }
180 -
181 163 public function duplicateAllInAForm($oldFormId)
182 164 {
183 165 $integCols = ['integration_name', 'integration_type', 'integration_details', 'category', 'form_id', 'user_id', 'user_ip', 'status', 'created_at', 'updated_at'];
184 166 $integDupData = [
@@ -185,9 +167,9 @@
185 167 'integration_name',
186 168 'integration_type',
187 169 'integration_details',
188 170 'category',
189 - $this->_formID,
171 + static::$_formID,
190 172 $this->_user_details['id'],
191 173 $this->_user_details['ip'],
192 174 'status',
193 175 $this->_user_details['time'],
@@ -200,9 +182,9 @@
200 182 {
201 183 return static::$_integrationModel->delete(
202 184 [
203 185 'id' => $integrationID,
204 - 'form_id' => $this->_formID,
186 + 'form_id' => static::$_formID,
205 187 ]
206 188 );
207 189 }
208 190
@@ -234,13 +216,13 @@
234 216 }
235 217 $fieldPattern = '/\${\w[^ ${}]*}/';
236 218 preg_match_all($fieldPattern, $stringToReplaceField, $matchedField);
237 219 $uniqueFieldsInStr = array_unique($matchedField[0]);
238 -
239 - foreach ($uniqueFieldsInStr as $value) {
220 + foreach ($uniqueFieldsInStr as $key => $value) {
240 221 $fieldName = substr($value, 2, strlen($value) - 3);
241 222
242 223 $repeaterArr = explode('.', $fieldName);
224 +
243 225 if (isset($fieldValues[$fieldName])) {
244 226 $stringToReplaceField = is_string($fieldValues[$fieldName])
245 227 ? str_replace($value, $fieldValues[$fieldName], $stringToReplaceField)
246 228 : wp_json_encode($fieldValues[$fieldName]);
@@ -246,8 +228,9 @@
246 228 : wp_json_encode($fieldValues[$fieldName]);
247 229 } elseif (2 === count($repeaterArr) && isset($fieldValues[$repeaterArr[0]])) {
248 230 $repeaterValues = [];
249 231 $repeaterFieldValues = $fieldValues[$repeaterArr[0]];
232 +
250 233 foreach ($repeaterFieldValues as $value) {
251 234 if (isset($value[$repeaterArr[1]])) {
252 235 $repeaterValues[] = $value[$repeaterArr[1]];
253 236 }
@@ -253,10 +236,8 @@
253 236 }
254 237 }
255 238
256 239 $stringToReplaceField = $repeaterValues;
257 - } else {
258 - $stringToReplaceField = FieldValueHandler::replaceSmartTagWithValue($value);
259 240 }
260 241 }
261 242 return $stringToReplaceField;
262 243 }
@@ -263,9 +244,9 @@
263 244
264 245 public static function updatedHiddenFieldValue($formID)
265 246 {
266 247 $hiddenFields = [];
267 - $frontendFormManger = FrontendFormManager::getInstance($formID);
248 + $frontendFormManger = new FrontendFormManager($formID);
268 249
269 250 if ($frontendFormManger->isHoneypotActive()) {
270 251 $str = '_bitform' . '_' . $formID . '_' . time() . '_';
271 252 $honpotToken = Helpers::honeypotEncryptedToken($str);
@@ -298,11 +279,8 @@
298 279 $responseData['msg_id'] = isset($workFlowReturnedData['msg_id']) ? $workFlowReturnedData['msg_id'] : 0;
299 280 if (isset($workFlowReturnedData['msg_duration'])) {
300 281 $responseData['msg_duration'] = $workFlowReturnedData['msg_duration'];
301 282 }
302 - if (isset($workFlowReturnedData['afterSubmit'])) {
303 - $responseData['afterSubmit'] = $workFlowReturnedData['afterSubmit'];
304 - }
305 283 }
306 284 if (isset($workFlowReturnedData['redirectPage'])) {
307 285 $responseData['redirectPage'] = $workFlowReturnedData['redirectPage'];
308 286 }
@@ -308,11 +286,8 @@
308 286 }
309 287 if ('update' === $opType && isset($workFlowReturnedData['updatedData'])) {
310 288 $responseData['updatedData'] = $workFlowReturnedData['updatedData'];
311 289 }
312 - if (isset($workFlowReturnedData['new_nonce'])) {
313 - $responseData['new_nonce'] = $workFlowReturnedData['new_nonce'];
314 - }
315 290 if (!isset($workFlowReturnedData['triggerData'])) {
316 291 return $responseData;
317 292 }
318 293
@@ -319,12 +294,10 @@
319 294 $triggerData = $workFlowReturnedData['triggerData'];
320 295
321 296 $trnasientData = get_transient("bitform_trigger_transient_{$entryId}");
322 297 $trnasientData = is_string($trnasientData) ? json_decode($trnasientData) : $trnasientData;
323 -
324 298 if (!empty($trnasientData['fields'])) {
325 - $fieldData = isset($workFlowReturnedData['fields']) ? $workFlowReturnedData['fields'] : $workFlowReturnedData['updatedData'];
326 - $workFlowReturnedData['fields'] = array_merge($fieldData, $trnasientData['fields']);
299 + $workFlowReturnedData['fields'] = array_merge($workFlowReturnedData['fields'], $trnasientData['fields']);
327 300 }
328 301
329 302 if (function_exists('fastcgi_finish_request') || !wp_doing_ajax()) {
330 303 if (!headers_sent()) {
@@ -346,9 +319,9 @@
346 319 ];
347 320 }
348 321 }
349 322 ob_start();
350 - echo wp_doing_ajax() || defined('REST_REQUEST') ? wp_json_encode($response) : esc_html($workFlowReturnedData['message']);
323 + echo wp_doing_ajax() || defined('REST_REQUEST') ? wp_json_encode($response) : $workFlowReturnedData['message'];
351 324 ob_end_flush();
352 325 flush();
353 326 session_write_close();
354 327
@@ -363,21 +336,15 @@
363 336 'id' => $triggerData['entryID'],
364 337 ]
365 338 );
366 339
367 - $triggerData['fields'] = $workFlowReturnedData['fields'];
368 340 if (!is_wp_error($updatedStatus)) {
369 341 if ($workFlowReturnedData['dflt_template']) {
370 - // $triggerData['fields'] = $workFlowReturnedData['fields'];
371 - do_action('bitform_double_optin_confirmation', $workFlowReturnedData['integrationDetails'], $triggerData);
342 + $triggerData['fields'] = $workFlowReturnedData['fields'];
343 + do_action('bf_double_optin_confirmation', $workFlowReturnedData['integrationDetails'], $triggerData);
372 344 } elseif (isset($triggerData['dblOptin'])) {
373 345 foreach ($triggerData['dblOptin'] as $value) {
374 - try {
375 - MailNotifier::notify($value, $triggerData['formID'], $triggerData['fields'], $triggerData['entryID'], true, $triggerData['logID']);
376 - } catch (\Throwable $mailError) {
377 - // response already flushed — a mail failure here must not kill the rest of the run
378 - Log::debug_log('[+] Double opt-in mail failed: ' . $mailError->getMessage());
379 - }
346 + MailNotifier::notify($value, $triggerData['formID'], $triggerData['fields'], $triggerData['entryID'], true, $triggerData['logID']);
380 347 }
381 348 }
382 349 }
383 350 if (function_exists('fastcgi_finish_request')) {
@@ -388,20 +355,16 @@
388 355 die;
389 356 }
390 357 return $workFlowReturnedData;
391 358 }
392 - if (isset($triggerData['mail']) && !empty($triggerData['mail'])) {
359 +
360 + if (isset($triggerData['mail'])) {
393 361 $formManager = new AdminFormManager($triggerData['formID']);
394 362 $formContent = $formManager->getFormContent();
395 363 $submitted_fields = $formContent->fields;
396 364 $fieldValueForMail = FieldValueHandler::formatFieldValueForMail($submitted_fields, $workFlowReturnedData['fields']);
397 365 foreach ($triggerData['mail'] as $value) {
398 - try {
399 - MailNotifier::notify($value, $triggerData['formID'], $fieldValueForMail, $triggerData['entryID'], false, $triggerData['logID']);
400 - } catch (\Throwable $mailError) {
401 - // response already flushed — a mail failure here must not skip the integrations below
402 - Log::debug_log('[+] Mail notification failed: ' . $mailError->getMessage());
403 - }
366 + MailNotifier::notify($value, $triggerData['formID'], $fieldValueForMail, $triggerData['entryID']);
404 367 }
405 368 }
406 369 do_action('bitforms_exec_integrations', $triggerData['integrations'], $workFlowReturnedData['fields'], $triggerData['formID'], $triggerData['entryID'], $triggerData['logID']);
407 370
@@ -415,9 +378,9 @@
415 378 return $workFlowReturnedData;
416 379 }
417 380
418 381 $entryID = $triggerData['entryID'];
419 - if (isset($workFlowReturnedData['cron']) && $workFlowReturnedData['cron'] && empty($triggerData['mail']) && isset($workFlowReturnedData['integrationRun']) && $workFlowReturnedData['integrationRun']) {
382 + if (isset($workFlowReturnedData['cron']) && $workFlowReturnedData['cron'] && !isset($triggerData['mail']) && isset($workFlowReturnedData['integrationRun']) && $workFlowReturnedData['integrationRun']) {
420 383 $eventScheuled = wp_schedule_single_event(time(), 'bitforms_exec_integrations', [$triggerData['integrations'], $workFlowReturnedData['fields'], $triggerData['formID'], $triggerData['entryID'], $triggerData['logID']]);
421 384 $scheduleTime = wp_next_scheduled('bitforms_exec_integrations', [$triggerData['integrations'], $workFlowReturnedData['fields'], $triggerData['formID'], $triggerData['entryID'], $triggerData['logID']]);
422 385 $responseData['cron'] = get_site_url(null, "/wp-cron.php?doing_wp_cron&{$scheduleTime}");
423 386 } elseif (!empty($triggerData['integrations']) || !empty($triggerData['mail']) || isset($workFlowReturnedData['integrationRun']) && !$workFlowReturnedData['integrationRun']) {
@@ -426,15 +389,9 @@
426 389 $triggerData['dbl_opt_donf'] = $workFlowReturnedData['integrationDetails'];
427 390 $triggerData['dbl_opt_dflt_template'] = $workFlowReturnedData['dflt_template'];
428 391 $triggerData['integrationRun'] = $workFlowReturnedData['integrationRun'];
429 392 }
430 -
431 - // Generate one-time trigger token for security
432 - $triggerToken = wp_hash($triggerData['entryID'] . $triggerData['logID'] . time() . wp_rand(), 'nonce');
433 - $triggerData['trigger_token'] = $triggerToken;
434 -
435 393 set_transient("bitform_trigger_transient_{$entryID}", $triggerData, HOUR_IN_SECONDS);
436 - $queuedAt = current_time('mysql');
437 394 $entryLog = new FormEntryLogModel();
438 395 $queueuEntry = $entryLog->log_history_insert(
439 396 [
440 397 'log_id' => $triggerData['logID'],
@@ -440,32 +397,16 @@
440 397 'log_id' => $triggerData['logID'],
441 398 'integration_id' => 0,
442 399 'api_type' => wp_json_encode(['type' => 'trigger', 'type_name' => 'Workflow', 'on' => $opType]),
443 400 'response_type' => 'queued',
444 - // 'trigger' is a durable copy of the transient payload: object-cache hosts can
445 - // evict the transient (and it expires after 1h), which would otherwise lose the run
446 - 'response_obj' => wp_json_encode(['status' => 'queued', 'queued_at' => $queuedAt, 'trigger' => $triggerData]),
447 - 'created_at' => $queuedAt,
401 + 'response_obj' => wp_json_encode(['status' => 'queued']),
402 + 'created_at' => current_time('mysql'),
448 403 ]
449 404 );
450 - if (\is_int($queueuEntry)) {
451 - // Safety net: the browser-fired bitforms_trigger_workflow request can be lost
452 - // (navigation abort, WAF, network drop). This event re-runs the queued workflow;
453 - // it no-ops if the browser trigger already claimed the run.
454 - wp_schedule_single_event(
455 - time() + 2 * MINUTE_IN_SECONDS,
456 - 'bitforms_reclaim_workflow',
457 - [(int) $entryID, (int) $triggerData['formID'], (int) $triggerData['logID'], $queueuEntry]
458 - );
459 - if (!wp_next_scheduled('bitforms_reclaim_sweep')) {
460 - wp_schedule_event(time() + HOUR_IN_SECONDS, 'hourly', 'bitforms_reclaim_sweep');
461 - }
462 - }
463 405 $responseData['cronNotOk'] = [
464 406 $triggerData['entryID'],
465 407 $triggerData['logID'],
466 408 $queueuEntry,
467 - $triggerToken, // Add one-time token as 4th element
468 409 ];
469 410 }
470 411 return $responseData;
471 412 }
@@ -496,12 +437,11 @@
496 437 /**
497 438 *
498 439 * @param mixed $fieldValues
499 440 * @param mixed $returnRepeaterValue default is array
500 - * @param mixed $formID form ID required for string formatting
501 441 * @return array and repeater field value as array or string
502 442 */
503 - public static function formattedRepeaterValue($fieldValues, $returnRepeaterValue = '', $formID = null)
443 + public static function formattedRepeaterValue($fieldValues, $returnRepeaterValue = '')
504 444 {
505 445 // get the repeater field and store it in an array
506 446 $repeaterField = [];
507 447 foreach ($fieldValues as $key => $value) {
@@ -510,15 +450,14 @@
510 450 }
511 451 $repeaterField[] = $key;
512 452 }
513 453
514 - // put the value of the child key to the parent repeater field\
454 + // put the value of the child key to the parent repeater field
515 455 foreach ($repeaterField as $key) {
516 456 list($parentKey, $childKey) = explode('.', $key);
517 - $repeatValues = isset($fieldValues[$parentKey]) ? $fieldValues[$parentKey] : $key;
457 + $repeatValues = $fieldValues[$parentKey];
518 458
519 459 if (!is_array($repeatValues)) {
520 - $fieldValues[$key] = $fieldValues[$childKey];
521 460 continue;
522 461 }
523 462
524 463 foreach ($repeatValues as $value) {
@@ -534,163 +473,11 @@
534 473 }
535 474
536 475 // convert the array to string repeater field value
537 476 if ('string' === $returnRepeaterValue) {
538 - $form = new FormManager($formID);
539 477 foreach ($repeaterField as $key) {
540 - if (isset($fieldValues[$key]) && is_array($fieldValues[$key])) {
541 - $fieldValues[$key] = implode(', ', self::flattenArray($fieldValues[$key]));
542 - }
478 + $fieldValues[$key] = implode(', ', $fieldValues[$key]);
543 479 }
544 -
545 - foreach ($fieldValues as $key=>$value) {
546 - if (!$form->isRepeaterField($key)) {
547 - continue;
548 - }
549 - $repeaterValues = $fieldValues[$key];
550 -
551 - $newFieldValues = self::constructRepeaterValue($repeaterValues, $formID);
552 -
553 - $fieldValues[$key] = is_array($newFieldValues) ? json_encode(array_values($newFieldValues), JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) : $fieldValues[$key];
554 - }
555 480 }
556 -
557 481 return $fieldValues;
558 482 }
559 -
560 - private static function flattenArray($array)
561 - {
562 - return array_map(function ($val) {
563 - if (is_array($val)) {
564 - return count($val) > 1 ? ('[' . implode(', ') . ']') : implode(', ', $val);
565 - } else {
566 - return $val;
567 - }
568 - }, $array);
569 - }
570 -
571 - /**
572 - * Replace file field value with web url
573 - * @param mixed $fieldValues
574 - * @param number $formId
575 - * @param number $entryId
576 - * @return mixed $fieldValues
577 - */
578 - public static function handleFileUrl($fieldValues, $formId, $entryId)
579 - {
580 - if (!$formId || !$entryId) {
581 - return $fieldValues;
582 - }
583 - $form = FormManager::getInstance($formId);
584 - $formFields = $form->getFields();
585 -
586 - $fldsIncludeFile = ['file-up', 'advanced-file-up', 'repeater', 'signature'];
587 - foreach ($fieldValues as $key =>$value) {
588 - $fldTyp = $formFields[$key]['type'] ?? null;
589 - if (!in_array($fldTyp, $fldsIncludeFile)) {
590 - continue;
591 - }
592 -
593 - if ('repeater' === $fldTyp && is_array($value)) {
594 - foreach ($value as $rIndex => $rValue) {
595 - foreach ($rValue as $subKey=>$subValue) {
596 - if (!in_array($formFields[$subKey]['type'], $fldsIncludeFile)) {
597 - continue;
598 - }
599 - // for file fields inside repeater
600 - if (is_array($subValue)) {
601 - foreach ($subValue as $fileIndex=>$fileValue) {
602 - $fieldValues[$key][$rIndex][$subKey][$fileIndex] = self::constructFileUrl($formId, $entryId, $fileValue);
603 - }
604 - } else {
605 - // for signature field inside repeater;
606 - $fieldValues[$key][$rIndex][$subKey] = self::constructFileUrl($formId, $entryId, $subValue);
607 - }
608 - }
609 - }
610 - }
611 -
612 - if (in_array($fldTyp, ['file-up', 'advanced-file-up', 'signature'], true)) {
613 - if (is_array($value)) {
614 - foreach ($value as $fldIndex=>$fldValue) {
615 - $fieldValues[$key][$fldIndex] = self::constructFileUrl($formId, $entryId, $fldValue);
616 - }
617 - } else {
618 - $fieldValues[$key] = self::constructFileUrl($formId, $entryId, $value);
619 - }
620 - }
621 - }
622 -
623 - return $fieldValues;
624 - }
625 -
626 - public static function assignRepeaterFieldValue($fieldValues, $formId)
627 - {
628 - if (!$formId) {
629 - return $fieldValues;
630 - }
631 - $form = new FormManager($formId);
632 - $formFields = $form->getFields();
633 -
634 - foreach ($fieldValues as $key=>$value) {
635 - if (!$form->isRepeaterField($key)) {
636 - continue;
637 - }
638 -
639 - if ('repeater' === $formFields[$key]['type']) {
640 - if (is_array($value)) {
641 - foreach ($value as $rIndex=>$rValue) {
642 - foreach ($rValue as $subKey =>$subValue) {
643 - $fieldValues[$subKey] = $subValue;
644 - }
645 - }
646 - }
647 - }
648 - }
649 -
650 - return $fieldValues;
651 - }
652 -
653 - public static function constructRepeaterValue($repeaterValues, $formId)
654 - {
655 - if (!$formId || !is_array($repeaterValues)) {
656 - return $repeaterValues;
657 - }
658 - $form = FormManager::getInstance($formId);
659 - $formFields = $form->getFields();
660 - $newRptrValue = [];
661 - foreach ($repeaterValues as $key=>$value) {
662 - $newRepeatedValue = [];
663 - foreach ($value as $rKey =>$rValue) {
664 - $fldLbl = isset($formFields[$rKey]) ? $formFields[$rKey]['label'] : $rKey;
665 - $newRepeatedValue[$fldLbl] = $rValue;
666 - }
667 - $newRptrValue[$key] = $newRepeatedValue;
668 - }
669 -
670 - return $newRptrValue;
671 - }
672 -
673 - private static function constructFileUrl($formId, $entryId, $fileName)
674 - {
675 - $webPath = Helpers::getWebPathWithEncryptedEntryId($formId, $entryId);
676 -
677 - return rtrim($webPath, '/') . '/' . ltrim($fileName, '/');
678 - }
679 -
680 - // private static function constructFileUrl($formId, $entryId, $fileName)
681 - // {
682 - // $restBaseUrl = get_rest_url();
683 - // if (!$restBaseUrl) {
684 - // return $fileName;
685 - // }
686 - // //http://bitlocal.local/wp-json/bitform/v1/bitform-file-download/?formID=1&entryID=16&fileID=bit-form-1005.pdf
687 - // $queryParam = build_query([
688 - // 'formID' => $formId,
689 - // 'entryID' => $entryId,
690 - // 'fileID' => $fileName
691 - // ]);
692 -
693 - // $path = $restBaseUrl . 'bitform' . '/' . 'v1' . '/' . 'bitform-file-download' . '/' . '?' . $queryParam;
694 - // return rtrim($path, '/');
695 - // }
696 483 }