Passed
Push — 6.4 ( b2f05b...7c0c3d )
by Christian
12:48 queued 13s
created

SendMailAction::mappingAttachments()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 18
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 10
c 0
b 0
f 0
nc 3
nop 3
dl 0
loc 18
rs 9.9332
1
<?php declare(strict_types=1);
2
3
namespace Shopware\Core\Content\Flow\Dispatching\Action;
4
5
use Doctrine\DBAL\Connection;
6
use Psr\Log\LoggerInterface;
7
use Shopware\Core\Content\ContactForm\Event\ContactFormEvent;
8
use Shopware\Core\Content\Flow\Dispatching\DelayableAction;
9
use Shopware\Core\Content\Flow\Dispatching\StorableFlow;
10
use Shopware\Core\Content\Flow\Events\FlowSendMailActionEvent;
11
use Shopware\Core\Content\Mail\Service\AbstractMailService;
12
use Shopware\Core\Content\Mail\Service\MailAttachmentsConfig;
13
use Shopware\Core\Content\MailTemplate\Exception\MailEventConfigurationException;
14
use Shopware\Core\Content\MailTemplate\Exception\SalesChannelNotFoundException;
15
use Shopware\Core\Content\MailTemplate\MailTemplateActions;
16
use Shopware\Core\Content\MailTemplate\MailTemplateEntity;
17
use Shopware\Core\Content\MailTemplate\Subscriber\MailSendSubscriberConfig;
18
use Shopware\Core\Framework\Adapter\Translation\Translator;
19
use Shopware\Core\Framework\Context;
20
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
21
use Shopware\Core\Framework\DataAbstractionLayer\Exception\InconsistentCriteriaIdsException;
22
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
23
use Shopware\Core\Framework\Event\FlowEvent;
24
use Shopware\Core\Framework\Event\MailAware;
25
use Shopware\Core\Framework\Event\OrderAware;
26
use Shopware\Core\Framework\Feature;
27
use Shopware\Core\Framework\Uuid\Uuid;
28
use Shopware\Core\Framework\Validation\DataBag\DataBag;
29
use Shopware\Core\System\Locale\LanguageLocaleCodeProvider;
30
use Symfony\Contracts\EventDispatcher\Event;
31
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
32
33
/**
34
 * @package business-ops
35
 *
36
 * @deprecated tag:v6.5.0 - reason:remove-subscriber - FlowActions won't be executed over the event system anymore,
37
 * therefore the actions won't implement the EventSubscriberInterface anymore.
38
 */
39
class SendMailAction extends FlowAction implements DelayableAction
0 ignored issues
show
Deprecated Code introduced by
The class Shopware\Core\Content\Fl...ching\Action\FlowAction has been deprecated: tag:v6.5.0 - reason:remove-subscriber - FlowActions won't be executed over the event system anymore, therefore the actions won't implement the EventSubscriberInterface anymore. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

