Completed
Push — master ( ad69e3...dd814f )
by Dominik
04:35
created

AzineEmailTemplateController   C

Complexity

Total Complexity 61

Size/Duplication

Total Lines 451
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 14

Test Coverage

Coverage 62.01%

Importance

Changes 0
Metric Value
wmc 61
lcom 1
cbo 14
dl 0
loc 451
ccs 111
cts 179
cp 0.6201
rs 5.4385
c 0
b 0
f 0

13 Methods

Rating   Name   Duplication   Size   Complexity  
A indexAction() 0 14 1
C webPreViewAction() 0 72 8
C userIsAllowedToSeeThisMail() 0 45 8
B reAttachAllEntities() 0 18 6
A serveImageAction() 0 14 2
A getTemplateProviderService() 0 4 1
A renderResponse() 0 4 1
A getSentEmailForToken() 0 6 1
C sendTestEmailAction() 0 61 9
A getSpamIndexReportForSwiftMessage() 0 4 1
C checkSpamScoreOfSentEmailAction() 0 30 11
C getSpamIndexReport() 0 45 7
B webViewAction() 0 46 5

How to fix   Complexity   

Complex Class

Complex classes like AzineEmailTemplateController often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use AzineEmailTemplateController, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Azine\EmailBundle\Controller;
4
5
use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
6
use Symfony\Component\HttpFoundation\Session\Session;
7
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
8
use Symfony\Component\HttpFoundation\JsonResponse;
9
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
10
use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
11
use Symfony\Component\HttpFoundation\RedirectResponse;
12
use Azine\EmailBundle\Entity\SentEmail;
13
use Symfony\Component\HttpFoundation\Response;
14
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
15
use Symfony\Component\HttpFoundation\BinaryFileResponse;
16
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
17
use Symfony\Component\HttpFoundation\Request;
18
use Azine\EmailBundle\Services\TemplateProviderInterface;
19
20
/**
21
 * This controller provides the following actions:
22
 *
23
 * index: view a list of all your templates with the option to send a test mail with "dummy"-data to an email-address of your choice (see WebViewServiceInterface::getTemplatesForWebPreView() & WebViewServiceInterface::getTestMailAccounts) .
24
 * webPreView: shows the selected html- or txt-email-template filled with the dummy-data you defined (in the WebViewServiceInterface::getDummyVarsFor() function).
25
 * webView: shows an email that has been sent (and stored as SentEmail-entity in the database)
26
 * sendTestMail: sends an email filled with the dummy-data you defined to the selected email-address.
27
 * serveImage: serve an image from the template-image-folder
28
 *
29
 * @author dominik
30
 */
