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