39
class SendMailAction extends /** @scrutinizer ignore-deprecated */ FlowAction implements DelayableAction
Loading history...
40
{
41
    public const ACTION_NAME = MailTemplateActions::MAIL_TEMPLATE_MAIL_SEND_ACTION;
42
    public const MAIL_CONFIG_EXTENSION = 'mail-attachments';
43
    private const RECIPIENT_CONFIG_ADMIN = 'admin';
44
    private const RECIPIENT_CONFIG_CUSTOM = 'custom';
45
    private const RECIPIENT_CONFIG_CONTACT_FORM_MAIL = 'contactFormMail';
46
47
    private EntityRepositoryInterface $mailTemplateRepository;
48
49
    private LoggerInterface $logger;
50
51
    private AbstractMailService $emailService;
52
53
    private EventDispatcherInterface $eventDispatcher;
54
55
    private EntityRepositoryInterface $mailTemplateTypeRepository;
56
57
    private Translator $translator;
58
59
    private Connection $connection;
60
61
    private LanguageLocaleCodeProvider $languageLocaleProvider;
62
63
    private bool $updateMailTemplate;
64
65
    /**
66
     * @internal
67
     */
68
    public function __construct(
69
        AbstractMailService $emailService,
70
        EntityRepositoryInterface $mailTemplateRepository,
71
        LoggerInterface $logger,
72
        EventDispatcherInterface $eventDispatcher,
73
        EntityRepositoryInterface $mailTemplateTypeRepository,
74
        Translator $translator,
75
        Connection $connection,
76
        LanguageLocaleCodeProvider $languageLocaleProvider,
77
        bool $updateMailTemplate
78
    ) {
79
        $this->mailTemplateRepository = $mailTemplateRepository;
80
        $this->logger = $logger;
81
        $this->emailService = $emailService;
82
        $this->eventDispatcher = $eventDispatcher;
83
        $this->mailTemplateTypeRepository = $mailTemplateTypeRepository;
84
        $this->translator = $translator;
85
        $this->connection = $connection;
86
        $this->languageLocaleProvider = $languageLocaleProvider;
87
        $this->updateMailTemplate = $updateMailTemplate;
88
    }
89
90
    public static function getName(): string
91
    {
92
        return 'action.mail.send';
93
    }
94
95
    /**
96
     * @deprecated tag:v6.5.0 - reason:remove-subscriber - Will be removed
97
     */
98
    public static function getSubscribedEvents(): array
99
    {
100
        if (Feature::isActive('v6.5.0.0')) {
101
            return [];
102
        }
103
104
        return [
105
            self::getName() => 'handle',
106
        ];
107
    }
108
109
    /**
110
     * @return array<string>
111
     */
112
    public function requirements(): array
113
    {
114
        return [MailAware::class];
115
    }
116
117
    /**
118
     * @deprecated tag:v6.5.0 Will be removed, implement handleFlow instead
119
     *
120
     * @throws MailEventConfigurationException
121
     * @throws SalesChannelNotFoundException
122
     * @throws InconsistentCriteriaIdsException
123
     */
124
    public function handle(Event $event): void
125
    {
126
        Feature::triggerDeprecationOrThrow(
127
            'v6.5.0.0',
128
            Feature::deprecatedMethodMessage(__CLASS__, __METHOD__, 'v6.5.0.0')
129
        );
130
131
        if (!$event instanceof FlowEvent) {
132
            return;
133
        }
134
135
        $mailEvent = $event->getEvent();
136
137
        $extension = $event->getContext()->getExtension(self::MAIL_CONFIG_EXTENSION);
138
        if (!$extension instanceof MailSendSubscriberConfig) {
139
            $extension = new MailSendSubscriberConfig(false, [], []);
140
        }
141
142
        if ($extension->skip()) {
143
            return;
144
        }
145
146
        if (!$mailEvent instanceof MailAware) {
147
            throw new MailEventConfigurationException('Not an instance of MailAware', \get_class($mailEvent));
148
        }
149
150
        $eventConfig = $event->getConfig();
151
152
        if (empty($eventConfig['recipient'])) {
153
            throw new MailEventConfigurationException('The recipient value in the flow action configuration is missing.', \get_class($mailEvent));
154
        }
155
156
        if (!isset($eventConfig['mailTemplateId'])) {
157
            return;
158
        }
159
160
        $mailTemplate = $this->getMailTemplate($eventConfig['mailTemplateId'], $event->getContext());
161
162
        if ($mailTemplate === null) {
163
            return;
164
        }
165
166
        $injectedTranslator = $this->injectTranslator($mailEvent->getContext(), $mailEvent->getSalesChannelId());
167
168
        $data = new DataBag();
169
170
        $contactFormData = [];
171
        if ($mailEvent instanceof ContactFormEvent) {
172
            $contactFormData = $mailEvent->getContactFormData();
173
        }
174
175
        $recipients = $this->getRecipients($eventConfig['recipient'], $mailEvent->getMailStruct()->getRecipients(), $contactFormData);
176
177
        if (empty($recipients)) {
178
            return;
179
        }
180
181
        $data->set('recipients', $recipients);
182
        $data->set('senderName', $mailTemplate->getTranslation('senderName'));
183
        $data->set('salesChannelId', $mailEvent->getSalesChannelId());
184
185
        $data->set('templateId', $mailTemplate->getId());
186
        $data->set('customFields', $mailTemplate->getCustomFields());
187
        $data->set('contentHtml', $mailTemplate->getTranslation('contentHtml'));
188
        $data->set('contentPlain', $mailTemplate->getTranslation('contentPlain'));
189
        $data->set('subject', $mailTemplate->getTranslation('subject'));
190
        $data->set('mediaIds', []);
191
192
        $data->set('attachmentsConfig', new MailAttachmentsConfig(
193
            $event->getContext(),
194
            $mailTemplate,
195
            $extension,
196
            $eventConfig,
197
            $mailEvent instanceof OrderAware ? $mailEvent->getOrderId() : null,
198
        ));
199
200
        if (!empty($eventConfig['replyTo'])) {
201
            $data->set('senderMail', $eventConfig['replyTo']);
202
        }
203
204
        $this->eventDispatcher->dispatch(new FlowSendMailActionEvent($data, $mailTemplate, $event));
205
206
        if ($data->has('templateId')) {
207
            $this->updateMailTemplateType(
208
                $event->getContext(),
209
                $event,
210
                $this->getTemplateData($mailEvent),
211
                $mailTemplate
212
            );
213
        }
214
215
        $this->send($data, $event->getContext(), $this->getTemplateData($mailEvent), $extension, $injectedTranslator);
216
    }
217
218
    /**
219
     * @throws MailEventConfigurationException
220
     * @throws SalesChannelNotFoundException
221
     * @throws InconsistentCriteriaIdsException
222
     */
223
    public function handleFlow(StorableFlow $flow): void
224
    {
225
        $extension = $flow->getContext()->getExtension(self::MAIL_CONFIG_EXTENSION);
226
        if (!$extension instanceof MailSendSubscriberConfig) {
227
            $extension = new MailSendSubscriberConfig(false, [], []);
228
        }
229
230
        if ($extension->skip()) {
231
            return;
232
        }
233
234
        if (!$flow->hasStore(MailAware::MAIL_STRUCT) || !$flow->hasStore(MailAware::SALES_CHANNEL_ID)) {
235
            throw new MailEventConfigurationException('Not have data from MailAware', \get_class($flow));
236
        }
237
238
        $eventConfig = $flow->getConfig();
239
        if (empty($eventConfig['recipient'])) {
240
            throw new MailEventConfigurationException('The recipient value in the flow action configuration is missing.', \get_class($flow));
241
        }
242
243
        if (!isset($eventConfig['mailTemplateId'])) {
244
            return;
245
        }
246
247
        $mailTemplate = $this->getMailTemplate($eventConfig['mailTemplateId'], $flow->getContext());
248
249
        if ($mailTemplate === null) {
250
            return;
251
        }
252
253
        $injectedTranslator = $this->injectTranslator($flow->getContext(), $flow->getStore(MailAware::SALES_CHANNEL_ID));
254
255
        $data = new DataBag();
256
257
        $recipients = $this->getRecipients(
258
            $eventConfig['recipient'],
259
            $flow->getStore(MailAware::MAIL_STRUCT)['recipients'],
260
            $flow->getStore('contactFormData', []),
261
        );
262
263
        if (empty($recipients)) {
264
            return;
265
        }
266
267
        $data->set('recipients', $recipients);
268
        $data->set('senderName', $mailTemplate->getTranslation('senderName'));
269
        $data->set('salesChannelId', $flow->getStore(MailAware::SALES_CHANNEL_ID));
270
271
        $data->set('templateId', $mailTemplate->getId());
272
        $data->set('customFields', $mailTemplate->getCustomFields());
273
        $data->set('contentHtml', $mailTemplate->getTranslation('contentHtml'));
274
        $data->set('contentPlain', $mailTemplate->getTranslation('contentPlain'));
275
        $data->set('subject', $mailTemplate->getTranslation('subject'));
276
        $data->set('mediaIds', []);
277
278
        $data->set('attachmentsConfig', new MailAttachmentsConfig(
279
            $flow->getContext(),
280
            $mailTemplate,
281
            $extension,
282
            $eventConfig,
283
            $flow->getStore(OrderAware::ORDER_ID),
284
        ));
285
286
        if (!empty($eventConfig['replyTo'])) {
287
            $data->set('senderMail', $eventConfig['replyTo']);
288
        }
289
290
        $this->eventDispatcher->dispatch(new FlowSendMailActionEvent($data, $mailTemplate, $flow));
291
292
        if ($data->has('templateId')) {
293
            $this->updateMailTemplateType(
294
                $flow->getContext(),
295
                $flow,
296
                $flow->data(),
297
                $mailTemplate
298
            );
299
        }
300
301
        $this->send($data, $flow->getContext(), $flow->data(), $extension, $injectedTranslator);
302
    }
303
304
    /**
305
     * @param array<string, mixed> $templateData
306
     */
307
    private function send(DataBag $data, Context $context, array $templateData, MailSendSubscriberConfig $extension, bool $injectedTranslator): void
308
    {
309
        try {
310
            $this->emailService->send(
311
                $data->all(),
312
                $context,
313
                $templateData
314
            );
315
        } catch (\Exception $e) {
316
            $this->logger->error(
317
                "Could not send mail:\n"
318
                . $e->getMessage() . "\n"
319
                . 'Error Code:' . $e->getCode() . "\n"
320
                . "Template data: \n"
321
                . json_encode($data->all()) . "\n"
322
            );
323
        }
324
325
        if ($injectedTranslator) {
326
            $this->translator->resetInjection();
327
        }
328
    }
329
330
    /**
331
     * @param FlowEvent|StorableFlow $event
332
     * @param array<string, mixed> $templateData
333
     */
334
    private function updateMailTemplateType(
335
        Context $context,
336
        $event,
337
        array $templateData,
338
        MailTemplateEntity $mailTemplate
339
    ): void {
340
        if (!$mailTemplate->getMailTemplateTypeId()) {
341
            return;
342
        }
343
344
        if (!$this->updateMailTemplate) {
345
            return;
346
        }
347
348
        $mailTemplateTypeTranslation = $this->connection->fetchOne(
349
            'SELECT 1 FROM mail_template_type_translation WHERE language_id = :languageId AND mail_template_type_id =:mailTemplateTypeId',
350
            [
351
                'languageId' => Uuid::fromHexToBytes($context->getLanguageId()),
352
                'mailTemplateTypeId' => Uuid::fromHexToBytes($mailTemplate->getMailTemplateTypeId()),
353
            ]
354
        );
355
356
        if (!$mailTemplateTypeTranslation) {
357
            // Don't throw errors if this fails // Fix with NEXT-15475
358
            $this->logger->error(
359
                "Could not update mail template type, because translation for this language does not exits:\n"
360
                . 'Flow id: ' . $event->getFlowState()->flowId . "\n"
361
                . 'Sequence id: ' . $event->getFlowState()->getSequenceId()
362
            );
363
364
            return;
365
        }
366
367
        $this->mailTemplateTypeRepository->update([[
368
            'id' => $mailTemplate->getMailTemplateTypeId(),
369
            'templateData' => $templateData,
370
        ]], $context);
371
    }
372
373
    private function getMailTemplate(string $id, Context $context): ?MailTemplateEntity
374
    {
375
        $criteria = new Criteria([$id]);
376
        $criteria->setTitle('send-mail::load-mail-template');
377
        $criteria->addAssociation('media.media');
378
        $criteria->setLimit(1);
379
380
        return $this->mailTemplateRepository
381
            ->search($criteria, $context)
382
            ->first();
383
    }
384
385
    /**
386
     * @throws MailEventConfigurationException
387
     *
388
     * @return array<string, mixed>
389
     */
390
    private function getTemplateData(MailAware $event): array
391
    {
392
        $data = [];
393
394
        foreach (array_keys($event::getAvailableData()->toArray()) as $key) {
395
            $getter = 'get' . ucfirst($key);
396
            if (!method_exists($event, $getter)) {
397
                throw new MailEventConfigurationException('Data for ' . $key . ' not available.', \get_class($event));
398
            }
399
            $data[$key] = $event->$getter();
400
        }
401
402
        return $data;
403
    }
404
405
    private function injectTranslator(Context $context, ?string $salesChannelId): bool
406
    {
407
        if ($salesChannelId === null) {
408
            return false;
409
        }
410
411
        if ($this->translator->getSnippetSetId() !== null) {
412
            return false;
413
        }
414
415
        $this->translator->injectSettings(
416
            $salesChannelId,
417
            $context->getLanguageId(),
418
            $this->languageLocaleProvider->getLocaleForLanguageId($context->getLanguageId()),
419
            $context
420
        );
421
422
        return true;
423
    }
424
425
    /**
426
     * @param array<string, mixed> $recipients
427
     * @param array<string, mixed> $mailStructRecipients
428
     * @param array<int|string, mixed> $contactFormData
429
     *
430
     * @return array<int|string, string>
431
     */
432
    private function getRecipients(array $recipients, array $mailStructRecipients, array $contactFormData): array
433
    {
434
        switch ($recipients['type']) {
435
            case self::RECIPIENT_CONFIG_CUSTOM:
436
                return $recipients['data'];
437
            case self::RECIPIENT_CONFIG_ADMIN:
438
                $admins = $this->connection->fetchAllAssociative(
439
                    'SELECT first_name, last_name, email FROM user WHERE admin = true'
440
                );
441
                $emails = [];
442
                foreach ($admins as $admin) {
443
                    $emails[$admin['email']] = $admin['first_name'] . ' ' . $admin['last_name'];
444
                }
445
446
                return $emails;
447
            case self::RECIPIENT_CONFIG_CONTACT_FORM_MAIL:
448
                if (empty($contactFormData)) {
449
                    return [];
450
                }
451
452
                if (!\array_key_exists('email', $contactFormData)) {
453
                    return [];
454
                }
455
456
                return [$contactFormData['email'] => ($contactFormData['firstName'] ?? '') . ' ' . ($contactFormData['lastName'] ?? '')];
457
            default:
458
                return $mailStructRecipients;
459
        }
460
    }
461
}
462