Completed
Branch master (6e9dcf)
by Dominik
02:26
created

AzineEmailTemplateController::webPreViewAction()   C

Complexity

Conditions 8
Paths 32

Size

Total Lines 69
Code Lines 35

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 41
CRAP Score 8

Importance

Changes 8
Bugs 1 Features 4
Metric Value
c 8
b 1
f 4
dl 0
loc 69
ccs 41
cts 41
cp 1
rs 6.5437
cc 8
eloc 35
nc 32
nop 2
crap 8

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
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
     */
50 1
    public function webPreViewAction($template, $format = null)
51
    {
52 1
        if (!$format) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $format of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

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