|
16 | 16 | use PDOException; |
17 | 17 | use ProcessMaker\Facades\WorkflowManager; |
18 | 18 | use ProcessMaker\Jobs\StartEventConditional; |
| 19 | +use ProcessMaker\Models\EnvironmentVariable; |
19 | 20 | use ProcessMaker\Models\Process; |
20 | 21 | use ProcessMaker\Models\ProcessRequest; |
21 | 22 | use ProcessMaker\Models\ProcessRequestLock; |
22 | 23 | use ProcessMaker\Models\ScheduledTask; |
| 24 | +use ProcessMaker\Models\Setting; |
23 | 25 | use ProcessMaker\Models\TimerExpression; |
24 | 26 | use ProcessMaker\Nayra\Bpmn\Models\BoundaryEvent; |
25 | 27 | use ProcessMaker\Nayra\Bpmn\Models\DatePeriod; |
@@ -398,13 +400,211 @@ public function executeTimerStartEvent(ScheduledTask $task, $config) |
398 | 400 | if (!$definitions->findElementById($config->element_id)) { |
399 | 401 | return; |
400 | 402 | } |
| 403 | + |
| 404 | + if ($this->shouldSkipHandleRepliesTimerStart($process)) { |
| 405 | + Log::info('Skipping Actions By Email Handle Replies timer event because ABE inbound mail configuration is not adequate', [ |
| 406 | + 'process_id' => $process->id, |
| 407 | + 'process_name' => $process->name, |
| 408 | + ]); |
| 409 | + |
| 410 | + return; |
| 411 | + } |
| 412 | + |
401 | 413 | $event = $definitions->getEvent($config->element_id); |
402 | 414 | $data = []; |
403 | 415 |
|
404 | 416 | //Trigger the start event |
405 | 417 | $processRequest = WorkflowManager::triggerStartEvent($process, $event, $data); |
406 | 418 | } |
407 | 419 |
|
| 420 | + /** |
| 421 | + * Determine if the timer should be skipped for the Actions By Email handle replies process. |
| 422 | + * |
| 423 | + * @param Process $process |
| 424 | + * @return bool |
| 425 | + */ |
| 426 | + private function shouldSkipHandleRepliesTimerStart(Process $process): bool |
| 427 | + { |
| 428 | + if (!$this->isHandleRepliesProcess($process)) { |
| 429 | + return false; |
| 430 | + } |
| 431 | + |
| 432 | + return !$this->hasAdequateAbeInboundConfiguration(); |
| 433 | + } |
| 434 | + |
| 435 | + /** |
| 436 | + * Whether Actions By Email has enough configuration to poll inbound mail (IMAP or OAuth). |
| 437 | + * |
| 438 | + * This is a heuristic in core: the connector may add more keys; we avoid starting the |
| 439 | + * Handle Replies timer when the mailbox clearly is not set up (FOUR-30587). |
| 440 | + * |
| 441 | + * @return bool |
| 442 | + */ |
| 443 | + private function hasAdequateAbeInboundConfiguration(): bool |
| 444 | + { |
| 445 | + if (!Setting::readyToUseSettingsDatabase()) { |
| 446 | + return false; |
| 447 | + } |
| 448 | + |
| 449 | + $authMethodIndex = (int) ($this->getAbeInboundSettingValue(['abe_imap_auth_method']) ?? 0); |
| 450 | + $username = $this->getAbeInboundSettingValue(['abe_imap_username', 'email_connector_mail_username']); |
| 451 | + |
| 452 | + if (!$this->hasValue($username)) { |
| 453 | + return false; |
| 454 | + } |
| 455 | + |
| 456 | + if ($authMethodIndex === 1) { |
| 457 | + return $this->hasEnvironmentVariables([ |
| 458 | + 'ABE_GMAIL_API_CLIENT_ID', |
| 459 | + 'ABE_GMAIL_API_SECRET', |
| 460 | + 'ABE_GMAIL_API_ACCESS_TOKEN', |
| 461 | + 'ABE_GMAIL_API_REFRESH_TOKEN', |
| 462 | + ]); |
| 463 | + } |
| 464 | + |
| 465 | + if ($authMethodIndex === 2) { |
| 466 | + return $this->hasEnvironmentVariables([ |
| 467 | + 'ABE_OFFICE_365_CLIENT_ID', |
| 468 | + 'ABE_OFFICE_365_TENANT_ID', |
| 469 | + 'ABE_OFFICE_365_SECRET', |
| 470 | + 'ABE_OFFICE_365_ACCESS_TOKEN', |
| 471 | + 'ABE_OFFICE_365_REFRESH_TOKEN', |
| 472 | + 'ABE_OFFICE_365_ACCESS_TOKEN_EXPIRE_DATE', |
| 473 | + ]); |
| 474 | + } |
| 475 | + |
| 476 | + return $this->hasStandardAbeInboundConfiguration(); |
| 477 | + } |
| 478 | + |
| 479 | + /** |
| 480 | + * IMAP configuration for standard authentication mode. |
| 481 | + */ |
| 482 | + private function hasStandardAbeInboundConfiguration(): bool |
| 483 | + { |
| 484 | + $password = $this->getAbeInboundSettingValue(['abe_imap_password', 'email_connector_mail_password']); |
| 485 | + if (!$this->hasValue($password)) { |
| 486 | + return false; |
| 487 | + } |
| 488 | + |
| 489 | + $inboxUri = $this->getAbeInboundSettingValue(['abe_imap_inbox_uri']); |
| 490 | + if ($this->hasValue($inboxUri)) { |
| 491 | + return true; |
| 492 | + } |
| 493 | + |
| 494 | + $server = $this->getAbeInboundSettingValue(['abe_imap_server', 'email_connector_mail_host']); |
| 495 | + $port = $this->getAbeInboundSettingValue(['abe_imap_port', 'email_connector_mail_port']); |
| 496 | + |
| 497 | + return $this->hasValue($server) && $this->hasValue($port); |
| 498 | + } |
| 499 | + |
| 500 | + /** |
| 501 | + * Reads the first non-empty value from supported Actions By Email / mail settings keys. |
| 502 | + * |
| 503 | + * @param array $keys |
| 504 | + * @return string|null |
| 505 | + */ |
| 506 | + private function getAbeInboundSettingValue(array $keys): ?string |
| 507 | + { |
| 508 | + $settings = Setting::query() |
| 509 | + ->whereIn('key', $keys) |
| 510 | + ->get() |
| 511 | + ->keyBy('key'); |
| 512 | + |
| 513 | + if ($settings->isEmpty()) { |
| 514 | + return null; |
| 515 | + } |
| 516 | + |
| 517 | + foreach ($keys as $key) { |
| 518 | + $setting = $settings->get($key); |
| 519 | + if (!$setting) { |
| 520 | + continue; |
| 521 | + } |
| 522 | + |
| 523 | + $value = $this->extractSettingValue($setting->config); |
| 524 | + if ($this->hasValue($value)) { |
| 525 | + return (string) $value; |
| 526 | + } |
| 527 | + } |
| 528 | + |
| 529 | + return null; |
| 530 | + } |
| 531 | + |
| 532 | + /** |
| 533 | + * Normalize setting values from different possible setting formats. |
| 534 | + * |
| 535 | + * @param mixed $value |
| 536 | + * @return mixed |
| 537 | + */ |
| 538 | + private function extractSettingValue($value) |
| 539 | + { |
| 540 | + if (is_object($value)) { |
| 541 | + $value = (array) $value; |
| 542 | + } |
| 543 | + |
| 544 | + if (is_array($value)) { |
| 545 | + if (array_key_exists('value', $value)) { |
| 546 | + return $value['value']; |
| 547 | + } |
| 548 | + |
| 549 | + return $value; |
| 550 | + } |
| 551 | + |
| 552 | + return $value; |
| 553 | + } |
| 554 | + |
| 555 | + /** |
| 556 | + * Check if a setting value is present and not empty. |
| 557 | + * |
| 558 | + * @param mixed $value |
| 559 | + * @return bool |
| 560 | + */ |
| 561 | + private function hasValue($value): bool |
| 562 | + { |
| 563 | + if (is_array($value)) { |
| 564 | + foreach ($value as $item) { |
| 565 | + if ($this->hasValue($item)) { |
| 566 | + return true; |
| 567 | + } |
| 568 | + } |
| 569 | + |
| 570 | + return false; |
| 571 | + } |
| 572 | + |
| 573 | + return is_scalar($value) && trim((string) $value) !== ''; |
| 574 | + } |
| 575 | + |
| 576 | + /** |
| 577 | + * Identify the handle replies process by name. |
| 578 | + * |
| 579 | + * @param Process $process |
| 580 | + * @return bool |
| 581 | + */ |
| 582 | + private function isHandleRepliesProcess(Process $process): bool |
| 583 | + { |
| 584 | + if ((string) $process->package_key === 'package-actions-by-email/handle-replies') { |
| 585 | + return true; |
| 586 | + } |
| 587 | + |
| 588 | + $name = (string) $process->name; |
| 589 | + |
| 590 | + return stripos($name, 'actions by email') !== false && stripos($name, 'handle replies') !== false; |
| 591 | + } |
| 592 | + |
| 593 | + private function hasEnvironmentVariables(array $names): bool |
| 594 | + { |
| 595 | + $values = EnvironmentVariable::query() |
| 596 | + ->whereIn('name', $names) |
| 597 | + ->pluck('value', 'name'); |
| 598 | + |
| 599 | + foreach ($names as $name) { |
| 600 | + if (!isset($values[$name]) || trim((string) $values[$name]) === '') { |
| 601 | + return false; |
| 602 | + } |
| 603 | + } |
| 604 | + |
| 605 | + return true; |
| 606 | + } |
| 607 | + |
408 | 608 | /** |
409 | 609 | * Execute a timer start event |
410 | 610 | * |
|
0 commit comments