Completed
Push — fix-phpunit-inclusion ( 32fa89 )
by Dominik
14:50
created

AzineEmailTemplateController::webViewAction()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 46
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 8.7414

Importance

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