Completed
Pull Request — master (#11)
by
unknown
03:43
created

reAttachAllEntities()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 18
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 0
Metric Value
dl 0
loc 18
ccs 0
cts 15
cp 0
rs 8.8571
c 0
b 0
f 0
cc 6
eloc 12
nc 5
nop 1
crap 42
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\Bundle\FrameworkBundle\Controller\Controller;
15
use Symfony\Component\HttpFoundation\Request;
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 extends Controller
30
{
31
32
    /**
33
     * Show a set of options to view html- and text-versions of email in the browser and send them as emails to test-accounts
34
     */
35
    public function indexAction(Request $request)
36
    {
37
        $customEmail = $request->get('customEmail', '[email protected]');
38
        $templates = $this->get('azine_email_web_view_service')->getTemplatesForWebPreView();
39
        $emails = $this->get('azine_email_web_view_service')->getTestMailAccounts();
40
41
        return $this->get('templating')
42
                    ->renderResponse("AzineEmailBundle:Webview:index.html.twig",
43
                    array(	
44
                        'customEmail' => $customEmail,
45
                        'templates'   => $templates,
46
                        'emails'      => $emails,
47
                    ));
48
    }
49
50
    /**
51
     * Show a web-preview-version of an email-template, filled with dummy-content
52
     * @param string $format
53
     * @return Response
54
     */
55 1
    public function webPreViewAction($template, $format = null)
56
    {
57 1
        $request = $this->get('request');
58 1
        if ($format !== "txt") {
59 1
            $format = "html";
60 1
        }
61
62 1
        $locale = $request->getLocale();
63
64
        // merge request vars with dummyVars, but make sure request vars remain as they are.
65 1
        $emailVars = array_merge(array(), $request->query->all());
66 1
        $emailVars = $this->get('azine_email_web_view_service')->getDummyVarsFor($template, $locale, $emailVars);
67 1
        $emailVars = array_merge($emailVars, $request->query->all());
68
69
        // add the styles
70 1
        $emailVars = $this->getTemplateProviderService()->addTemplateVariablesFor($template, $emailVars);
71
72
        // add the from-email for the footer-text
73 1
        if (!array_key_exists('fromEmail', $emailVars)) {
74 1
            $noReply = $this->getParameter('azine_email_no_reply');
75 1
            $emailVars['fromEmail'] = $noReply['email'];
76 1
            $emailVars['fromName'] = $noReply['name'];
77 1
        }
78
79
        // set the emailLocale for the templates
80 1
        $emailVars['emailLocale'] = $locale;
81
82
        // replace absolute image-paths with relative ones.
83 1
        $emailVars = $this->getTemplateProviderService()->makeImagePathsWebRelative($emailVars, $locale);
84
85
        // add code-snippets
86 1
        $emailVars = $this->getTemplateProviderService()->addTemplateSnippetsWithImagesFor($template, $emailVars, $locale);
87
88
        // render & return email
89 1
        $response = $this->renderResponse("$template.$format.twig", $emailVars);
90
91
        // add campaign tracking params
92 1
        $campaignParams = $this->getTemplateProviderService()->getCampaignParamsFor($template, $emailVars);
93 1
        $campaignParams['utm_medium'] = 'webPreview';
94 1
        if(sizeof($campaignParams) > 0) {
95 1
            $htmlBody = $response->getContent();
96 1
            $htmlBody = $this->get("azine.email.bundle.twig.filters")->addCampaignParamsToAllUrls($htmlBody, $campaignParams);
97
98 1
            $emailOpenTrackingCodeBuilder = $this->get('azine_email_email_open_tracking_code_builder');
99 1
            if ($emailOpenTrackingCodeBuilder) {
100
                // add an image at the end of the html tag with the tracking-params to track email-opens
101 1
                $imgTrackingCode = $emailOpenTrackingCodeBuilder->getTrackingImgCode($template, $campaignParams, $emailVars, "dummy", "[email protected]", null, null);
102 1
                if ($imgTrackingCode && strlen($imgTrackingCode) > 0) {
103
                    // replace the tracking url, so no request is made to the real tracking system.
104 1
                    $imgTrackingCode = str_replace("://", "://webview-dummy-domain.", $imgTrackingCode);
105 1
                    $htmlCloseTagPosition = strpos($htmlBody, "</html>");
106 1
                    $htmlBody = substr_replace($htmlBody, $imgTrackingCode, $htmlCloseTagPosition, 0);
107 1
                }
108 1
            }
109 1
            $response->setContent($htmlBody);
110 1
        }
111
112
        // if the requested format is txt, remove the html-part
113 1
        if ($format == "txt") {
114
            // set the correct content-type
115 1
            $response->headers->set("Content-Type","text/plain");
116
117
            // cut away the html-part
118 1
            $content = $response->getContent();
119 1
            $textEnd = stripos($content, "<!doctype");
120 1
            $response->setContent(substr($content, 0, $textEnd));
121 1
        }
122
123 1
        return $response;
124
    }
125
126
    /**
127
     * Show a web-version of an email that has been sent to recipients and has been stored in the database.
128
     * @param string $token
129
     */
130 6
    public function webViewAction ($token)
131
    {
132
        // find email recipients, template & params
133 6
        $sentEmail = $this->getSentEmailForToken($token);
134
135
        // check if the sent email is available
136
        if ($sentEmail != null) {
137
138
            // check if the current user is allowed to see the email
139
            if ($this->userIsAllowedToSeeThisMail($sentEmail)) {
140
141
                $template = $sentEmail->getTemplate();
142
                $emailVars = $sentEmail->getVariables();
143
144
                // re-attach all entities to the EntityManager.
145
                $this->reAttachAllEntities($emailVars);
146
147
                // remove the web-view-token from the param-array
148
                $templateProvider = $this->getTemplateProviderService();
149
                unset($emailVars[$templateProvider->getWebViewTokenId()]);
150
151
                // render & return email
152
                $response = $this->renderResponse("$template.html.twig", $emailVars);
153
154
                $campaignParams = $templateProvider->getCampaignParamsFor($template, $emailVars);
155
156
                if (sizeof($campaignParams) > 0) {
157
                    $response->setContent($this->get("azine.email.bundle.twig.filters")->addCampaignParamsToAllUrls($response->getContent(), $campaignParams));
158
                }
159
160
                return $response;
161
162
            // if the user is not allowed to see this mail
163
            } else {
164
                $msg = $this->getTranslator()->trans('web.pre.view.test.mail.access.denied');
0 ignored issues
show
Bug introduced by
The method getTranslator() does not seem to exist on object<Azine\EmailBundle...mailTemplateController>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
165
                throw new AccessDeniedException($msg);
166
            }
167
        }
168
169
        // the parameters-array is null => the email is not available in webView
170
        $days = $this->getParameter("azine_email_web_view_retention");
171
        $response = $this->renderResponse("AzineEmailBundle:Webview:mail.not.available.html.twig", array('days' => $days));
172
        $response->setStatusCode(404);
173
174
        return $response;
175
    }
176
177
    /**
178
     * Check if the user is allowed to see the email.
179
     * => the mail is public or the user is among the recipients or the user is an admin.
180
     *
181
     * @param  SentEmail $mail
182
     * @return boolean
183
     */
184
    private function userIsAllowedToSeeThisMail(SentEmail $mail)
185
    {
186
        $recipients = $mail->getRecipients();
187
188
        // it is a public email
189
        if ($recipients == null) {
190
            return true;
191
        }
192
193
        // get the current user
194
        $currentUser = null;
195
        if (!$this->has('security.token_storage')) {
196
            // @codeCoverageIgnoreStart
197
            throw new \LogicException('The SecurityBundle is not registered in your application.');
198
            // @codeCoverageIgnoreEnd
199
200
        } else {
201
            $token = $this->get('security.token_storage')->getToken();
202
203
            // check if the token is not null and the user in the token an object
204
            if ($token instanceof TokenInterface && is_object($token->getUser())) {
205
                $currentUser = $token->getUser();
206
            }
207
        }
208
209
        // it is not a public email, and a user is logged in
210
        if ($currentUser != null) {
211
212
            // the user is among the recipients
213
            if(array_search($currentUser->getEmail(), $recipients) !== false)
214
215
                return true;
216
217
            // the user is admin
218
            if($currentUser->hasRole("ROLE_ADMIN"))
219
220
                return true;
221
        }
222
223
        // not public email, but
224
        // 		- there is no user, or
225
        //		- the user is not among the recipients and
226
        //		- the user not an admin-user either
227
        return false;
228
    }
229
230
    /**
231
     * Replace all unmanaged Objects in the array (recursively)
232
     * by managed Entities fetched via Doctrine EntityManager.
233
     *
234
     *  It is assumed that managed objects can be identified
235
     *  by their id and implement the function getId() to get that id.
236
     *
237
     * @param  array $vars passed by reference & manipulated but not returned.
238
     * @return null
239
     */
240
    private function reAttachAllEntities(array &$vars)
241
    {
242
        $em = $this->getDoctrine()->getManager();
243
        foreach ($vars as $key => $next) {
244
            if (is_object($next) && method_exists($next, 'getId')) {
245
                $className = get_class($next);
246
                $managedEntity = $em->find($className, $next->getId());
247
                if ($managedEntity) {
248
                    $vars[$key] = $managedEntity;
249
                }
250
                continue;
251
            } elseif (is_array($next)) {
252
                $this->reAttachAllEntities($next);
253
                continue;
254
            }
255
        }
256
257
    }
258
259
    /**
260
     * Serve the image from the templates-folder
261
     * @param  string                                               $filename
262
     * @param  string                                               $folderKey
263
     * @return \Symfony\Component\HttpFoundation\BinaryFileResponse
264
     */
265 2
    public function serveImageAction($folderKey, $filename)
266
    {
267 2
        $folder = $this->getTemplateProviderService()->getFolderFrom($folderKey);
268 2
        if ($folder !== false) {
269 1
            $fullPath = $folder.$filename;
270 1
            $response = BinaryFileResponse::create($fullPath);
271 1
            $response->setContentDisposition(ResponseHeaderBag::DISPOSITION_INLINE);
272 1
            $response->headers->set("Content-Type", "image");
273
274 1
            return $response;
275
        }
276
277 1
        throw new FileNotFoundException($filename);
278
    }
279
280
    /**
281
     * @return TemplateProviderInterface
282
     */
283 3
    protected function getTemplateProviderService()
284
    {
285 3
        return $this->get('azine_email_template_provider');
286
    }
287
288
    /**
289
     * @param  string   $view
290
     * @param  array    $parameters
291
     * @param  Response $response
292
     * @return Response
293
     */
294 1
    protected function renderResponse($view, array $parameters = array(), Response $response = null)
295
    {
296 1
        return $this->get('templating')->renderResponse($view, $parameters, $response);
297
    }
298
299
    /**
300
     * Get the sent email from the database
301
     * @param  string    $token the token identifying the sent email
302
     * @return SentEmail
303
     */
304 6
    protected function getSentEmailForToken($token)
305
    {
306 6
        $sentEmail = $this->getDoctrine()->getRepository('AzineEmailBundle:SentEmail')->findOneByToken($token);
307
308
        return $sentEmail;
309
    }
310
311
    /**
312
     * Send a test-mail for the template to the given email-address
313
     * @param  string                                             $template templateId without ending => AzineEmailBundle::baseEmailLayout (without .txt.twig)
314
     * @param  string                                             $email
315
     * @return \Symfony\Component\HttpFoundation\RedirectResponse
316
     */
317
    public function sendTestEmailAction(Request $request, $template, $email)
318
    {
319
        $locale = $request->getLocale();
320
321
        // get the email-vars for email-sending => absolute fs-paths to images
322
        $emailVars = $this->get('azine_email_web_view_service')->getDummyVarsFor($template, $locale);
323
324
        // send the mail
325
        $message = \Swift_Message::newInstance();
326
        $mailer = $this->get("azine_email_template_twig_swift_mailer");
327
        $sent = $mailer->sendSingleEmail($email, "Test Recipient", $emailVars['subject'], $emailVars, $template.".txt.twig", $locale, $emailVars['sendMailAccountAddress'], $emailVars['sendMailAccountName']." (Test)", $message);
328
329
        $spamReport = $this->getSpamIndexReportForSwiftMessage($message);
330
        if (is_array($spamReport)) {
331
            if ($spamReport['curlHttpCode'] == 200 && $spamReport['success']) {
332
                $spamScore = $spamReport['score'];
333
                $spamInfo = "SpamScore: $spamScore! \n".$spamReport['report'];
334
            } else {
335
                //@codeCoverageIgnoreStart
336
                // this only happens if the spam-check-server has a problem / is not responding
337
                $spamScore = 10;
338
                $spamInfo = "Getting the spam-info failed.
339
                             HttpCode: ".$spamReport['curlHttpCode']."
340
                             SpamReportMsg: ".$spamReport['message'];
341
                if(array_key_exists('curlError', $spamReport)) {
342
                    $spamInfo .= "
343
                             cURL-Error: " . $spamReport['curlError'];
344
                }
345
                //@codeCoverageIgnoreEnd
346
            }
347
348
            if ($spamScore <= 2) {
349
                $request->getSession()->getFlashBag()->add('info', $spamInfo);
350
            } elseif ($spamScore > 2 && $spamScore < 5) {
351
                $request->getSession()->getFlashBag()->add('warn', $spamInfo);
352
            } else {
353
                $request->getSession()->getFlashBag()->add('error', $spamInfo);
354
            }
355
356
        }
357
358
        // inform about sent/failed emails
359
        if ($sent) {
360
            $msg = $this->getTranslator()->trans('web.pre.view.test.mail.sent.for.%template%.to.%email%', array('%template%' => $template, '%email%' => $email));
0 ignored issues
show
Bug introduced by
The method getTranslator() does not seem to exist on object<Azine\EmailBundle...mailTemplateController>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
361
            $this->getSession()->getFlashBag()->add('info', $msg);
0 ignored issues
show
Bug introduced by
The method getSession() does not seem to exist on object<Azine\EmailBundle...mailTemplateController>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
362
363
            //@codeCoverageIgnoreStart
364
        } else {
365
            // this only happens if the mail-server has a problem
366
            $msg = $this->getTranslator()->trans('web.pre.view.test.mail.failed.for.%template%.to.%email%', array('%template%' => $template, '%email%' => $email));
0 ignored issues
show
Bug introduced by
The method getTranslator() does not seem to exist on object<Azine\EmailBundle...mailTemplateController>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
367
            $this->getSession()->getFlashBag()->add('warn', $msg);
0 ignored issues
show
Bug introduced by
The method getSession() does not seem to exist on object<Azine\EmailBundle...mailTemplateController>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
368
            //@codeCoverageIgnoreStart
369
        }
370
371
        // show the index page again.
372
        return new RedirectResponse($this->get('router')->generate('azine_email_template_index', array('customEmail' => $email)));
373
    }
374
375
    /**
376
     * Make an RESTful call to http://spamcheck.postmarkapp.com/filter to test the emails-spam-index.
377
     * See http://spamcheck.postmarkapp.com/doc
378
     * @return array TestResult array('success', 'message', 'curlHttpCode', 'curlError', ['score', 'report'])
379
     */
380
    public function getSpamIndexReportForSwiftMessage(\Swift_Message $message, $report = 'long')
381
    {
382
        return $this->getSpamIndexReport($message->toString(), $report);
383
    }
384
385
    /**
386
     * @param $msgString
387
     * @param string $report
388
     * @return mixed
389
     */
390 1
    private function getSpamIndexReport($msgString, $report = 'long')
391
    {
392
        // check if cURL is loaded/available
393
        if (!function_exists('curl_init')) {
394
            // @codeCoverageIgnoreStart
395
            return array(   "success" => false,
396
                            "curlHttpCode" => "-",
397
                            "curlError" => "-",
398
                            "message" => "No Spam-Check done. cURL module is not available.",
399
                    );
400
            // @codeCoverageIgnoreEnd
401
        }
402
403 1
        $ch = curl_init("http://spamcheck.postmarkapp.com/filter");
404 1
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
405 1
        curl_setopt($ch, CURLOPT_POST, true);
406 1
        $data = array("email" => $msgString, "options" => $report);
407 1
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
408 1
        curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-Type: application/json", "Accept: application/json"));
409 1
        curl_setopt($ch, CURLOPT_TIMEOUT, 5); // max wait for 5sec for reply
410
411 1
        $result = json_decode(curl_exec($ch), true);
412 1
        $error = curl_error($ch);
413 1
        $result['curlHttpCode'] = curl_getinfo($ch, CURLINFO_HTTP_CODE);
414 1
        curl_close($ch);
415
416 1
        if (strlen($error) > 0) {
417
            $result['curlError'] = $error;
418
        }
419
420 1
        if (!array_key_exists("message", $result)) {
421 1
            $result['message'] = "-";
422 1
        }
423
424 1
        if (!array_key_exists('success', $result)) {
425
            $result['message'] = "Something went wrong! Here's the content of the curl-reply:\n\n".nl2br(print_r($result, true));
426
427 1
        } elseif (!$result['success'] && strpos($msgString, "Content-Transfer-Encoding: base64") !== false) {
428
            $result['message'] = $result['message']."\n\nRemoving the base64-Encoded Mime-Parts might help.";
429
430
        }
431
432 1
        return $result;
433
434
    }
435
436
    /**
437
     * Ajax action to check the spam-score for the pasted email-source
438
     */
439
    public function checkSpamScoreOfSentEmailAction(Request $request)
440
    {
441
        $msgString = $request->get('emailSource');
442
        $spamReport = $this->getSpamIndexReport($msgString);
443
        $spamInfo = "";
444
        if (is_array($spamReport)) {
445
            if (array_key_exists('curlHttpCode', $spamReport) && $spamReport['curlHttpCode'] == 200 && $spamReport['success'] && array_key_exists('score', $spamReport)) {
446
                $spamScore = $spamReport['score'];
447
                $spamInfo = "SpamScore: $spamScore! \n".$spamReport['report'];
448
                //@codeCoverageIgnoreStart
449
                // this only happens if the spam-check-server has a problem / is not responding
450
            } else {
451
                if( array_key_exists('curlHttpCode', $spamReport) && array_key_exists('curlError', $spamReport) && array_key_exists('message', $spamReport)){
452
                    $spamInfo = "Getting the spam-info failed.
453
                    HttpCode: " . $spamReport['curlHttpCode'] . "
454
                    cURL-Error: " . $spamReport['curlError'] . "
455
                    SpamReportMsg: " . $spamReport['message'];
456
457
                } elseif ($spamReport != null && is_array($spamReport)) {
458
                    $spamInfo = "Getting the spam-info failed. This was returned:
459
---Start----------------------------------------------
460
" . implode(";\n", $spamReport) ."
461
---End------------------------------------------------";
462
                }
463
                //@codeCoverageIgnoreEnd
464
            }
465
        }
466
467
        return new JsonResponse(array('result' => $spamInfo));
468
    }
469
}
470