|
17 | 17 | use ProcessMaker\Events\ActivityAssigned; |
18 | 18 | use ProcessMaker\Events\ActivityReassignment; |
19 | 19 | use ProcessMaker\Facades\WorkflowUserManager; |
| 20 | +use ProcessMaker\Managers\DataManager; |
| 21 | +use ProcessMaker\Models\MustacheExpressionEvaluator; |
20 | 22 | use ProcessMaker\Nayra\Bpmn\TokenTrait; |
21 | 23 | use ProcessMaker\Nayra\Contracts\Bpmn\ActivityInterface; |
22 | 24 | use ProcessMaker\Nayra\Contracts\Bpmn\FlowElementInterface; |
@@ -1440,6 +1442,59 @@ public function reassign($toUserId, User $requestingUser, $comments = '') |
1440 | 1442 | } |
1441 | 1443 | } |
1442 | 1444 |
|
| 1445 | + /** |
| 1446 | + * Build context for Mustache (end event external URL). Same as scripts/screens: _user, _request, process data, APP_URL. |
| 1447 | + */ |
| 1448 | + private function getElementDestinationMustacheContext(): array |
| 1449 | + { |
| 1450 | + try { |
| 1451 | + $context = (new DataManager())->getData($this); |
| 1452 | + } catch (Throwable $e) { |
| 1453 | + Log::warning('Failed to load Mustache context via DataManager, falling back to request data', [ |
| 1454 | + 'token_id' => $this->id, |
| 1455 | + 'error' => $e->getMessage(), |
| 1456 | + ]); |
| 1457 | + $request = $this->processRequest; |
| 1458 | + $context = array_merge($request->data ?? [], $request ? (new DataManager())->updateRequestMagicVariable([], $request) : []); |
| 1459 | + $user = $this->user ?? auth()->user(); |
| 1460 | + if ($user) { |
| 1461 | + $userData = $user->attributesToArray(); |
| 1462 | + unset($userData['remember_token']); |
| 1463 | + $context['_user'] = $userData; |
| 1464 | + } |
| 1465 | + } |
| 1466 | + |
| 1467 | + $context['APP_URL'] = config('app.url'); |
| 1468 | + |
| 1469 | + // Never expose remember_token to Mustache (defense in depth; DataManager/fallback may already strip it) |
| 1470 | + if (isset($context['_user']) && is_array($context['_user'])) { |
| 1471 | + unset($context['_user']['remember_token']); |
| 1472 | + } |
| 1473 | + |
| 1474 | + // Normalize to plain arrays/scalars so Mustache resolves all keys (common PHP idiom) |
| 1475 | + $json = json_encode($context, JSON_THROW_ON_ERROR); |
| 1476 | + $normalized = json_decode($json, true, 512, JSON_THROW_ON_ERROR); |
| 1477 | + |
| 1478 | + return is_array($normalized) ? $normalized : []; |
| 1479 | + } |
| 1480 | + |
| 1481 | + /** |
| 1482 | + * Resolve Mustache in end event external URL. FEEL is not supported here; use Mustache only. |
| 1483 | + * Context: APP_URL, _request, _user, process variables (same as getElementDestinationMustacheContext). |
| 1484 | + * |
| 1485 | + * Example (Mustache): |
| 1486 | + * {{APP_URL}}/admin/users/{{_request.id}}/edit -> https://example.com/admin/users/123/edit |
| 1487 | + * {{APP_URL}}/webentry/{{_request.id}} -> https://example.com/webentry/123 |
| 1488 | + * {{APP_URL}}/path/{{my_process_var}} -> uses process variable my_process_var |
| 1489 | + */ |
| 1490 | + private function resolveElementDestinationUrl(string $url): string |
| 1491 | + { |
| 1492 | + $url = html_entity_decode($url, ENT_QUOTES | ENT_HTML401, 'UTF-8'); |
| 1493 | + $context = $this->getElementDestinationMustacheContext(); |
| 1494 | + |
| 1495 | + return (new MustacheExpressionEvaluator())->render($url, $context); |
| 1496 | + } |
| 1497 | + |
1443 | 1498 | /** |
1444 | 1499 | * Determines the destination based on the type of element destination property |
1445 | 1500 | * |
@@ -1481,6 +1536,11 @@ private function getElementDestination($elementDestinationType, $elementDestinat |
1481 | 1536 | $elementDestination = $elementDestinationProp['value']['url'] ?? null; |
1482 | 1537 | } |
1483 | 1538 | } |
| 1539 | + if (is_string($elementDestination) && $elementDestination !== '') { |
| 1540 | + if ($elementDestinationType === 'externalURL' || $elementDestinationType === 'customDashboard') { |
| 1541 | + $elementDestination = $this->resolveElementDestinationUrl($elementDestination); |
| 1542 | + } |
| 1543 | + } |
1484 | 1544 | break; |
1485 | 1545 | case 'taskList': |
1486 | 1546 | $elementDestination = route('tasks.index'); |
|
0 commit comments