Completed
Pull Request — master (#10)
by
unknown
03:45
created

AzineEmailTemplateController::indexAction()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 12
ccs 9
cts 9
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 8
nc 1
nop 0
crap 1
1
<?php
2
3
namespace Azine\EmailBundle\Controller;
4
5
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
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\ContainerAwareInterface;
15
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
16
use Azine\EmailBundle\Services\TemplateProviderInterface;
17
18
/**
19
 * This controller provides the following actions:
20
 *
21
 * 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) .
22
 * webPreView: shows the selected html- or txt-email-template filled with the dummy-data you defined (in the WebViewServiceInterface::getDummyVarsFor() function).
23
 * webView: shows an email that has been sent (and stored as SentEmail-entity in the database)
24
 * sendTestMail: sends an email filled with the dummy-data you defined to the selected email-address.
25
 * serveImage: serve an image from the template-image-folder
26
 *
27
 * @author dominik
28
 */
29
class AzineEmailTemplateController implements ContainerAwareInterface
30
{
31
    use ContainerAwareTrait;
32
33 1
/**
34
 * This controller provides the following actions:
35 1
 *
36 1
 * 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) .
37 1
 * webPreView: shows the selected html- or txt-email-template filled with the dummy-data you defined (in the WebViewServiceInterface::getDummyVarsFor() function).
38
 * webView: shows an email that has been sent (and stored as SentEmail-entity in the database)
39 1
 * sendTestMail: sends an email filled with the dummy-data you defined to the selected email-address.
40 1
 * serveImage: serve an image from the template-image-folder
41 1
 *
42 1
 * @author dominik
43 1
 */
44
class AzineEmailTemplateController extends ContainerAware
0 ignored issues
show
Bug introduced by
This code did not parse for me. Apparently, there is an error somewhere around this line:

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