diff --git a/application/Espo/Controllers/LeadCapture.php b/application/Espo/Controllers/LeadCapture.php index 4267596504..49ae1cae89 100644 --- a/application/Espo/Controllers/LeadCapture.php +++ b/application/Espo/Controllers/LeadCapture.php @@ -29,28 +29,33 @@ namespace Espo\Controllers; -use Espo\Services\LeadCapture as Service; +use Espo\Core\Exceptions\ForbiddenSilent; use Espo\Core\Exceptions\Forbidden; use Espo\Core\Exceptions\BadRequest; use Espo\Core\Exceptions\NotFound; -use Espo\Core\{ - Controllers\Record, - Api\Request, - Api\Response, -}; +use Espo\Core\Api\Request; +use Espo\Core\Api\Response; +use Espo\Core\Controllers\Record; +use Espo\Core\Exceptions\Error; +use Espo\Tools\LeadCapture\LeadCapture as LeadCaptureService; use stdClass; class LeadCapture extends Record { + /** + * @throws BadRequest + * @throws NotFound + * @throws Error + */ public function postActionLeadCapture(Request $request, Response $response): bool { - $params = $request->getRouteParams(); $data = $request->getParsedBody(); + $apiKey = $request->getRouteParam('apiKey'); - if (empty($params['apiKey'])) { + if (!$apiKey) { throw new BadRequest('No API key provided.'); } @@ -58,20 +63,24 @@ class LeadCapture extends Record $response->setHeader('Access-Control-Allow-Origin', $allowOrigin); - $this->getLeadCaptureService()->leadCapture($params['apiKey'], $data); + $this->getLeadCaptureService()->capture($apiKey, $data); return true; } + /** + * @throws BadRequest + * @throws NotFound + */ public function optionsActionLeadCapture(Request $request, Response $response): bool { - $params = $request->getRouteParams(); + $apiKey = $request->getRouteParam('apiKey'); - if (empty($params['apiKey'])) { + if (!$apiKey) { throw new BadRequest('No API key provided.'); } - if (!$this->getLeadCaptureService()->isApiKeyValid($params['apiKey'])) { + if (!$this->getLeadCaptureService()->isApiKeyValid($apiKey)) { throw new NotFound(); } @@ -84,6 +93,11 @@ class LeadCapture extends Record return true; } + /** + * @throws BadRequest + * @throws NotFound + * @throws ForbiddenSilent + */ public function postActionGenerateNewApiKey(Request $request): stdClass { $data = $request->getParsedBody(); @@ -103,16 +117,15 @@ class LeadCapture extends Record */ public function getActionSmtpAccountDataList(): array { - if (!$this->getUser()->isAdmin()) { + if (!$this->user->isAdmin()) { throw new Forbidden(); } return $this->getLeadCaptureService()->getSmtpAccountDataList(); } - private function getLeadCaptureService(): Service + private function getLeadCaptureService(): LeadCaptureService { - /** @var Service */ - return $this->getRecordService(); + return $this->injectableFactory->create(LeadCaptureService::class); } } diff --git a/application/Espo/EntryPoints/ConfirmOptIn.php b/application/Espo/EntryPoints/ConfirmOptIn.php index bd94b5dba3..337100b148 100644 --- a/application/Espo/EntryPoints/ConfirmOptIn.php +++ b/application/Espo/EntryPoints/ConfirmOptIn.php @@ -29,16 +29,16 @@ namespace Espo\EntryPoints; -use Espo\Services\LeadCapture as Service; +use Espo\Tools\LeadCapture\LeadCapture as Service; -use Espo\Core\{ - Exceptions\BadRequest, - Exceptions\Error, - EntryPoint\EntryPoint, - EntryPoint\Traits\NoAuth, - Utils\Client\ActionRenderer, - Api\Request, - Api\Response}; +use Espo\Core\Api\Request; +use Espo\Core\Api\Response; +use Espo\Core\EntryPoint\EntryPoint; +use Espo\Core\EntryPoint\Traits\NoAuth; +use Espo\Core\Exceptions\BadRequest; +use Espo\Core\Exceptions\Error; +use Espo\Core\Exceptions\NotFound; +use Espo\Core\Utils\Client\ActionRenderer; class ConfirmOptIn implements EntryPoint { @@ -56,7 +56,7 @@ class ConfirmOptIn implements EntryPoint /** * @throws BadRequest * @throws Error - * @throws \Espo\Core\Exceptions\NotFound + * @throws NotFound */ public function run(Request $request, Response $response): void { diff --git a/application/Espo/Services/LeadCapture.php b/application/Espo/Services/LeadCapture.php index 32d6512cfb..7dc2589859 100644 --- a/application/Espo/Services/LeadCapture.php +++ b/application/Espo/Services/LeadCapture.php @@ -30,37 +30,21 @@ namespace Espo\Services; use Espo\Entities\LeadCapture as LeadCaptureEntity; - -use Espo\Entities\InboundEmail; - -use Espo\{ - Modules\Crm\Entities\Lead, - ORM\Entity, - Tools\LeadCapture\LeadCapture as Tool, -}; - -use Espo\Core\{ - Exceptions\Forbidden, - Exceptions\NotFound, - Exceptions\Error, - Utils\Util, -}; - -use stdClass; +use Espo\Modules\Crm\Entities\Lead; +use Espo\ORM\Entity; +use Espo\Tools\LeadCapture\LeadCapture as Tool; +use Espo\Core\Utils\Util; /** - * @extends Record<\Espo\Entities\LeadCapture> + * @extends Record */ class LeadCapture extends Record { - /** - * @var string[] - */ + /** @var string[] */ protected $readOnlyAttributeList = ['apiKey']; /** * @param LeadCaptureEntity $entity - * @throws Error */ public function prepareEntityForOutput(Entity $entity) { @@ -106,12 +90,7 @@ class LeadCapture extends Record foreach ($attributeList as $i => $attribute) { $value = strtoupper(Util::camelCaseToUnderscore($attribute)); - if ( - in_array( - $seed->getAttributeType($attribute), - [Entity::VARCHAR, Entity::TEXT] - ) - ) { + if (in_array($seed->getAttributeType($attribute), [Entity::VARCHAR, Entity::TEXT])) { $value = '"' . $value . '"'; } @@ -131,135 +110,13 @@ class LeadCapture extends Record protected function beforeCreateEntity(Entity $entity, $data) { - $apiKey = $this->generateApiKey(); + $apiKey = $this->createLeadCaptureService()->generateApiKey(); $entity->set('apiKey', $apiKey); } - /** - * @throws \Espo\Core\Exceptions\ForbiddenSilent - * @throws NotFound - * @throws Error - */ - public function generateNewApiKeyForEntity(string $id): Entity - { - $entity = $this->getEntity($id); - - if (!$entity) { - throw new NotFound(); - } - - $apiKey = $this->generateApiKey(); - - $entity->set('apiKey', $apiKey); - - $this->entityManager->saveEntity($entity); - - $this->prepareEntityForOutput($entity); - - return $entity; - } - - public function generateApiKey(): string - { - return Util::generateApiKey(); - } - - public function isApiKeyValid(string $apiKey): bool - { - $leadCapture = $this->entityManager - ->getRDBRepository(LeadCaptureEntity::ENTITY_TYPE) - ->where([ - 'apiKey' => $apiKey, - 'isActive' => true, - ]) - ->findOne(); - - if ($leadCapture) { - return true; - } - - return false; - } - - protected function createTool(): Tool + protected function createLeadCaptureService(): Tool { return $this->injectableFactory->create(Tool::class); } - - /** - * @throws \Espo\Core\Exceptions\BadRequest - * @throws NotFound - * @throws Error - */ - public function leadCapture(string $apiKey, stdClass $data): void - { - $this->createTool()->capture($apiKey, $data); - } - - /** - * @throws Error - */ - public function jobOptInConfirmation(stdClass $data): void - { - if (empty($data->id)) { - throw new Error(); - } - - $this->createTool()->sendOptInConfirmation($data->id); - } - - /** - * @throws \Espo\Core\Exceptions\BadRequest - * @throws Error - * @throws NotFound - * - * @return array{ - * status: 'success'|'expired', - * message: ?string, - * leadCaptureName?: ?string, - * leadCaptureId?: string, - * } - */ - public function confirmOptIn(string $id): array - { - return $this->createTool()->confirmOptIn($id); - } - - /** - * @return stdClass[] - * @throws Forbidden - */ - public function getSmtpAccountDataList(): array - { - if (!$this->user->isAdmin()) { - throw new Forbidden(); - } - - $dataList = []; - - $inboundEmailList = $this->entityManager - ->getRDBRepository(InboundEmail::ENTITY_TYPE) - ->where([ - 'useSmtp' => true, - 'status' => 'Active', - ['emailAddress!=' => ''], - ['emailAddress!=' => null], - ]) - ->find(); - - foreach ($inboundEmailList as $inboundEmail) { - $item = (object) []; - - $key = 'inboundEmail:' . $inboundEmail->getId(); - - $item->key = $key; - $item->emailAddress = $inboundEmail->getEmailAddress(); - $item->fromName = $inboundEmail->getFromName(); - - $dataList[] = $item; - } - - return $dataList; - } } diff --git a/application/Espo/Tools/LeadCapture/Jobs/OptInConfirmation.php b/application/Espo/Tools/LeadCapture/Jobs/OptInConfirmation.php new file mode 100644 index 0000000000..1e3513bbca --- /dev/null +++ b/application/Espo/Tools/LeadCapture/Jobs/OptInConfirmation.php @@ -0,0 +1,64 @@ +leadCapture = $leadCapture; + } + + /** + * @throws BadRequest + * @throws Error + * @throws NotFound + */ + public function run(Data $data): void + { + $uniqueId = $data->get('id'); + + if (!$uniqueId) { + throw new RuntimeException(); + } + + $this->leadCapture->confirmOptIn($uniqueId); + } +} diff --git a/application/Espo/Tools/LeadCapture/LeadCapture.php b/application/Espo/Tools/LeadCapture/LeadCapture.php index 469c35a978..aa087b7c6c 100644 --- a/application/Espo/Tools/LeadCapture/LeadCapture.php +++ b/application/Espo/Tools/LeadCapture/LeadCapture.php @@ -29,6 +29,12 @@ namespace Espo\Tools\LeadCapture; +use Espo\Core\Exceptions\Forbidden; +use Espo\Core\Exceptions\ForbiddenSilent; +use Espo\Core\Job\JobSchedulerFactory; +use Espo\Core\Record\ServiceContainer; +use Espo\Core\Utils\Util; +use Espo\Entities\User; use Espo\Modules\Crm\Services\Campaign as CampaignService; use Espo\Services\EmailTemplate as EmailTemplateService; use Espo\Services\InboundEmail as InboundEmailService; @@ -56,13 +62,13 @@ use Espo\Core\Utils\Log; use Espo\Entities\Email; use Espo\Entities\EmailTemplate; use Espo\Entities\InboundEmail; -use Espo\Entities\Job; use Espo\Entities\LeadCapture as LeadCaptureEntity; use Espo\Entities\LeadCaptureLogRecord; use Espo\Modules\Crm\Entities\Campaign; use Espo\Modules\Crm\Entities\TargetList; use Espo\ORM\Entity; +use Espo\Tools\LeadCapture\Jobs\OptInConfirmation; use stdClass; use DateTime; @@ -81,6 +87,9 @@ class LeadCapture private EmailTemplateService $emailTemplateService; private InboundEmailService $inboundEmailService; private FieldValidationManager $fieldValidationManager; + private JobSchedulerFactory $jobSchedulerFactory; + private User $user; + private ServiceContainer $recordServiceContainer; public function __construct( EntityManager $entityManager, @@ -94,7 +103,10 @@ class LeadCapture CampaignService $campaignService, EmailTemplateService $emailTemplateService, InboundEmailService $inboundEmailService, - FieldValidationManager $fieldValidationManager + FieldValidationManager $fieldValidationManager, + JobSchedulerFactory $jobSchedulerFactory, + User $user, + ServiceContainer $recordServiceContainer ) { $this->entityManager = $entityManager; $this->fieldUtil = $fieldUtil; @@ -108,6 +120,9 @@ class LeadCapture $this->emailTemplateService = $emailTemplateService; $this->inboundEmailService = $inboundEmailService; $this->fieldValidationManager = $fieldValidationManager; + $this->jobSchedulerFactory = $jobSchedulerFactory; + $this->user = $user; + $this->recordServiceContainer = $recordServiceContainer; } /** @@ -219,14 +234,14 @@ class LeadCapture $this->entityManager->saveEntity($uniqueId); - $this->entityManager->createEntity(Job::ENTITY_TYPE, [ - 'serviceName' => 'LeadCapture', - 'methodName' => 'jobOptInConfirmation', - 'data' => (object) [ + $this->jobSchedulerFactory + ->create() + ->setClassName(OptInConfirmation::class) + ->setData([ 'id' => $uniqueId->getIdValue(), - ], - 'queue' => QueueName::E0, - ]); + ]) + ->setQueue(QueueName::E0) + ->schedule(); } /** @@ -852,4 +867,86 @@ class LeadCapture $this->entityManager->saveEntity($logRecord); } + + public function isApiKeyValid(string $apiKey): bool + { + $leadCapture = $this->entityManager + ->getRDBRepository(LeadCaptureEntity::ENTITY_TYPE) + ->where([ + 'apiKey' => $apiKey, + 'isActive' => true, + ]) + ->findOne(); + + if ($leadCapture) { + return true; + } + + return false; + } + + /** + * @throws ForbiddenSilent + * @throws NotFound + */ + public function generateNewApiKeyForEntity(string $id): Entity + { + $service = $this->recordServiceContainer->get(LeadCaptureEntity::ENTITY_TYPE); + + $entity = $service->getEntity($id); + + if (!$entity) { + throw new NotFound(); + } + + $entity->set('apiKey', $this->generateApiKey()); + + $this->entityManager->saveEntity($entity); + + $service->prepareEntityForOutput($entity); + + return $entity; + } + + public function generateApiKey(): string + { + return Util::generateApiKey(); + } + + /** + * @return stdClass[] + * @throws Forbidden + */ + public function getSmtpAccountDataList(): array + { + if (!$this->user->isAdmin()) { + throw new Forbidden(); + } + + $dataList = []; + + $inboundEmailList = $this->entityManager + ->getRDBRepositoryByClass(InboundEmail::class) + ->where([ + 'useSmtp' => true, + 'status' => InboundEmail::STATUS_ACTIVE, + ['emailAddress!=' => ''], + ['emailAddress!=' => null], + ]) + ->find(); + + foreach ($inboundEmailList as $inboundEmail) { + $item = (object) []; + + $key = 'inboundEmail:' . $inboundEmail->getId(); + + $item->key = $key; + $item->emailAddress = $inboundEmail->getEmailAddress(); + $item->fromName = $inboundEmail->getFromName(); + + $dataList[] = $item; + } + + return $dataList; + } }