Completed
Push — master ( a27561...0fc833 )
by
unknown
13:33
created

EmailFinisher::initializeFluidEmail()   B

Complexity

Conditions 8
Paths 40

Size

Total Lines 58
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 34
nc 40
nop 1
dl 0
loc 58
rs 8.1315
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the TYPO3 CMS project.
7
 *
8
 * It is free software; you can redistribute it and/or modify it under
9
 * the terms of the GNU General Public License, either version 2
10
 * of the License, or any later version.
11
 *
12
 * For the full copyright and license information, please read the
13
 * LICENSE.txt file that was distributed with this source code.
14
 *
15
 * The TYPO3 project - inspiring people to share!
16
 */
17
18
namespace TYPO3\CMS\Form\Domain\Finishers;
19
20
use Symfony\Component\Mime\Address;
21
use TYPO3\CMS\Core\Mail\FluidEmail;
22
use TYPO3\CMS\Core\Mail\Mailer;
23
use TYPO3\CMS\Core\Mail\MailMessage;
24
use TYPO3\CMS\Core\Utility\GeneralUtility;
25
use TYPO3\CMS\Extbase\Domain\Model\FileReference;
26
use TYPO3\CMS\Fluid\View\StandaloneView;
27
use TYPO3\CMS\Fluid\View\TemplatePaths;
28
use TYPO3\CMS\Form\Domain\Finishers\Exception\FinisherException;
29
use TYPO3\CMS\Form\Domain\Model\FormElements\FileUpload;
30
use TYPO3\CMS\Form\Domain\Runtime\FormRuntime;
31
use TYPO3\CMS\Form\Service\TranslationService;
32
use TYPO3\CMS\Form\ViewHelpers\RenderRenderableViewHelper;
33
34
/**
35
 * This finisher sends an email to one recipient
36
 *
37
 * Options:
38
 *
39
 * - templatePathAndFilename (mandatory for Mail): Template path and filename for the mail body
40
 * - templateName (mandatory for FluidEmail): Template name for the mail body
41
 * - templateRootPaths: root paths for the templates
42
 * - layoutRootPaths: root paths for the layouts
43
 * - partialRootPaths: root paths for the partials
44
 * - variables: associative array of variables which are available inside the Fluid template
45
 *
46
 * The following options control the mail sending. In all of them, placeholders in the form
47
 * of {...} are replaced with the corresponding form value; i.e. {email} as senderAddress
48
 * makes the recipient address configurable.
49
 *
50
 * - subject (mandatory): Subject of the email
51
 * - recipients (mandatory): Email addresses and human-readable names of the recipients
52
 * - senderAddress (mandatory): Email address of the sender
53
 * - senderName: Human-readable name of the sender
54
 * - replyToRecipients: Email addresses and human-readable names of the reply-to recipients
55
 * - carbonCopyRecipients: Email addresses and human-readable names of the copy recipients
56
 * - blindCarbonCopyRecipients: Email addresses and human-readable names of the blind copy recipients
57
 *
58
 * Scope: frontend
59
 */
