* @copyright Since 2007 PrestaShop SA and Contributors * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) */ namespace PrestaShop\PrestaShop\Core\Domain\CustomerService\CommandHandler; use Configuration; use Customer; use CustomerMessage; use CustomerThread; use Mail; use Order; use PrestaShop\PrestaShop\Core\ConstraintValidator\Constraints\CleanHtml; use PrestaShop\PrestaShop\Core\Domain\CustomerMessage\Command\AddOrderCustomerMessageCommand; use PrestaShop\PrestaShop\Core\Domain\CustomerMessage\CommandHandler\AddOrderCustomerMessageHandlerInterface; use PrestaShop\PrestaShop\Core\Domain\CustomerMessage\Exception\CannotSendEmailException; use PrestaShop\PrestaShop\Core\Domain\CustomerMessage\Exception\CustomerMessageConstraintException; use PrestaShop\PrestaShop\Core\Domain\CustomerMessage\Exception\CustomerMessageException; use PrestaShop\PrestaShop\Core\Domain\Order\Exception\OrderNotFoundException; use Symfony\Component\Validator\Validator\ValidatorInterface; use Symfony\Contracts\Translation\TranslatorInterface; use Tools; class AddOrderCustomerMessageHandler implements AddOrderCustomerMessageHandlerInterface { /** * @var int */ private $contextShopId; /** * @var int */ private $contextLanguageId; /** * @var int */ private $contextEmployeeId; /** * @var TranslatorInterface */ private $translator; /** * @var ValidatorInterface */ private $validator; /** * @param TranslatorInterface $translator * @param ValidatorInterface $validator * @param int $contextShopId * @param int $contextLanguageId * @param int $contextEmployeeId */ public function __construct( TranslatorInterface $translator, ValidatorInterface $validator, int $contextShopId, int $contextLanguageId, int $contextEmployeeId ) { $this->contextShopId = $contextShopId; $this->contextLanguageId = $contextLanguageId; $this->contextEmployeeId = $contextEmployeeId; $this->translator = $translator; $this->validator = $validator; } /** * {@inheritdoc} * * @throws CustomerMessageException * @throws OrderNotFoundException */ public function handle(AddOrderCustomerMessageCommand $command): void { $this->assertIsValidMessage($command->getMessage()); $order = new Order($command->getOrderId()->getValue()); if (0 >= $order->id) { throw new OrderNotFoundException($command->getOrderId(), "Order with id {$command->getOrderId()->getValue()} was not found"); } $customer = new Customer($order->id_customer); if (0 >= $customer->id) { throw new CustomerMessageException("Associated order customer with id {$command->getOrderId()->getValue()} was not found", CustomerMessageException::ORDER_CUSTOMER_NOT_FOUND); } $customerServiceThreadId = CustomerThread::getIdCustomerThreadByEmailAndIdOrder( $customer->email, $order->id ); if (!$customerServiceThreadId) { try { $customerServiceThreadId = $this->createCustomerMessageThread($order); } catch (\PrestaShopException $e) { throw new CustomerMessageException('An unexpected error occurred when creating customer message thread', 0, $e); } } try { $this->createMessage($customerServiceThreadId, $command); } catch (\PrestaShopException $e) { throw new CustomerMessageException('An unexpected error occurred when creating customer message', 0, $e); } $failedMailSentMessage = 'An unexpected error occurred when sending the email'; try { $isSent = $this->sendMail($customer, $order, $command); if (!$isSent) { throw new CannotSendEmailException($failedMailSentMessage); } } catch (\PrestaShopException $e) { throw new CannotSendEmailException($failedMailSentMessage, 0, $e); } } /** * @param string $message * * @throws CustomerMessageConstraintException */ private function assertIsValidMessage(string $message): void { $errors = $this->validator->validate($message, new CleanHtml()); if (0 !== \count($errors)) { throw new CustomerMessageConstraintException(sprintf('Given message "%s" contains javascript events or script tags', $message), CustomerMessageConstraintException::INVALID_MESSAGE); } } /** * Creates customer message thread which groups customer message in an order group. * * @param Order $order * * @return int * * @throws \PrestaShopDatabaseException * @throws \PrestaShopException */ private function createCustomerMessageThread(Order $order): int { $orderCustomer = new Customer($order->id_customer); $customerThread = new CustomerThread(); $customerThread->id_contact = 0; $customerThread->id_customer = (int) $order->id_customer; $customerThread->id_shop = $this->contextShopId; $customerThread->id_order = $order->id; $customerThread->id_lang = $this->contextLanguageId; $customerThread->email = $orderCustomer->email; $customerThread->status = 'open'; $customerThread->token = Tools::passwdGen(12); $customerThread->add(); return $customerThread->id; } /** * Creates actual message. * * @param int $customerServiceThreadId * @param AddOrderCustomerMessageCommand $command * * @throws \PrestaShopDatabaseException * @throws \PrestaShopException */ private function createMessage(int $customerServiceThreadId, AddOrderCustomerMessageCommand $command): void { $customerMessage = new CustomerMessage(); $customerMessage->id_customer_thread = $customerServiceThreadId; $customerMessage->id_employee = $this->contextEmployeeId; $customerMessage->message = $command->getMessage(); $customerMessage->private = $command->isPrivate(); $customerMessage->add(); } /** * Sends email to customer * * @param Customer $customer * @param Order $order * @param AddOrderCustomerMessageCommand $command * * @return bool * * @throws \PrestaShopDatabaseException * @throws \PrestaShopException */ private function sendMail(Customer $customer, Order $order, AddOrderCustomerMessageCommand $command): bool { if ($command->isPrivate()) { return true; } $message = $command->getMessage(); if (Configuration::get('PS_MAIL_TYPE', null, null, $order->id_shop) != Mail::TYPE_TEXT) { $message = Tools::nl2br(Tools::htmlentitiesUTF8($command->getMessage())); } $orderLanguage = $order->getAssociatedLanguage(); $varsTpl = [ '{lastname}' => $customer->lastname, '{firstname}' => $customer->firstname, '{id_order}' => $order->id, '{order_name}' => $order->getUniqReference(), '{message}' => $message, ]; return Mail::Send( (int) $orderLanguage->getId(), 'order_merchant_comment', $this->translator->trans( 'New message regarding your order', [], 'Emails.Subject', $orderLanguage->locale ), $varsTpl, $customer->email, $customer->firstname . ' ' . $customer->lastname, null, null, null, null, _PS_MAIL_DIR_, true, (int) $order->id_shop ); } }