Completed
Push — sendEmailChangeConfirmation ( 430b3c...f5bb7d )
by Dominik
06:40
created

AzineEmailTemplateController   C

Complexity

Total Complexity 60

Size/Duplication

Total Lines 438
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 13

Test Coverage

Coverage 78.97%

Importance

Changes 0
Metric Value
wmc 60
lcom 2
cbo 13
dl 0
loc 438
ccs 139
cts 176
cp 0.7897
rs 6.0975
c 0
b 0
f 0

13 Methods

Rating   Name   Duplication   Size   Complexity  
A indexAction() 0 12 1
C webPreViewAction() 0 70 8
B webViewAction() 0 46 4
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
B sendTestEmailAction() 0 57 9
A getSpamIndexReportForSwiftMessage() 0 4 1
C getSpamIndexReport() 0 45 7
C checkSpamScoreOfSentEmailAction() 0 30 11

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
namespace Azine\EmailBundle\Controller;
3
4
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
5
use Azine\EmailBundle\Services\AzineEmailTwigExtension;
6
use Symfony\Component\HttpFoundation\JsonResponse;
7
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
8
use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
9
use Symfony\Component\HttpFoundation\RedirectResponse;
10
use Azine\EmailBundle\Entity\SentEmail;
11
use Symfony\Component\HttpFoundation\Response;
12
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
13
use Symfony\Component\HttpFoundation\BinaryFileResponse;
14
use Symfony\Component\DependencyInjection\ContainerAware;
15
use Azine\EmailBundle\Services\TemplateProviderInterface;
16
17
/**
18
 * This controller provides the following actions:
19
 *
20
 * 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) .
21
 * webPreView: shows the selected html- or txt-email-template filled with the dummy-data you defined (in the WebViewServiceInterface::getDummyVarsFor() function).
22
 * webView: shows an email that has been sent (and stored as SentEmail-entity in the database)
23
 * sendTestMail: sends an email filled with the dummy-data you defined to the selected email-address.
24
 * serveImage: serve an image from the template-image-folder
25
 *
26
 * @author dominik
27
 */
