| @@ -5,8 +5,9 @@ | ||
| 5 | 5 | use BitCode\BitForm\Core\Cryptography\Cryptography; |
| 6 | 6 | use BitCode\BitForm\Core\Database\FormEntryModel; |
| 7 | 7 | use BitCode\BitForm\Core\Util\FileHandler; |
| 8 | 8 | use BitCode\BitForm\Core\Util\Log; |
| 9 | +use BitCode\BitForm\Core\WorkFlow\WorkflowExecutor; | |
| 9 | 10 | use Exception; |
| 10 | 11 | use WP_Error; |
| 11 | 12 | |
| 12 | 13 | class Helpers |
| @@ -86,16 +87,19 @@ | ||
| 86 | 87 | { |
| 87 | 88 | if ('' === trim($input)) { |
| 88 | 89 | return $input; |
| 89 | 90 | } |
| 91 | + // Line-boundary whitespace only. The previous regexes also rewrote the | |
| 92 | + // inside of string and template literals, corrupting generated config and | |
| 93 | + // custom JS. Inputs are already terser-minified, so the aggressive pass | |
| 94 | + // saved ~0.1% anyway. | |
| 90 | 95 | return preg_replace( |
| 91 | 96 | [ |
| 92 | - '/ {2,}/', | |
| 93 | - '/\s*=\s*/', | |
| 94 | - '/\s*,\s*/', | |
| 95 | - '/\s+(?=\(|\{|\:|\?)|\t|(?:\r?\n[ \t]*)+/s' | |
| 97 | + '/^[ \t]+/m', // leading indentation | |
| 98 | + '/[ \t]+$/m', // trailing whitespace | |
| 99 | + '/(?:\r?\n){3,}/', // 3+ consecutive newlines -> 2 | |
| 96 | 100 | ], |
| 97 | - [' ', '=', ',', ''], | |
| 101 | + ['', '', "\n\n"], | |
| 98 | 102 | $input |
| 99 | 103 | ); |
| 100 | 104 | } |
| 101 | 105 | |
| @@ -414,10 +418,17 @@ | ||
| 414 | 418 | |
| 415 | 419 | // Validate trigger token from transient |
| 416 | 420 | $transientData = get_transient("bitform_trigger_transient_{$entryID}"); |
| 417 | 421 | |
| 422 | + // Object caches (W3TC, SG Optimizer, Redis) can drop the transient before the | |
| 423 | + // browser trigger arrives; the queue log row holds the same payload durably. | |
| 418 | 424 | if (empty($transientData)) { |
| 419 | - Log::debug_log('Trigger token transient missing for entryID=' . $entryID . ', logID=' . $logID); | |
| 425 | + $queueLogId = isset($request->cronNotOk[2]) && is_numeric($request->cronNotOk[2]) ? absint($request->cronNotOk[2]) : 0; | |
| 426 | + $transientData = WorkflowExecutor::loadTriggerDataFromLog($queueLogId); | |
| 427 | + Log::debug_log('Trigger token transient missing for entryID=' . $entryID . ', logID=' . $logID . '; durable copy ' . (empty($transientData) ? 'not found' : 'used')); | |
| 428 | + } | |
| 429 | + | |
| 430 | + if (empty($transientData)) { | |
| 420 | 431 | return [ |
| 421 | 432 | 'valid' => false, |
| 422 | 433 | 'error' => 'Trigger token expired or missing', |
| 423 | 434 | 'triggerData' => null, |
| @@ -424,12 +435,12 @@ | ||
| 424 | 435 | 'isAdminBypass' => false |
| 425 | 436 | ]; |
| 426 | 437 | } |
| 427 | 438 | |
| 428 | - $triggerData = is_string($transientData) ? json_decode($transientData) : $transientData; | |
| 439 | + $triggerData = is_string($transientData) ? json_decode($transientData, true) : (array) $transientData; | |
| 429 | 440 | // Verify token matches and belongs to this entry/log |
| 430 | 441 | if ( |
| 431 | - !isset($triggerData['trigger_token']) | |
| 442 | + !isset($triggerData['trigger_token'], $triggerData['entryID'], $triggerData['logID']) | |
| 432 | 443 | || !hash_equals($triggerData['trigger_token'], $submittedToken) |
| 433 | 444 | || (int)$triggerData['entryID'] !== $entryID |
| 434 | 445 | || (int)$triggerData['logID'] !== $logID |
| 435 | 446 | ) { |