31
32
class AzineEmailTemplateController extends Controller
33
{
34
35
    /**
36
     * Show a set of options to view html- and text-versions of email in the browser and send them as emails to test-accounts
37
     */
38 1
    public function indexAction(Request $request)
39
    {
40 1
        $customEmail = $request->get('customEmail', '[email protected]');
41 1
        $templates = $this->get('azine_email_web_view_service')->getTemplatesForWebPreView();
42 1
        $emails = $this->get('azine_email_web_view_service')->getTestMailAccounts();
43
44 1
        return $this->get('templating')
45 1
                    ->renderResponse("AzineEmailBundle:Webview:index.html.twig",
46
                    array(	
47 1
                        'customEmail' => $customEmail,
48 1
                        'templates'   => $templates,
49 1
                        'emails'      => $emails,
50 1
                    ));
51
    }
52
53
    /**
54
     * Show a web-preview-version of an email-template, filled with dummy-content
55
     * @param string $format
56
     * @return Response
57
     */
58 1
    public function webPreViewAction(Request $request, $template, $format = null)
59
    {
60
        
61 1
        if ($format !== "txt") {
62 1
            $format = "html";
63 1
        }
64
65 1
        $template = urldecode($template);
66
67 1
        $locale = $request->getLocale();
68
69
        // merge request vars with dummyVars, but make sure request vars remain as they are.
70 1
        $emailVars = array_merge(array(), $request->query->all());
71 1
        $emailVars = $this->get('azine_email_web_view_service')->getDummyVarsFor($template, $locale, $emailVars);
72 1
        $emailVars = array_merge($emailVars, $request->query->all());
73
74
        // add the styles
75 1
        $emailVars = $this->getTemplateProviderService()->addTemplateVariablesFor($template, $emailVars);
76
77
        // add the from-email for the footer-text
78 1
        if (!array_key_exists('fromEmail', $emailVars)) {
79 1
            $noReply = $this->getParameter('azine_email_no_reply');
80 1
            $emailVars['fromEmail'] = $noReply['email'];
81 1
            $emailVars['fromName'] = $noReply['name'];
82 1
        }
83
84
        // set the emailLocale for the templates
85 1
        $emailVars['emailLocale'] = $locale;
86
87
        // replace absolute image-paths with relative ones.
88 1
        $emailVars = $this->getTemplateProviderService()->makeImagePathsWebRelative($emailVars, $locale);
89
90
        // add code-snippets
91 1
        $emailVars = $this->getTemplateProviderService()->addTemplateSnippetsWithImagesFor($template, $emailVars, $locale);
92
93
        // render & return email
94 1
        $response = $this->renderResponse("$template.$format.twig", $emailVars);
95
96
        // add campaign tracking params
97 1
        $campaignParams = $this->getTemplateProviderService()->getCampaignParamsFor($template, $emailVars);
98 1
        $campaignParams['utm_medium'] = 'webPreview';
99 1
        if(sizeof($campaignParams) > 0) {
100 1
            $htmlBody = $response->getContent();
101 1
            $htmlBody = $this->get("azine.email.bundle.twig.filters")->addCampaignParamsToAllUrls($htmlBody, $campaignParams);
102
103 1
            $emailOpenTrackingCodeBuilder = $this->get('azine_email_email_open_tracking_code_builder');
104 1
            if ($emailOpenTrackingCodeBuilder) {
105
                // add an image at the end of the html tag with the tracking-params to track email-opens
106 1
                $imgTrackingCode = $emailOpenTrackingCodeBuilder->getTrackingImgCode($template, $campaignParams, $emailVars, "dummy", "[email protected]", null, null);
107 1
                if ($imgTrackingCode && strlen($imgTrackingCode) > 0) {
108
                    // replace the tracking url, so no request is made to the real tracking system.
109 1
                    $imgTrackingCode = str_replace("://", "://webview-dummy-domain.", $imgTrackingCode);
110 1
                    $htmlCloseTagPosition = strpos($htmlBody, "</html>");
111 1
                    $htmlBody = substr_replace($htmlBody, $imgTrackingCode, $htmlCloseTagPosition, 0);
112 1
                }
113 1
            }
114 1
            $response->setContent($htmlBody);
115 1
        }
116
117
        // if the requested format is txt, remove the html-part
118 1
        if ($format == "txt") {
119
            // set the correct content-type
120 1
            $response->headers->set("Content-Type","text/plain");
121
122
            // cut away the html-part
123 1
            $content = $response->getContent();
124 1
            $textEnd = stripos($content, "<!doctype");
125 1
            $response->setContent(substr($content, 0, $textEnd));
126 1
        }
127
128 1
        return $response;
129
    }
130
131
    /**
132
     * Show a web-version of an email that has been sent to recipients and has been stored in the database.
133
     * @param Request $request
134
     * @param string $token
135
     * @return Response
136
     */
137 6
    public function webViewAction (Request $request, $token)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
138
    {
139
        // find email recipients, template & params
140 6
        $sentEmail = $this->getSentEmailForToken($token);
141
142
        // check if the sent email is available
143 6
        if ($sentEmail !== null) {
144
145
            // check if the current user is allowed to see the email
146 5
            if ($this->userIsAllowedToSeeThisMail($sentEmail)) {
147
148 3
                $template = $sentEmail->getTemplate();
149 3
                $emailVars = $sentEmail->getVariables();
150
151
                // re-attach all entities to the EntityManager.
152 3
                $this->reAttachAllEntities($emailVars);
153
154
                // remove the web-view-token from the param-array
155 3
                $templateProvider = $this->getTemplateProviderService();
156 3
                unset($emailVars[$templateProvider->getWebViewTokenId()]);
157
158
                // render & return email
159 3
                $response = $this->renderResponse("$template.html.twig", $emailVars);
160
161 3
                $campaignParams = $templateProvider->getCampaignParamsFor($template, $emailVars);
162
163 3
                if ($campaignParams != null && sizeof($campaignParams) > 0) {
164 1
                    $response->setContent($this->get("azine.email.bundle.twig.filters")->addCampaignParamsToAllUrls($response->getContent(), $campaignParams));
165 1
                }
166
167 3
                return $response;
168
169
            // if the user is not allowed to see this mail
170
            } else {
171 2
                $msg = $this->get('translator')->trans('web.pre.view.test.mail.access.denied');
172 2
                throw new AccessDeniedException($msg);
173
            }
174
        }
175
176
        // the parameters-array is null => the email is not available in webView
177 1
        $days = $this->getParameter("azine_email_web_view_retention");
178 1
        $response = $this->renderResponse("AzineEmailBundle:Webview:mail.not.available.html.twig", array('days' => $days));
179 1
        $response->setStatusCode(404);
180
181 1
        return $response;
182
    }
183
184
    /**
185
     * Check if the user is allowed to see the email.
186
     * => the mail is public or the user is among the recipients or the user is an admin.
187
     *
188
     * @param  SentEmail $mail
189
     * @return boolean
190
     */
191 5
    private function userIsAllowedToSeeThisMail(SentEmail $mail)
192
    {
193 5
        $recipients = $mail->getRecipients();
194
195
        // it is a public email
196 5
        if ($recipients === null) {
197 1
            return true;
198
        }
199
200
        // get the current user
201 4
        $currentUser = null;
202 4
        if (!$this->has('security.token_storage')) {
203
            // @codeCoverageIgnoreStart
204
            throw new \LogicException('The SecurityBundle is not registered in your application.');
205
            // @codeCoverageIgnoreEnd
206
207
        } else {
208 4
            $token = $this->get('security.token_storage')->getToken();
209
210
            // check if the token is not null and the user in the token an object
211 4
            if ($token instanceof TokenInterface && is_object($token->getUser())) {
212 3
                $currentUser = $token->getUser();
213 3
            }
214
        }
215
216
        // it is not a public email, and a user is logged in
217 4
        if ($currentUser !== null) {
218
219
            // the user is among the recipients
220 3
            if(array_search($currentUser->getEmail(), $recipients) !== false)
221
222 3
                return true;
223
224
            // the user is admin
225 2
            if($currentUser->hasRole("ROLE_ADMIN"))
226
227 2
                return true;
228 1
        }
229
230
        // not public email, but
231
        // 		- there is no user, or
232
        //		- the user is not among the recipients and
233
        //		- the user not an admin-user either
234 2
        return false;
235
    }
236
237
    /**
238
     * Replace all unmanaged Objects in the array (recursively)
239
     * by managed Entities fetched via Doctrine EntityManager.
240
     *
241
     *  It is assumed that managed objects can be identified
242
     *  by their id and implement the function getId() to get that id.
243
     *
244
     * @param  array $vars passed by reference & manipulated but not returned.
245
     * @return null
246
     */
247 3
    private function reAttachAllEntities(array &$vars)
248
    {
249 3
        $em = $this->get('doctrine')->getManager();
250 3
        foreach ($vars as $key => $next) {
251
            if (is_object($next) && method_exists($next, 'getId')) {
252
                $className = get_class($next);
253
                $managedEntity = $em->find($className, $next->getId());
254
                if ($managedEntity) {
255
                    $vars[$key] = $managedEntity;
256
                }
257
                continue;
258
            } elseif (is_array($next)) {
259
                $this->reAttachAllEntities($next);
260
                continue;
261
            }
262 3
        }
263
264 3
    }
265
266
    /**
267
     * Serve the image from the templates-folder
268
     * @param Request $request
269
     * @param  string $folderKey
270
     * @param  string $filename
271
     * @return BinaryFileResponse
272
     */
273 2
    public function serveImageAction(Request $request, $folderKey, $filename)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
274
    {
275 2
        $folder = $this->getTemplateProviderService()->getFolderFrom($folderKey);
276 2
        if ($folder !== false) {
277 1
            $fullPath = $folder.$filename;
278 1
            $response = BinaryFileResponse::create($fullPath);
279 1
            $response->setContentDisposition(ResponseHeaderBag::DISPOSITION_INLINE);
280 1
            $response->headers->set("Content-Type", "image");
281
282 1
            return $response;
283
        }
284
285 1
        throw new FileNotFoundException($filename);
286
    }
287
288
    /**
289
     * @return TemplateProviderInterface
290
     */
291 6
    protected function getTemplateProviderService()
292
    {
293 6
        return $this->get('azine_email_template_provider');
294
    }
295
296
    /**
297
     * @param  string   $view
298
     * @param  array    $parameters
299
     * @param  Response $response
300
     * @return Response
301
     */
302 5
    protected function renderResponse($view, array $parameters = array(), Response $response = null)
303
    {
304 5
        return $this->get('templating')->renderResponse($view, $parameters, $response);
305
    }
306
307
    /**
308
     * Get the sent email from the database
309
     * @param  string    $token the token identifying the sent email
310
     * @return SentEmail
311
     */
312 6
    protected function getSentEmailForToken($token)
313
    {
314 6
        $sentEmail = $this->get('doctrine')->getRepository('AzineEmailBundle:SentEmail')->findOneByToken($token);
315
316 6
        return $sentEmail;
317
    }
318
319
    /**
320
     * Send a test-mail for the template to the given email-address
321
     * @param Request $request
322
     * @param  string $template templateId without ending => AzineEmailBundle::baseEmailLayout (without .txt.twig)
323
     * @param  string $email
324
     * @return RedirectResponse
325
     */
326
    public function sendTestEmailAction(Request $request, $template, $email)
327
    {
328
        $locale = $request->getLocale();
329
330
        $template = urldecode($template);
331
332
        // get the email-vars for email-sending => absolute fs-paths to images
333
        $emailVars = $this->get('azine_email_web_view_service')->getDummyVarsFor($template, $locale);
334
335
        // send the mail
336
        $message = \Swift_Message::newInstance();
337
        $mailer = $this->get("azine_email_template_twig_swift_mailer");
338
        $sent = $mailer->sendSingleEmail($email, "Test Recipient", $emailVars['subject'], $emailVars, $template.".txt.twig", $locale, $emailVars['sendMailAccountAddress'], $emailVars['sendMailAccountName']." (Test)", $message);
339
340
        $flashBag = $request->getSession()->getFlashBag();
341
342
        $spamReport = $this->getSpamIndexReportForSwiftMessage($message);
343
        if (is_array($spamReport)) {
344
            if ($spamReport['curlHttpCode'] == 200 && $spamReport['success']) {
345
                $spamScore = $spamReport['score'];
346
                $spamInfo = "SpamScore: $spamScore! \n".$spamReport['report'];
347
            } else {
348
                //@codeCoverageIgnoreStart
349
                // this only happens if the spam-check-server has a problem / is not responding
350
                $spamScore = 10;
351
                $spamInfo = "Getting the spam-info failed.
352
                             HttpCode: ".$spamReport['curlHttpCode']."
353
                             SpamReportMsg: ".$spamReport['message'];
354
                if(array_key_exists('curlError', $spamReport)) {
355
                    $spamInfo .= "
356
                             cURL-Error: " . $spamReport['curlError'];
357
                }
358
                //@codeCoverageIgnoreEnd
359
            }
360
361
            if ($spamScore <= 2) {
362
                $flashBag->add('info', $spamInfo);
363
            } elseif ($spamScore > 2 && $spamScore < 5) {
364
                $flashBag->add('warn', $spamInfo);
365
            } else {
366
                $flashBag->add('error', $spamInfo);
367
            }
368
369
        }
370
371
        // inform about sent/failed emails
372
        if ($sent) {
373
            $msg = $this->get('translator')->trans('web.pre.view.test.mail.sent.for.%template%.to.%email%', array('%template%' => $template, '%email%' => $email));
374
            $flashBag->add('info', $msg);
375
376
            //@codeCoverageIgnoreStart
377
        } else {
378
            // this only happens if the mail-server has a problem
379
            $msg = $this->get('translator')->trans('web.pre.view.test.mail.failed.for.%template%.to.%email%', array('%template%' => $template, '%email%' => $email));
380
            $flashBag->add('warn', $msg);
381
            //@codeCoverageIgnoreStart
382
        }
383
384
        // show the index page again.
385
        return new RedirectResponse($this->get('router')->generate('azine_email_template_index', array('customEmail' => $email)));
386
    }
387
388
    /**
389
     * Make an RESTful call to http://spamcheck.postmarkapp.com/filter to test the emails-spam-index.
390
     * See http://spamcheck.postmarkapp.com/doc
391
     * @return array TestResult array('success', 'message', 'curlHttpCode', 'curlError', ['score', 'report'])
392
     */
393
    public function getSpamIndexReportForSwiftMessage(\Swift_Message $message, $report = 'long')
394
    {
395
        return $this->getSpamIndexReport($message->toString(), $report);
396
    }
397
398
    /**
399
     * @param $msgString
400
     * @param string $report
401
     * @return mixed
402
     */
403
    private function getSpamIndexReport($msgString, $report = 'long')
404
    {
405
        // check if cURL is loaded/available
406
        if (!function_exists('curl_init')) {
407
            // @codeCoverageIgnoreStart
408
            return array(   "success" => false,
409
                            "curlHttpCode" => "-",
410
                            "curlError" => "-",
411
                            "message" => "No Spam-Check done. cURL module is not available.",
412
                    );
413
            // @codeCoverageIgnoreEnd
414
        }
415
416
        $ch = curl_init("http://spamcheck.postmarkapp.com/filter");
417
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
418
        curl_setopt($ch, CURLOPT_POST, true);
419
        $data = array("email" => $msgString, "options" => $report);
420
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
421
        curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-Type: application/json", "Accept: application/json"));
422
        curl_setopt($ch, CURLOPT_TIMEOUT, 5); // max wait for 5sec for reply
423
424
        $result = json_decode(curl_exec($ch), true);
425
        $error = curl_error($ch);
426
        $result['curlHttpCode'] = curl_getinfo($ch, CURLINFO_HTTP_CODE);
427
        curl_close($ch);
428
429
        if (strlen($error) > 0) {
430
            $result['curlError'] = $error;
431
        }
432
433
        if (!array_key_exists("message", $result)) {
434
            $result['message'] = "-";
435
        }
436
437
        if (!array_key_exists('success', $result)) {
438
            $result['message'] = "Something went wrong! Here's the content of the curl-reply:\n\n".nl2br(print_r($result, true));
439
440
        } elseif (!$result['success'] && strpos($msgString, "Content-Transfer-Encoding: base64") !== false) {
441
            $result['message'] = $result['message']."\n\nRemoving the base64-Encoded Mime-Parts might help.";
442
443
        }
444
445
        return $result;
446
447
    }
448
449
    /**
450
     * Ajax action to check the spam-score for the pasted email-source
451
     */
452
    public function checkSpamScoreOfSentEmailAction(Request $request)
453
    {
454
        $msgString = $request->get('emailSource');
455
        $spamReport = $this->getSpamIndexReport($msgString);
456
        $spamInfo = "";
457
        if (is_array($spamReport)) {
458
            if (array_key_exists('curlHttpCode', $spamReport) && $spamReport['curlHttpCode'] == 200 && $spamReport['success'] && array_key_exists('score', $spamReport)) {
459
                $spamScore = $spamReport['score'];
460
                $spamInfo = "SpamScore: $spamScore! \n".$spamReport['report'];
461
                //@codeCoverageIgnoreStart
462
                // this only happens if the spam-check-server has a problem / is not responding
463
            } else {
464
                if( array_key_exists('curlHttpCode', $spamReport) && array_key_exists('curlError', $spamReport) && array_key_exists('message', $spamReport)){
465
                    $spamInfo = "Getting the spam-info failed.
466
                    HttpCode: " . $spamReport['curlHttpCode'] . "
467
                    cURL-Error: " . $spamReport['curlError'] . "
468
                    SpamReportMsg: " . $spamReport['message'];
469
470
                } elseif ($spamReport !== null && is_array($spamReport)) {
471
                    $spamInfo = "Getting the spam-info failed. This was returned:
472
---Start----------------------------------------------
473
" . implode(";\n", $spamReport) ."
474
---End------------------------------------------------";
475
                }
476
                //@codeCoverageIgnoreEnd
477
            }
478
        }
479
480
        return new JsonResponse(array('result' => $spamInfo));
481
    }
482
}
483