28
class AzineEmailTemplateController extends ContainerAware
0 ignored issues
show
Deprecated Code introduced by
The class Symfony\Component\Depend...njection\ContainerAware has been deprecated with message: since version 2.8, to be removed in 3.0. Use the ContainerAwareTrait instead.

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
29
{
30
    /**
31
     * Show a set of options to view html- and text-versions of email in the browser and send them as emails to test-accounts
32
     */
33 1
    public function indexAction()
34
    {
35 1
        $customEmail = $this->container->get('request')->get('customEmail', '[email protected]');
36 1
        $templates = $this->container->get('azine_email_web_view_service')->getTemplatesForWebPreView();
37 1
        $emails = $this->container->get('azine_email_web_view_service')->getTestMailAccounts();
38
39 1
        return $this->container->get('templating')->renderResponse("AzineEmailBundle:Webview:index.html.twig",
40 1
                                                                     array(	'customEmail'	=> $customEmail,
41 1
                                                                            'templates'		=> $templates,
42 1
                                                                            'emails'		=> $emails,
43 1
                                                                            ));
44
    }
45
46
    /**
47
     * Show a web-preview-version of an email-template, filled with dummy-content
48
     * @param string $format
49
     * @return Response
50
     */
51 1
    public function webPreViewAction($template, $format = null)
52
    {
53 1
        if ($format !== "txt") {
54 1
            $format = "html";
55 1
        }
56
57 1
        $request = $this->container->get('request');
58 1
        $locale = $request->getLocale();
59
60
        // merge request vars with dummyVars, but make sure request vars remain as they are.
61 1
        $emailVars = array_merge(array(), $request->query->all());
62 1
        $emailVars = $this->container->get('azine_email_web_view_service')->getDummyVarsFor($template, $locale, $emailVars);
63 1
        $emailVars = array_merge($emailVars, $request->query->all());
64
65
        // add the styles
66 1
        $emailVars = $this->getTemplateProviderService()->addTemplateVariablesFor($template, $emailVars);
67
68
        // add the from-email for the footer-text
69 1
        if (!array_key_exists('fromEmail', $emailVars)) {
70 1
            $noReply = $this->container->getParameter('azine_email_no_reply');
71 1
            $emailVars['fromEmail'] = $noReply['email'];
72 1
            $emailVars['fromName'] = $noReply['name'];
73 1
        }
74
75
        // set the emailLocale for the templates
76 1
        $emailVars['emailLocale'] = $locale;
77
78
        // replace absolute image-paths with relative ones.
79 1
        $emailVars = $this->getTemplateProviderService()->makeImagePathsWebRelative($emailVars, $locale);
80
81
        // add code-snippets
82 1
        $emailVars = $this->getTemplateProviderService()->addTemplateSnippetsWithImagesFor($template, $emailVars, $this->container->get('request')->getLocale());
83
84
        // render & return email
85 1
        $response = $this->renderResponse("$template.$format.twig", $emailVars);
86
87
        // add campaign tracking params
88 1
        $campaignParams = $this->getTemplateProviderService()->getCampaignParamsFor($template, $emailVars);
89 1
        $campaignParams['utm_medium'] = 'webPreview';
90 1
        if(sizeof($campaignParams) > 0) {
91 1
            $htmlBody = $response->getContent();
92 1
            $htmlBody = $this->container->get("azine.email.bundle.twig.filters")->addCampaignParamsToAllUrls($htmlBody, $campaignParams);
93
94 1
            $emailOpenTrackingCodeBuilder = $this->container->get('azine_email_email_open_tracking_code_builder');
95 1
            if ($emailOpenTrackingCodeBuilder) {
96
                // add an image at the end of the html tag with the tracking-params to track email-opens
97 1
                $imgTrackingCode = $emailOpenTrackingCodeBuilder->getTrackingImgCode($template, $campaignParams, $emailVars, "dummy", "[email protected]", null, null);
98 1
                if ($imgTrackingCode && strlen($imgTrackingCode) > 0) {
99
                    // replace the tracking url, so no request is made to the real tracking system.
100 1
                    $imgTrackingCode = str_replace("://", "://webview-dummy-domain.", $imgTrackingCode);
101 1
                    $htmlCloseTagPosition = strpos($htmlBody, "</html>");
102 1
                    $htmlBody = substr_replace($htmlBody, $imgTrackingCode, $htmlCloseTagPosition, 0);
103 1
                }
104 1
            }
105 1
            $response->setContent($htmlBody);
106 1
        }
107
108
        // if the requested format is txt, remove the html-part
109 1
        if ($format == "txt") {
110
            // set the correct content-type
111 1
            $response->headers->set("Content-Type","text/plain");
112
113
            // cut away the html-part
114 1
            $content = $response->getContent();
115 1
            $textEnd = stripos($content, "<!doctype");
116 1
            $response->setContent(substr($content, 0, $textEnd));
117 1
        }
118
119 1
        return $response;
120
    }
121
122
    /**
123
     * Show a web-version of an email that has been sent to recipients and has been stored in the database.
124
     * @param string $token
125
     */
126 6
    public function webViewAction($token)
127
    {
128
        // find email recipients, template & params
129 6
        $sentEmail = $this->getSentEmailForToken($token);
130
131
        // check if the sent email is available
132 6
        if ($sentEmail != null) {
133
134
            // check if the current user is allowed to see the email
135 5
            if ($this->userIsAllowedToSeeThisMail($sentEmail)) {
136
137 3
                $template = $sentEmail->getTemplate();
138 3
                $emailVars = $sentEmail->getVariables();
139
140
                // re-attach all entities to the EntityManager.
141 3
                $this->reAttachAllEntities($emailVars);
142
143
                // remove the web-view-token from the param-array
144 3
                $templateProvider = $this->getTemplateProviderService();
145 3
                unset($emailVars[$templateProvider->getWebViewTokenId()]);
146
147
                // render & return email
148 3
                $response = $this->renderResponse("$template.html.twig", $emailVars);
149
150 3
                $campaignParams = $templateProvider->getCampaignParamsFor($template, $emailVars);
151
152 3
                if (sizeof($campaignParams) > 0) {
153 1
                    $response->setContent($this->container->get("azine.email.bundle.twig.filters")->addCampaignParamsToAllUrls($response->getContent(), $campaignParams));
154 1
                }
155
156 3
                return $response;
157
158
            // if the user is not allowed to see this mail
159
            } else {
160 2
                $msg = $this->container->get('translator')->trans('web.pre.view.test.mail.access.denied');
161 2
                throw new AccessDeniedException($msg);
162
            }
163
        }
164
165
        // the parameters-array is null => the email is not available in webView
166 1
        $days = $this->container->getParameter("azine_email_web_view_retention");
167 1
        $response = $this->renderResponse("AzineEmailBundle:Webview:mail.not.available.html.twig", array('days' => $days));
168 1
        $response->setStatusCode(404);
169
170 1
        return $response;
171
    }
172
173
    /**
174
     * Check if the user is allowed to see the email.
175
     * => the mail is public or the user is among the recipients or the user is an admin.
176
     *
177
     * @param  SentEmail $mail
178
     * @return boolean
179
     */
180 5
    private function userIsAllowedToSeeThisMail(SentEmail $mail)
181
    {
182 5
        $recipients = $mail->getRecipients();
183
184
        // it is a public email
185 5
        if ($recipients == null) {
186 1
            return true;
187
        }
188
189
        // get the current user
190 4
        $currentUser = null;
191 4
        if (!$this->container->has('security.token_storage')) {
192
            // @codeCoverageIgnoreStart
193
            throw new \LogicException('The SecurityBundle is not registered in your application.');
194
            // @codeCoverageIgnoreEnd
195
196
        } else {
197 4
            $token = $this->container->get('security.token_storage')->getToken();
198
199
            // check if the token is not null and the user in the token an object
200 4
            if ($token instanceof TokenInterface && is_object($token->getUser())) {
201 3
                $currentUser = $token->getUser();
202 3
            }
203
        }
204
205
        // it is not a public email, and a user is logged in
206 4
        if ($currentUser != null) {
207
208
            // the user is among the recipients
209 3
            if(array_search($currentUser->getEmail(), $recipients) !== false)
210
211 3
                return true;
212
213
            // the user is admin
214 2
            if($currentUser->hasRole("ROLE_ADMIN"))
215
216 2
                return true;
217 1
        }
218
219
        // not public email, but
220
        // 		- there is no user, or
221
        //		- the user is not among the recipients and
222
        //		- the user not an admin-user either
223 2
        return false;
224
    }
225
226
    /**
227
     * Replace all unmanaged Objects in the array (recursively)
228
     * by managed Entities fetched via Doctrine EntityManager.
229
     *
230
     *  It is assumed that managed objects can be identified
231
     *  by their id and implement the function getId() to get that id.
232
     *
233
     * @param  array $vars passed by reference & manipulated but not returned.
234
     * @return null
235
     */
236 3
    private function reAttachAllEntities(array &$vars)
237
    {
238 3
        $em = $this->container->get('doctrine')->getManager();
239 3
        foreach ($vars as $key => $next) {
240
            if (is_object($next) && method_exists($next, 'getId')) {
241
                $className = get_class($next);
242
                $managedEntity = $em->find($className, $next->getId());
243
                if ($managedEntity) {
244
                    $vars[$key] = $managedEntity;
245
                }
246
                continue;
247
            } elseif (is_array($next)) {
248
                $this->reAttachAllEntities($next);
249
                continue;
250
            }
251 3
        }
252
253 3
    }
254
255
    /**
256
     * Serve the image from the templates-folder
257
     * @param  string                                               $filename
258
     * @param  string                                               $folderKey
259
     * @return \Symfony\Component\HttpFoundation\BinaryFileResponse
260
     */
261 2
    public function serveImageAction($folderKey, $filename)
262
    {
263 2
        $folder = $this->getTemplateProviderService()->getFolderFrom($folderKey);
264 2
        if ($folder !== false) {
265 1
            $fullPath = $folder.$filename;
266 1
            $response = BinaryFileResponse::create($fullPath);
267 1
            $response->setContentDisposition(ResponseHeaderBag::DISPOSITION_INLINE);
268 1
            $response->headers->set("Content-Type", "image");
269
270 1
            return $response;
271
        }
272
273 1
        throw new FileNotFoundException($filename);
274
    }
275
276
    /**
277
     * @return TemplateProviderInterface
278
     */
279 6
    protected function getTemplateProviderService()
280
    {
281 6
        return $this->container->get('azine_email_template_provider');
282
    }
283
284
    /**
285
     * @param  string   $view
286
     * @param  array    $parameters
287
     * @param  Response $response
288
     * @return Response
289
     */
290 5
    protected function renderResponse($view, array $parameters = array(), Response $response = null)
291
    {
292 5
        return $this->container->get('templating')->renderResponse($view, $parameters, $response);
293
    }
294
295
    /**
296
     * Get the sent email from the database
297
     * @param  string    $token the token identifying the sent email
298
     * @return SentEmail
299
     */
300 6
    protected function getSentEmailForToken($token)
301
    {
302 6
        $sentEmail = $this->container->get('doctrine')->getRepository('AzineEmailBundle:SentEmail')->findOneByToken($token);
303
304 6
        return $sentEmail;
305
    }
306
307
    /**
308
     * Send a test-mail for the template to the given email-address
309
     * @param  string                                             $template templateId without ending => AzineEmailBundle::baseEmailLayout (without .txt.twig)
310
     * @param  string                                             $email
311
     * @return \Symfony\Component\HttpFoundation\RedirectResponse
312
     */
313
    public function sendTestEmailAction($template, $email)
314
    {
315
        $locale = $this->container->get('request')->getLocale();
316
317
        // get the email-vars for email-sending => absolute fs-paths to images
318
        $emailVars = $this->container->get('azine_email_web_view_service')->getDummyVarsFor($template, $locale);
319
320
        // send the mail
321
        $message = \Swift_Message::newInstance();
322
        $mailer = $this->container->get("azine_email_template_twig_swift_mailer");
323
        $sent = $mailer->sendSingleEmail($email, "Test Recipient", $emailVars['subject'], $emailVars, $template.".txt.twig", $this->container->get('request')->getLocale(), $emailVars['sendMailAccountAddress'], $emailVars['sendMailAccountName']." (Test)", $message);
324
325
        $spamReport = $this->getSpamIndexReportForSwiftMessage($message);
326
        if (is_array($spamReport)) {
327
            if ($spamReport['curlHttpCode'] == 200 && $spamReport['success']) {
328
                $spamScore = $spamReport['score'];
329
                $spamInfo = "SpamScore: $spamScore! \n".$spamReport['report'];
330
            } else {
331
                //@codeCoverageIgnoreStart
332
                // this only happens if the spam-check-server has a problem / is not responding
333
                $spamScore = 10;
334
                $spamInfo = "Getting the spam-info failed.
335
                             HttpCode: ".$spamReport['curlHttpCode']."
336
                             SpamReportMsg: ".$spamReport['message'];
337
                if(array_key_exists('curlError', $spamReport)) {
338
                    $spamInfo .= "
339
                             cURL-Error: " . $spamReport['curlError'];
340
                }
341
                //@codeCoverageIgnoreEnd
342
            }
343
344
            if ($spamScore <= 2) {
345
                $this->container->get('session')->getFlashBag()->add('info', $spamInfo);
346
            } elseif ($spamScore > 2 && $spamScore < 5) {
347
                $this->container->get('session')->getFlashBag()->add('warn', $spamInfo);
348
            } else {
349
                $this->container->get('session')->getFlashBag()->add('error', $spamInfo);
350
            }
351
352
        }
353
354
        // inform about sent/failed emails
355
        if ($sent) {
356
            $msg = $this->container->get('translator')->trans('web.pre.view.test.mail.sent.for.%template%.to.%email%', array('%template%' => $template, '%email%' => $email));
357
            $this->container->get('session')->getFlashBag()->add('info', $msg);
358
359
            //@codeCoverageIgnoreStart
360
        } else {
361
            // this only happens if the mail-server has a problem
362
            $msg = $this->container->get('translator')->trans('web.pre.view.test.mail.failed.for.%template%.to.%email%', array('%template%' => $template, '%email%' => $email));
363
            $this->container->get('session')->getFlashBag()->add('warn', $msg);
364
            //@codeCoverageIgnoreStart
365
        }
366
367
        // show the index page again.
368
        return new RedirectResponse($this->container->get('router')->generate('azine_email_template_index', array('customEmail' => $email)));
369
    }
370
371
    /**
372
     * Make an RESTful call to http://spamcheck.postmarkapp.com/filter to test the emails-spam-index.
373
     * See http://spamcheck.postmarkapp.com/doc
374
     * @return array TestResult array('success', 'message', 'curlHttpCode', 'curlError', ['score', 'report'])
375
     */
376
    public function getSpamIndexReportForSwiftMessage(\Swift_Message $message, $report = 'long')
377
    {
378
        return $this->getSpamIndexReport($message->toString(), $report);
379
    }
380
381
    /**
382
     * @param $msgString
383
     * @param string $report
384
     * @return mixed
385
     */
386 2
    private function getSpamIndexReport($msgString, $report = 'long')
387
    {
388
        // check if cURL is loaded/available
389
        if (!function_exists('curl_init')) {
390
            // @codeCoverageIgnoreStart
391
            return array(	"success" => false,
392
                            "curlHttpCode" => "-",
393
                            "curlError" => "-",
394
                            "message" => "No Spam-Check done. cURL module is not available.",
395
                    );
396
            // @codeCoverageIgnoreEnd
397
        }
398
399 2
        $ch = curl_init("http://spamcheck.postmarkapp.com/filter");
400 2
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
401 2
        curl_setopt($ch, CURLOPT_POST, true);
402 2
        $data = array("email" => $msgString, "options" => $report);
403 2
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
404 2
        curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-Type: application/json", "Accept: application/json"));
405 2
        curl_setopt($ch, CURLOPT_TIMEOUT, 5); // max wait for 5sec for reply
406
407 2
        $result = json_decode(curl_exec($ch), true);
408 2
        $error = curl_error($ch);
409 2
        $result['curlHttpCode'] = curl_getinfo($ch, CURLINFO_HTTP_CODE);
410 2
        curl_close($ch);
411
412 2
        if (strlen($error) > 0) {
413
            $result['curlError'] = $error;
414
        }
415
416 2
        if (!array_key_exists("message", $result)) {
417 2
            $result['message'] = "-";
418 2
        }
419
420 2
        if (!array_key_exists('success', $result)) {
421
            $result['message'] = "Something went wrong! Here's the content of the curl-reply:\n\n".nl2br(print_r($result, true));
422
423 2
        } elseif (!$result['success'] && strpos($msgString, "Content-Transfer-Encoding: base64") !== false) {
424
            $result['message'] = $result['message']."\n\nRemoving the base64-Encoded Mime-Parts might help.";
425
426
        }
427
428 2
        return $result;
429
430
    }
431
432
    /**
433
     * Ajax action to check the spam-score for the pasted email-source
434
     */
435 1
    public function checkSpamScoreOfSentEmailAction()
436
    {
437 1
        $msgString = $this->container->get('request')->get('emailSource');
438 1
        $spamReport = $this->getSpamIndexReport($msgString);
439 1
        $spamInfo = "";
440 1
        if (is_array($spamReport)) {
441 1
            if (array_key_exists('curlHttpCode', $spamReport) && $spamReport['curlHttpCode'] == 200 && $spamReport['success'] && array_key_exists('score', $spamReport)) {
442 1
                $spamScore = $spamReport['score'];
443 1
                $spamInfo = "SpamScore: $spamScore! \n".$spamReport['report'];
444
                //@codeCoverageIgnoreStart
445
                // this only happens if the spam-check-server has a problem / is not responding
446
            } else {
447
                if( array_key_exists('curlHttpCode', $spamReport) && array_key_exists('curlError', $spamReport) && array_key_exists('message', $spamReport)){
448
                    $spamInfo = "Getting the spam-info failed.
449
                    HttpCode: " . $spamReport['curlHttpCode'] . "
450
                    cURL-Error: " . $spamReport['curlError'] . "
451
                    SpamReportMsg: " . $spamReport['message'];
452
453
                } elseif ($spamReport != null && is_array($spamReport)) {
454
                    $spamInfo = "Getting the spam-info failed. This was returned:
455
---Start----------------------------------------------
456
" . implode(";\n", $spamReport) ."
457
---End------------------------------------------------";
458
                }
459
                //@codeCoverageIgnoreEnd
460
            }
461 1
        }
462
463 1
        return new JsonResponse(array('result' => $spamInfo));
464
    }
465
}
466