60
class EmailFinisher extends AbstractFinisher
61
{
62
    const FORMAT_PLAINTEXT = 'plaintext';
63
    const FORMAT_HTML = 'html';
64
65
    /**
66
     * @var array
67
     */
68
    protected $defaultOptions = [
69
        'recipientName' => '',
70
        'senderName' => '',
71
        'addHtmlPart' => true,
72
        'attachUploads' => true,
73
    ];
74
75
    /**
76
     * Executes this finisher
77
     * @see AbstractFinisher::execute()
78
     *
79
     * @throws FinisherException
80
     */
81
    protected function executeInternal()
82
    {
83
        $languageBackup = null;
84
        // Flexform overrides write strings instead of integers so
85
        // we need to cast the string '0' to false.
86
        if (
87
            isset($this->options['addHtmlPart'])
88
            && $this->options['addHtmlPart'] === '0'
89
        ) {
90
            $this->options['addHtmlPart'] = false;
91
        }
92
93
        $subject = $this->parseOption('subject');
94
        $recipients = $this->getRecipients('recipients');
95
        $senderAddress = $this->parseOption('senderAddress');
96
        $senderAddress = is_string($senderAddress) ? $senderAddress : '';
97
        $senderName = $this->parseOption('senderName');
98
        $senderName = is_string($senderName) ? $senderName : '';
99
        $replyToRecipients = $this->getRecipients('replyToRecipients');
100
        $carbonCopyRecipients = $this->getRecipients('carbonCopyRecipients');
101
        $blindCarbonCopyRecipients = $this->getRecipients('blindCarbonCopyRecipients');
102
        $addHtmlPart = $this->parseOption('addHtmlPart') ? true : false;
103
        $attachUploads = $this->parseOption('attachUploads');
104
        $useFluidEmail = $this->parseOption('useFluidEmail');
105
        $title = $this->parseOption('title');
106
        $title = is_string($title) && $title !== '' ? $title : $subject;
107
108
        if (empty($subject)) {
109
            throw new FinisherException('The option "subject" must be set for the EmailFinisher.', 1327060320);
110
        }
111
        if (empty($recipients)) {
112
            throw new FinisherException('The option "recipients" must be set for the EmailFinisher.', 1327060200);
113
        }
114
        if (empty($senderAddress)) {
115
            throw new FinisherException('The option "senderAddress" must be set for the EmailFinisher.', 1327060210);
116
        }
117
118
        $formRuntime = $this->finisherContext->getFormRuntime();
119
120
        $translationService = TranslationService::getInstance();
121
        if (is_string($this->options['translation']['language'] ?? null) && $this->options['translation']['language'] !== '') {
122
            $languageBackup = $translationService->getLanguage();
123
            $translationService->setLanguage($this->options['translation']['language']);
124
        }
125
126
        $mail = $useFluidEmail
127
            ? $this
128
                ->initializeFluidEmail($formRuntime)
129
                ->format($addHtmlPart ? FluidEmail::FORMAT_BOTH : FluidEmail::FORMAT_PLAIN)
130
                ->assign('title', $title)
131
            : GeneralUtility::makeInstance(MailMessage::class);
132
133
        $mail
134
            ->from(new Address($senderAddress, $senderName))
135
            ->to(...$recipients)
136
            ->subject($subject);
0 ignored issues
show
Bug introduced by
It seems like $subject can also be of type array; however, parameter $subject of Symfony\Component\Mime\Email::subject() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

136
            ->subject(/** @scrutinizer ignore-type */ $subject);
Loading history...
137
138
        if (!empty($replyToRecipients)) {
139
            $mail->replyTo(...$replyToRecipients);
140
        }
141
142
        if (!empty($carbonCopyRecipients)) {
143
            $mail->cc(...$carbonCopyRecipients);
144
        }
145
146
        if (!empty($blindCarbonCopyRecipients)) {
147
            $mail->bcc(...$blindCarbonCopyRecipients);
148
        }
149
150
        if (!$useFluidEmail) {
151
            $parts = [
152
                [
153
                    'format' => 'Plaintext',
154
                    'contentType' => 'text/plain',
155
                ],
156
            ];
157
158
            if ($addHtmlPart) {
159
                $parts[] = [
160
                    'format' => 'Html',
161
                    'contentType' => 'text/html',
162
                ];
163
            }
164
165
            foreach ($parts as $i => $part) {
166
                $standaloneView = $this->initializeStandaloneView($formRuntime, $part['format']);
167
                $message = $standaloneView->render();
168
169
                if ($part['contentType'] === 'text/plain') {
170
                    $mail->text($message);
171
                } else {
172
                    $mail->html($message);
173
                }
174
            }
175
        }
176
177
        if (!empty($languageBackup)) {
178
            $translationService->setLanguage($languageBackup);
179
        }
180
181
        if ($attachUploads) {
182
            foreach ($formRuntime->getFormDefinition()->getRenderablesRecursively() as $element) {
183
                if (!$element instanceof FileUpload) {
184
                    continue;
185
                }
186
                $file = $formRuntime[$element->getIdentifier()];
187
                if ($file) {
188
                    if ($file instanceof FileReference) {
189
                        $file = $file->getOriginalResource();
190
                    }
191
                    $mail->attach($file->getContents(), $file->getName(), $file->getMimeType());
192
                }
193
            }
194
        }
195
196
        $useFluidEmail ? GeneralUtility::makeInstance(Mailer::class)->send($mail) : $mail->send();
0 ignored issues
show
Bug introduced by
The method send() does not exist on TYPO3\CMS\Core\Mail\FluidEmail. Did you maybe mean sender()? ( Ignorable by Annotation )

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

196
        $useFluidEmail ? GeneralUtility::makeInstance(Mailer::class)->send($mail) : $mail->/** @scrutinizer ignore-call */ send();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
197
    }
198
199
    /**
200
     * @param FormRuntime $formRuntime
201
     * @param string $format
202
     * @return StandaloneView
203
     * @throws FinisherException
204
     */
205
    protected function initializeStandaloneView(FormRuntime $formRuntime, string $format): StandaloneView
206
    {
207
        $standaloneView = GeneralUtility::makeInstance(StandaloneView::class);
208
209
        if (isset($this->options['templatePathAndFilename'])) {
210
            $this->options['templatePathAndFilename'] = strtr($this->options['templatePathAndFilename'], [
211
                '{@format}' => $format
212
            ]);
213
            $standaloneView->setTemplatePathAndFilename($this->options['templatePathAndFilename']);
214
        } else {
215
            if (!isset($this->options['templateName'])) {
216
                throw new FinisherException('The option "templateName" must be set for the EmailFinisher.', 1327058829);
217
            }
218
            // Use local variable instead of augmenting the options to
219
            // keep the format intact when sending multi-format mails
220
            $templateName = strtr($this->options['templateName'], [
221
                '{@format}' => $format
222
            ]);
223
            $standaloneView->setTemplate($templateName);
224
        }
225
226
        $standaloneView->assign('finisherVariableProvider', $this->finisherContext->getFinisherVariableProvider());
227
228
        if (isset($this->options['templateRootPaths']) && is_array($this->options['templateRootPaths'])) {
229
            $standaloneView->setTemplateRootPaths($this->options['templateRootPaths']);
230
        }
231
232
        if (isset($this->options['partialRootPaths']) && is_array($this->options['partialRootPaths'])) {
233
            $standaloneView->setPartialRootPaths($this->options['partialRootPaths']);
234
        }
235
236
        if (isset($this->options['layoutRootPaths']) && is_array($this->options['layoutRootPaths'])) {
237
            $standaloneView->setLayoutRootPaths($this->options['layoutRootPaths']);
238
        }
239
240
        if (is_array($this->options['variables'] ?? null)) {
241
            $standaloneView->assignMultiple($this->options['variables']);
242
        }
243
244
        $standaloneView->assign('form', $formRuntime);
245
        $standaloneView->getRenderingContext()
246
                       ->getViewHelperVariableContainer()
247
                       ->addOrUpdate(RenderRenderableViewHelper::class, 'formRuntime', $formRuntime);
248
249
        return $standaloneView;
250
    }
251
252
    protected function initializeFluidEmail(FormRuntime $formRuntime): FluidEmail
253
    {
254
        $templateConfiguration = $GLOBALS['TYPO3_CONF_VARS']['MAIL'];
255
256
        if (is_array($this->options['templateRootPaths'] ?? null)) {
257
            $templateConfiguration['templateRootPaths'] = array_replace_recursive(
258
                $templateConfiguration['templateRootPaths'],
259
                $this->options['templateRootPaths']
260
            );
261
            ksort($templateConfiguration['templateRootPaths']);
262
        }
263
264
        if (is_array($this->options['partialRootPaths'] ?? null)) {
265
            $templateConfiguration['partialRootPaths'] = array_replace_recursive(
266
                $templateConfiguration['partialRootPaths'],
267
                $this->options['partialRootPaths']
268
            );
269
            ksort($templateConfiguration['partialRootPaths']);
270
        }
271
272
        if (is_array($this->options['layoutRootPaths'] ?? null)) {
273
            $templateConfiguration['layoutRootPaths'] = array_replace_recursive(
274
                $templateConfiguration['layoutRootPaths'],
275
                $this->options['layoutRootPaths']
276
            );
277
            ksort($templateConfiguration['layoutRootPaths']);
278
        }
279
280
        $fluidEmail = GeneralUtility::makeInstance(
281
            FluidEmail::class,
282
            GeneralUtility::makeInstance(TemplatePaths::class, $templateConfiguration)
283
        );
284
285
        if (!isset($this->options['templateName']) || $this->options['templateName'] === '') {
286
            throw new FinisherException('The option "templateName" must be set to use FluidEmail.', 1599834020);
287
        }
288
289
        // Migrate old template name to default FluidEmail name
290
        if ($this->options['templateName'] === '{@format}.html') {
291
            $this->options['templateName'] = 'Default';
292
        }
293
294
        $fluidEmail
295
            ->setTemplate($this->options['templateName'])
296
            ->assignMultiple([
297
                'finisherVariableProvider' => $this->finisherContext->getFinisherVariableProvider(),
298
                'form' => $formRuntime
299
            ]);
300
301
        if (is_array($this->options['variables'] ?? null)) {
302
            $fluidEmail->assignMultiple($this->options['variables']);
303
        }
304
305
        $fluidEmail
306
            ->getViewHelperVariableContainer()
307
            ->addOrUpdate(RenderRenderableViewHelper::class, 'formRuntime', $formRuntime);
308
309
        return $fluidEmail;
310
    }
311
312
    /**
313
     * Get mail recipients
314
     *
315
     * @param string $listOption List option name
316
     * @return array
317
     */
318
    protected function getRecipients(string $listOption): array
319
    {
320
        $recipients = $this->parseOption($listOption) ?? [];
321
        $addresses = [];
322
        foreach ($recipients as $address => $name) {
323
            if (!GeneralUtility::validEmail($address)) {
324
                // Drop entries without valid address
325
                continue;
326
            }
327
            $addresses[] = new Address($address, $name);
328
        }
329
        return $addresses;
330
    }
331
}
332