This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | namespace LeKoala\Mailgun; |
||
4 | |||
5 | use \Exception; |
||
6 | use SilverStripe\Forms\Tab; |
||
7 | use SilverStripe\Forms\Form; |
||
8 | use SilverStripe\Forms\TabSet; |
||
9 | use SilverStripe\ORM\ArrayLib; |
||
10 | use SilverStripe\ORM\ArrayList; |
||
11 | use SilverStripe\View\ArrayData; |
||
12 | use SilverStripe\Control\Session; |
||
13 | use SilverStripe\Forms\DateField; |
||
14 | use SilverStripe\Forms\FieldList; |
||
15 | use SilverStripe\Forms\FormField; |
||
16 | use SilverStripe\Forms\TextField; |
||
17 | use LeKoala\Mailgun\MailgunHelper; |
||
18 | use SilverStripe\Control\Director; |
||
19 | use SilverStripe\Core\Environment; |
||
20 | use SilverStripe\Forms\FormAction; |
||
21 | use SilverStripe\Admin\LeftAndMain; |
||
22 | use SilverStripe\Forms\HiddenField; |
||
23 | use SilverStripe\Security\Security; |
||
24 | use SilverStripe\View\ViewableData; |
||
25 | use SilverStripe\Forms\LiteralField; |
||
26 | use SilverStripe\Control\Email\Email; |
||
27 | use SilverStripe\Forms\DropdownField; |
||
28 | use SilverStripe\Security\Permission; |
||
29 | use Mailgun\Model\Event\EventResponse; |
||
30 | use SilverStripe\Forms\CompositeField; |
||
31 | use SilverStripe\SiteConfig\SiteConfig; |
||
32 | use Mailgun\Model\Webhook\IndexResponse as WebhookIndexResponse; |
||
33 | use SilverStripe\Core\Injector\Injector; |
||
34 | use LeKoala\Mailgun\MailgunSwiftTransport; |
||
35 | use SilverStripe\Control\Email\SwiftMailer; |
||
36 | use SilverStripe\Forms\GridField\GridField; |
||
37 | use SilverStripe\Security\PermissionProvider; |
||
38 | use SilverStripe\Forms\GridField\GridFieldConfig; |
||
39 | use SilverStripe\Forms\GridField\GridFieldFooter; |
||
40 | use SilverStripe\Forms\GridField\GridFieldDetailForm; |
||
41 | use SilverStripe\Forms\GridField\GridFieldDataColumns; |
||
42 | use Symbiote\GridFieldExtensions\GridFieldTitleHeader; |
||
43 | use SilverStripe\Forms\GridField\GridFieldToolbarHeader; |
||
44 | use SilverStripe\Forms\GridField\GridFieldSortableHeader; |
||
45 | use Mailgun\Model\Domain\IndexResponse as DomainIndexResponse; |
||
46 | |||
47 | /** |
||
48 | * Allow you to see messages sent through the api key used to send messages |
||
49 | * |
||
50 | * @author LeKoala <[email protected]> |
||
51 | */ |
||
52 | class MailgunAdmin extends LeftAndMain implements PermissionProvider |
||
53 | { |
||
54 | |||
55 | const MESSAGE_CACHE_MINUTES = 5; |
||
56 | const WEBHOOK_CACHE_MINUTES = 1440; // 1 day |
||
57 | const SENDINGDOMAIN_CACHE_MINUTES = 1440; // 1 day |
||
58 | |||
59 | private static $menu_title = "Mailgun"; |
||
60 | private static $url_segment = "mailgun"; |
||
61 | private static $menu_icon = "mailgun/images/mailgun-icon.png"; |
||
62 | private static $url_rule = '/$Action/$ID/$OtherID'; |
||
63 | private static $allowed_actions = [ |
||
64 | 'settings', |
||
65 | 'SearchForm', |
||
66 | 'doSearch', |
||
67 | "doInstallHook", |
||
68 | "doUninstallHook", |
||
69 | "doInstallDomain", |
||
70 | "doUninstallDomain", |
||
71 | ]; |
||
72 | |||
73 | /** |
||
74 | * @var boolean |
||
75 | */ |
||
76 | private static $cache_enabled = true; |
||
77 | |||
78 | /** |
||
79 | * @var Exception |
||
80 | */ |
||
81 | protected $lastException; |
||
82 | |||
83 | /** |
||
84 | * @var ViewableData |
||
85 | */ |
||
86 | protected $currentMessage; |
||
87 | |||
88 | /** |
||
89 | * Inject public dependencies into the controller |
||
90 | * |
||
91 | * @var array |
||
92 | */ |
||
93 | private static $dependencies = [ |
||
94 | 'logger' => '%$Psr\Log\LoggerInterface', |
||
95 | 'cache' => '%$Psr\SimpleCache\CacheInterface.mailgun', // see _config/cache.yml |
||
96 | ]; |
||
97 | |||
98 | /** |
||
99 | * @var Psr\Log\LoggerInterface |
||
100 | */ |
||
101 | public $logger; |
||
102 | |||
103 | /** |
||
104 | * @var Psr\SimpleCache\CacheInterface |
||
105 | */ |
||
106 | public $cache; |
||
107 | |||
108 | public function init() |
||
109 | { |
||
110 | parent::init(); |
||
111 | |||
112 | if (isset($_GET['refresh'])) { |
||
113 | $this->getCache()->clear(); |
||
114 | } |
||
115 | } |
||
116 | |||
117 | public function index($request) |
||
118 | { |
||
119 | return parent::index($request); |
||
120 | } |
||
121 | |||
122 | public function settings($request) |
||
123 | { |
||
124 | return parent::index($request); |
||
125 | } |
||
126 | |||
127 | /** |
||
128 | * @return Session |
||
129 | */ |
||
130 | public function getSession() |
||
131 | { |
||
132 | return $this->getRequest()->getSession(); |
||
133 | } |
||
134 | |||
135 | /** |
||
136 | * Returns a GridField of messages |
||
137 | * @return CMSForm |
||
138 | */ |
||
139 | public function getEditForm($id = null, $fields = null) |
||
140 | { |
||
141 | if (!$id) { |
||
142 | $id = $this->currentPageID(); |
||
143 | } |
||
144 | |||
145 | $form = parent::getEditForm($id); |
||
146 | |||
147 | $record = $this->getRecord($id); |
||
148 | |||
149 | // Check if this record is viewable |
||
150 | if ($record && !$record->canView()) { |
||
151 | $response = Security::permissionFailure($this); |
||
152 | $this->setResponse($response); |
||
153 | return null; |
||
154 | } |
||
155 | |||
156 | // Build gridfield |
||
157 | $messageListConfig = GridFieldConfig::create()->addComponents( |
||
158 | new GridFieldSortableHeader(), |
||
159 | new GridFieldDataColumns(), |
||
160 | new GridFieldFooter() |
||
161 | ); |
||
162 | |||
163 | $messages = $this->Messages(); |
||
164 | if (is_string($messages)) { |
||
165 | // The api returned an error |
||
166 | $messagesList = new LiteralField("MessageAlert", $this->MessageHelper($messages, 'bad')); |
||
167 | } else { |
||
168 | $messagesList = GridField::create( |
||
169 | 'Messages', |
||
170 | false, |
||
171 | $messages, |
||
172 | $messageListConfig |
||
173 | )->addExtraClass("messages_grid"); |
||
174 | |||
175 | $columns = $messageListConfig->getComponentByType(GridFieldDataColumns::class); |
||
176 | $columns->setDisplayFields([ |
||
177 | 'event_id' => _t('MailgunAdmin.EventTransmissionId', 'Id'), |
||
178 | 'timestamp' => _t('MailgunAdmin.EventDate', 'Date'), |
||
179 | 'type' => _t('MailgunAdmin.EventType', 'Type'), |
||
180 | 'recipient' => _t('MailgunAdmin.EventRecipient', 'Recipient'), |
||
181 | 'subject' => _t('MailgunAdmin.EventSubject', 'Subject'), |
||
182 | 'sender' => _t('MailgunAdmin.EventSender', 'Sender'), |
||
183 | ]); |
||
184 | |||
185 | $columns->setFieldFormatting([ |
||
186 | 'timestamp' => function ($value, &$item) { |
||
187 | return date('Y-m-d H:i:s', $value); |
||
188 | }, |
||
189 | ]); |
||
190 | |||
191 | // Validator setup |
||
192 | $validator = null; |
||
193 | if ($record && method_exists($record, 'getValidator')) { |
||
194 | $validator = $record->getValidator(); |
||
195 | } |
||
196 | |||
197 | if ($validator) { |
||
198 | $messageListConfig |
||
199 | ->getComponentByType(GridFieldDetailForm::class) |
||
200 | ->setValidator($validator); |
||
201 | } |
||
202 | } |
||
203 | |||
204 | // Create tabs |
||
205 | $messagesTab = new Tab( |
||
206 | 'Messages', |
||
207 | _t('MailgunAdmin.Messages', 'Messages'), |
||
208 | $this->SearchFields(), |
||
209 | $messagesList, |
||
210 | // necessary for tree node selection in LeftAndMain.EditForm.js |
||
211 | new HiddenField('ID', false, 0) |
||
212 | ); |
||
213 | |||
214 | $fields = new FieldList([ |
||
215 | $root = new TabSet('Root', $messagesTab) |
||
216 | ]); |
||
217 | |||
218 | if ($this->CanConfigureApi()) { |
||
219 | $settingsTab = new Tab('Settings', _t('MailgunAdmin.Settings', 'Settings')); |
||
220 | |||
221 | $domainTabData = $this->DomainTab(); |
||
222 | $settingsTab->push($domainTabData); |
||
223 | |||
224 | $webhookTabData = $this->WebhookTab(); |
||
225 | $settingsTab->push($webhookTabData); |
||
226 | |||
227 | // Add a refresh button |
||
228 | $refreshButton = new LiteralField('RefreshButton', $this->ButtonHelper( |
||
229 | $this->Link() . '?refresh=true', |
||
230 | _t('MailgunAdmin.REFRESH', 'Force data refresh from the API') |
||
231 | )); |
||
232 | $settingsTab->push($refreshButton); |
||
233 | |||
234 | $fields->addFieldToTab('Root', $settingsTab); |
||
235 | } |
||
236 | |||
237 | // Tab nav in CMS is rendered through separate template |
||
238 | $root->setTemplate('SilverStripe\\Forms\\CMSTabSet'); |
||
239 | |||
240 | // Manage tabs state |
||
241 | $actionParam = $this->getRequest()->param('Action'); |
||
242 | if ($actionParam == 'setting') { |
||
243 | $settingsTab->addExtraClass('ui-state-active'); |
||
244 | } elseif ($actionParam == 'messages') { |
||
245 | $messagesTab->addExtraClass('ui-state-active'); |
||
246 | } |
||
247 | |||
248 | $actions = new FieldList(); |
||
249 | |||
250 | |||
251 | // Build replacement form |
||
252 | $form = Form::create( |
||
253 | $this, |
||
254 | 'EditForm', |
||
255 | $fields, |
||
256 | new FieldList() |
||
257 | )->setHTMLID('Form_EditForm'); |
||
258 | $form->addExtraClass('cms-edit-form fill-height'); |
||
259 | $form->setTemplate($this->getTemplatesWithSuffix('_EditForm')); |
||
260 | $form->addExtraClass('ss-tabset cms-tabset ' . $this->BaseCSSClasses()); |
||
261 | $form->setAttribute('data-pjax-fragment', 'CurrentForm'); |
||
262 | |||
263 | $this->extend('updateEditForm', $form); |
||
264 | |||
265 | return $form; |
||
266 | } |
||
267 | |||
268 | /** |
||
269 | * Get logger |
||
270 | * |
||
271 | * @return Psr\Log\LoggerInterface |
||
272 | */ |
||
273 | public function getLogger() |
||
274 | { |
||
275 | return $this->logger; |
||
276 | } |
||
277 | |||
278 | /** |
||
279 | * Get the cache |
||
280 | * |
||
281 | * @return Psr\SimpleCache\CacheInterface |
||
282 | */ |
||
283 | public function getCache() |
||
284 | { |
||
285 | return $this->cache; |
||
286 | } |
||
287 | |||
288 | /** |
||
289 | * @return boolean |
||
290 | */ |
||
291 | public function getCacheEnabled() |
||
292 | { |
||
293 | $v = $this->config()->cache_enabled; |
||
294 | if ($v === null) { |
||
295 | $v = self::$cache_enabled; |
||
296 | } |
||
297 | return $v; |
||
298 | } |
||
299 | |||
300 | /** |
||
301 | * A simple cache helper |
||
302 | * |
||
303 | * @param string $method Using dot notation like events.get |
||
304 | * @param array $params |
||
305 | * @param int $expireInSeconds |
||
306 | * @return array |
||
307 | */ |
||
308 | protected function getCachedData($method, $params, $expireInSeconds = 60) |
||
309 | { |
||
310 | $enabled = $this->getCacheEnabled(); |
||
311 | if ($enabled) { |
||
312 | $cache = $this->getCache(); |
||
313 | $key = md5($method . '-' . serialize($params)); |
||
314 | $cacheResult = $cache->get($key); |
||
315 | } |
||
316 | if ($enabled && $cacheResult) { |
||
317 | $data = unserialize($cacheResult); |
||
318 | } else { |
||
319 | try { |
||
320 | $client = MailgunHelper::getClient(); |
||
321 | |||
322 | if (!strpos($method, '.') !== false) { |
||
323 | throw new Exception("$method should use dot notation"); |
||
324 | } |
||
325 | |||
326 | // Split dot notation |
||
327 | $methodParts = explode('.', $method); |
||
328 | $service = $methodParts[0]; |
||
329 | $realMethod = $methodParts[1]; |
||
330 | |||
331 | if ($service == 'domains') { |
||
332 | if (!empty($params)) { |
||
333 | $data = $client->$service()->$realMethod($params[0]); |
||
334 | } else { |
||
335 | $data = $client->$service()->$realMethod(); |
||
336 | } |
||
337 | } else { |
||
338 | $data = $client->$service()->$realMethod(MailgunHelper::getDomain(), $params); |
||
339 | } |
||
340 | } catch (Exception $ex) { |
||
341 | $this->lastException = $ex; |
||
342 | $this->getLogger()->debug($ex); |
||
343 | $data = false; |
||
344 | } |
||
345 | |||
346 | //5 minutes cache |
||
347 | if ($enabled) { |
||
348 | $cache->set($key, serialize($data), $expireInSeconds); |
||
349 | } |
||
350 | } |
||
351 | |||
352 | return $data; |
||
353 | } |
||
354 | |||
355 | /** |
||
356 | * Values are mixed with default values and formatted for api usage |
||
357 | * |
||
358 | * @link https://documentation.mailgun.com/en/latest/api-events.html#query-options |
||
359 | * @return array |
||
360 | */ |
||
361 | public function getParams() |
||
362 | { |
||
363 | $params = $this->config()->default_search_params; |
||
364 | if (!$params) { |
||
365 | $params = []; |
||
366 | } |
||
367 | $data = $this->getSession()->get(__class__ . '.Search'); |
||
368 | if (!$data) { |
||
369 | $data = []; |
||
370 | } |
||
371 | |||
372 | $params = array_merge($params, $data); |
||
373 | |||
374 | // Respect api formats |
||
375 | if (!empty($params['begin'])) { |
||
376 | View Code Duplication | if (!is_int($params['begin'])) { |
|
0 ignored issues
–
show
|
|||
377 | $params['begin'] = strtotime(str_replace('/', '-', $params['begin'])); |
||
378 | } |
||
379 | // Need an end date, default to now |
||
380 | if (empty($params['end'])) { |
||
381 | $params['end'] = time(); |
||
382 | } |
||
383 | } |
||
384 | View Code Duplication | if (!empty($params['end'])) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
385 | if (!is_int($params['end'])) { |
||
386 | $params['end'] = strtotime(str_replace('/', '-', $params['end'])); |
||
387 | } |
||
388 | } |
||
389 | |||
390 | |||
391 | $params = array_filter($params); |
||
392 | |||
393 | return $params; |
||
394 | } |
||
395 | |||
396 | /** |
||
397 | * Get a raw value for a single param |
||
398 | * Useful for retrieving values as set by user |
||
399 | * |
||
400 | * @param string $name |
||
401 | * @param mixed $default |
||
402 | * @return mixed |
||
403 | */ |
||
404 | public function getParam($name, $default = null) |
||
405 | { |
||
406 | $data = $this->getSession()->get(__class__ . '.Search'); |
||
407 | if (!$data) { |
||
408 | return $default; |
||
409 | } |
||
410 | return (isset($data[$name]) && strlen($data[$name])) ? $data[$name] : $default; |
||
411 | } |
||
412 | |||
413 | public function SearchFields() |
||
414 | { |
||
415 | $disabled_filters = $this->config()->disabled_search_filters; |
||
416 | if (!$disabled_filters) { |
||
417 | $disabled_filters = []; |
||
418 | } |
||
419 | |||
420 | $fields = new CompositeField(); |
||
421 | $fields->push($from = new DateField('params[begin]', _t('MailgunAdmin.DATEFROM', 'From'), $this->getParam('begin'))); |
||
422 | // $from->setConfig('min', date('Y-m-d', strtotime('-10 days'))); |
||
423 | |||
424 | $fields->push(new DateField('params[end]', _t('MailgunAdmin.DATETO', 'To'), $to = $this->getParam('end'))); |
||
425 | |||
426 | View Code Duplication | if (!in_array('from', $disabled_filters)) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
427 | $fields->push($friendly_froms = new TextField('params[from]', _t('MailgunAdmin.FRIENDLYFROM', 'Sender'), $this->getParam('from'))); |
||
428 | $friendly_froms->setAttribute('placeholder', '[email protected],[email protected]'); |
||
429 | } |
||
430 | |||
431 | View Code Duplication | if (!in_array('to', $disabled_filters)) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
432 | $fields->push($recipients = new TextField('params[to]', _t('MailgunAdmin.RECIPIENTS', 'Recipients'), $this->getParam('to'))); |
||
433 | $recipients->setAttribute('placeholder', '[email protected],[email protected]'); |
||
434 | } |
||
435 | |||
436 | $fields->push(new DropdownField('params[limit]', _t('MailgunAdmin.PERPAGE', 'Number of results'), array( |
||
437 | 100 => 100, |
||
438 | 200 => 200, |
||
439 | 300 => 300, |
||
440 | ), $this->getParam('limit', 100))); |
||
441 | |||
442 | foreach ($fields->FieldList() as $field) { |
||
443 | $field->addExtraClass('no-change-track'); |
||
444 | } |
||
445 | |||
446 | // This is a ugly hack to allow embedding a form into another form |
||
447 | $doSearch = new FormAction('doSearch', _t('MailgunAdmin.DOSEARCH', 'Search')); |
||
448 | $doSearch->addExtraClass('btn-primary'); |
||
449 | $fields->push($doSearch); |
||
450 | $doSearch->setAttribute('onclick', "jQuery('#Form_SearchForm').append(jQuery('#Form_EditForm input,#Form_EditForm select').clone()).submit();"); |
||
451 | |||
452 | return $fields; |
||
453 | } |
||
454 | |||
455 | public function SearchForm() |
||
456 | { |
||
457 | $doSearch = new FormAction('doSearch'); |
||
458 | $SearchForm = new Form($this, 'SearchForm', new FieldList(), new FieldList( |
||
459 | [$doSearch] |
||
460 | )); |
||
461 | $SearchForm->setAttribute('style', 'display:none'); |
||
462 | return $SearchForm; |
||
463 | } |
||
464 | |||
465 | public function doSearch($data, Form $form) |
||
466 | { |
||
467 | $post = $this->getRequest()->postVar('params'); |
||
468 | if (!$post) { |
||
469 | return $this->redirectBack(); |
||
470 | } |
||
471 | $params = []; |
||
472 | |||
473 | $validFields = []; |
||
474 | foreach ($this->SearchFields()->FieldList()->dataFields() as $field) { |
||
475 | $validFields[] = str_replace(['params[', ']'], '', $field->getName()); |
||
476 | } |
||
477 | |||
478 | foreach ($post as $k => $v) { |
||
479 | if (in_array($k, $validFields)) { |
||
480 | $params[$k] = $v; |
||
481 | } |
||
482 | } |
||
483 | |||
484 | $this->getSession()->set(__class__ . '.Search', $params); |
||
485 | $this->getSession()->save($this->getRequest()); |
||
486 | |||
487 | return $this->redirectBack(); |
||
488 | } |
||
489 | |||
490 | /** |
||
491 | * List of messages events |
||
492 | * |
||
493 | * Messages are cached to avoid hammering the api |
||
494 | * |
||
495 | * @return ArrayList|string |
||
496 | */ |
||
497 | public function Messages() |
||
498 | { |
||
499 | $params = $this->getParams(); |
||
500 | |||
501 | /* @var $response EventResponse */ |
||
502 | $response = $this->getCachedData('events.get', $params, 60 * self::MESSAGE_CACHE_MINUTES); |
||
503 | |||
504 | $messages = []; |
||
505 | if ($response) { |
||
506 | $messages = $response->getItems(); |
||
507 | } |
||
508 | |||
509 | if (empty($messages)) { |
||
510 | if ($this->lastException) { |
||
511 | return $this->lastException->getMessage(); |
||
512 | } |
||
513 | return _t('MailgunAdmin.NO_MESSAGES', 'No messages'); |
||
514 | } |
||
515 | |||
516 | $list = new ArrayList(); |
||
517 | if ($messages) { |
||
518 | $mergedMessages = []; |
||
519 | $allHeaders = []; |
||
520 | |||
521 | // events may not contain full headers |
||
522 | foreach ($messages as $message) { |
||
523 | $realMessage = $message->getMessage(); |
||
524 | // "message-id" => "20191118104902.1.F9F052E7ED79D36A@sandbox7d650fc2614d4234be80987482afde91.mailgun.org" |
||
525 | View Code Duplication | if (!empty($realMessage['headers']['to'])) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
526 | $allHeaders[$realMessage['headers']['message-id']] = $realMessage['headers']; |
||
527 | } |
||
528 | } |
||
529 | |||
530 | foreach ($messages as $message) { |
||
531 | /* |
||
532 | "headers" => array:4 [â–¼ |
||
533 | "to" => ""Some Name" <[email protected]>" |
||
534 | "message-id" => "somekindofsandbox.mailgun.org" |
||
535 | "from" => "[email protected]" |
||
536 | "subject" => "Email subject here" |
||
537 | ] |
||
538 | "attachments" => [] |
||
539 | "size" => 110 |
||
540 | */ |
||
541 | $realMessage = $message->getMessage(); |
||
542 | View Code Duplication | if (empty($realMessage['headers']['to'])) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
543 | $headers = $allHeaders[$realMessage['headers']['message-id']]; |
||
544 | } else { |
||
545 | $headers = $realMessage['headers']; |
||
546 | } |
||
547 | |||
548 | $shortid = substr($realMessage['headers']['message-id'], 0, strpos($realMessage['headers']['message-id'], '@')); |
||
549 | $m = new ArrayData([ |
||
550 | 'event_id' => $shortid, |
||
551 | 'timestamp' => $message->getTimestamp(), |
||
552 | 'type' => $message->getEvent(), |
||
553 | 'recipient' => $headers['to'] ?? '', |
||
554 | 'subject' => $headers['subject'] ?? '', |
||
555 | 'sender' => $headers['from'] ?? '', |
||
556 | ]); |
||
557 | $list->push($m); |
||
558 | } |
||
559 | } |
||
560 | |||
561 | return $list; |
||
562 | } |
||
563 | |||
564 | /** |
||
565 | * Provides custom permissions to the Security section |
||
566 | * |
||
567 | * @return array |
||
568 | */ |
||
569 | public function providePermissions() |
||
570 | { |
||
571 | $title = _t("MailgunAdmin.MENUTITLE", LeftAndMain::menu_title_for_class('Mailgun')); |
||
572 | return [ |
||
573 | "CMS_ACCESS_MAILGUN" => [ |
||
574 | 'name' => _t('MailgunAdmin.ACCESS', "Access to '{title}' section", ['title' => $title]), |
||
575 | 'category' => _t('Permission.CMS_ACCESS_CATEGORY', 'CMS Access'), |
||
576 | 'help' => _t( |
||
577 | 'MailgunAdmin.ACCESS_HELP', |
||
578 | 'Allow use of Mailgun admin section' |
||
579 | ) |
||
580 | ], |
||
581 | ]; |
||
582 | } |
||
583 | |||
584 | /** |
||
585 | * Message helper |
||
586 | * |
||
587 | * @param string $message |
||
588 | * @param string $status |
||
589 | * @return string |
||
590 | */ |
||
591 | protected function MessageHelper($message, $status = 'info') |
||
592 | { |
||
593 | return '<div class="message ' . $status . '">' . $message . '</div>'; |
||
594 | } |
||
595 | |||
596 | /** |
||
597 | * Button helper |
||
598 | * |
||
599 | * @param string $link |
||
600 | * @param string $text |
||
601 | * @param boolean $confirm |
||
602 | * @return string |
||
603 | */ |
||
604 | protected function ButtonHelper($link, $text, $confirm = false) |
||
605 | { |
||
606 | $link = '<a class="btn btn-primary" href="' . $link . '"'; |
||
607 | if ($confirm) { |
||
608 | $link .= ' onclick="return confirm(\'' . _t('MailgunAdmin.CONFIRM_MSG', 'Are you sure?') . '\')"'; |
||
609 | } |
||
610 | $link .= '>' . $text . '</a>'; |
||
611 | return $link; |
||
612 | } |
||
613 | |||
614 | /** |
||
615 | * A template accessor to check the ADMIN permission |
||
616 | * |
||
617 | * @return bool |
||
618 | */ |
||
619 | public function IsAdmin() |
||
620 | { |
||
621 | return Permission::check("ADMIN"); |
||
622 | } |
||
623 | |||
624 | /** |
||
625 | * Check the permission for current user |
||
626 | * |
||
627 | * @return bool |
||
628 | */ |
||
629 | public function canView($member = null) |
||
630 | { |
||
631 | $mailer = MailgunHelper::getMailer(); |
||
632 | // Another custom mailer has been set |
||
633 | if (!$mailer instanceof SwiftMailer) { |
||
634 | return false; |
||
635 | } |
||
636 | // Doesn't use the proper transport |
||
637 | if (!$mailer->getSwiftMailer()->getTransport() instanceof MailgunSwiftTransport) { |
||
638 | return false; |
||
639 | } |
||
640 | return Permission::check("CMS_ACCESS_MAILGUN", 'any', $member); |
||
641 | } |
||
642 | |||
643 | /** |
||
644 | * |
||
645 | * @return bool |
||
646 | */ |
||
647 | public function CanConfigureApi() |
||
648 | { |
||
649 | return Permission::check('ADMIN') || Director::isDev(); |
||
650 | } |
||
651 | |||
652 | /** |
||
653 | * Check if webhook is installed |
||
654 | * |
||
655 | * @return array |
||
656 | */ |
||
657 | public function WebhookInstalled() |
||
658 | { |
||
659 | /* @var $response WebhookIndexResponse */ |
||
660 | $response = $this->getCachedData('webhooks.index', null, 60 * self::WEBHOOK_CACHE_MINUTES); |
||
661 | if (!$response) { |
||
662 | return false; |
||
663 | } |
||
664 | $url = $this->WebhookUrl(); |
||
665 | $dom = $this->getDomain(); |
||
666 | if (strpos($response->getBounceUrl(), $dom)) { |
||
667 | return true; |
||
668 | } |
||
669 | if (strpos($response->getDeliverUrl(), $dom)) { |
||
670 | return true; |
||
671 | } |
||
672 | if (strpos($response->getDropUrl(), $dom)) { |
||
673 | return true; |
||
674 | } |
||
675 | if (strpos($response->getSpamUrl(), $dom)) { |
||
676 | return true; |
||
677 | } |
||
678 | if (strpos($response->getUnsubscribeUrl(), $dom)) { |
||
679 | return true; |
||
680 | } |
||
681 | if (strpos($response->getClickUrl(), $dom)) { |
||
682 | return true; |
||
683 | } |
||
684 | if (strpos($response->getOpenUrl(), $dom)) { |
||
685 | return true; |
||
686 | } |
||
687 | return false; |
||
688 | } |
||
689 | |||
690 | /** |
||
691 | * Hook details for template |
||
692 | * @return \ArrayData |
||
693 | */ |
||
694 | public function WebhookDetails() |
||
695 | { |
||
696 | $el = $this->WebhookInstalled(); |
||
697 | if ($el) { |
||
698 | return new ArrayData($el); |
||
699 | } |
||
700 | } |
||
701 | |||
702 | /** |
||
703 | * Get content of the tab |
||
704 | * |
||
705 | * @return FormField |
||
706 | */ |
||
707 | public function WebhookTab() |
||
708 | { |
||
709 | if ($this->WebhookInstalled()) { |
||
710 | return $this->UninstallHookForm(); |
||
711 | } |
||
712 | return $this->InstallHookForm(); |
||
713 | } |
||
714 | |||
715 | /** |
||
716 | * @return string |
||
717 | */ |
||
718 | public function WebhookUrl() |
||
719 | { |
||
720 | if (self::config()->webhook_base_url) { |
||
721 | return rtrim(self::config()->webhook_base_url, '/') . '/__mailgun/incoming'; |
||
722 | } |
||
723 | if (Director::isLive()) { |
||
724 | return Director::absoluteURL('/__mailgun/incoming'); |
||
725 | } |
||
726 | $protocol = Director::protocol(); |
||
727 | return $protocol . $this->getDomain() . '/__mailgun/incoming'; |
||
728 | } |
||
729 | |||
730 | /** |
||
731 | * Install hook form |
||
732 | * |
||
733 | * @return FormField |
||
734 | */ |
||
735 | View Code Duplication | public function InstallHookForm() |
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
736 | { |
||
737 | $fields = new CompositeField(); |
||
738 | $fields->push(new LiteralField('Info', $this->MessageHelper( |
||
739 | _t('MailgunAdmin.WebhookNotInstalled', 'Webhook is not installed. It should be configured using the following url {url}. This url must be publicly visible to be used as a hook.', ['url' => $this->WebhookUrl()]), |
||
740 | 'bad' |
||
741 | ))); |
||
742 | $fields->push(new LiteralField('doInstallHook', $this->ButtonHelper( |
||
743 | $this->Link('doInstallHook'), |
||
744 | _t('MailgunAdmin.DOINSTALL_WEBHOOK', 'Install webhook') |
||
745 | ))); |
||
746 | return $fields; |
||
747 | } |
||
748 | |||
749 | public function doInstallHook() |
||
750 | { |
||
751 | if (!$this->CanConfigureApi()) { |
||
752 | return $this->redirectBack(); |
||
753 | } |
||
754 | |||
755 | $client = MailgunHelper::getClient(); |
||
756 | |||
757 | $url = $this->WebhookUrl(); |
||
758 | $description = SiteConfig::current_site_config()->Title; |
||
759 | |||
760 | try { |
||
761 | $types = self::config()->webhook_events; |
||
762 | if (!empty($types)) { |
||
763 | foreach ($types as $type) { |
||
764 | $client->webhooks()->create(MailgunHelper::getDomain(), $type, $url . '?type=' . $type); |
||
765 | } |
||
766 | } |
||
767 | $this->getCache()->clear(); |
||
768 | } catch (Exception $ex) { |
||
769 | $this->getLogger()->debug($ex); |
||
770 | } |
||
771 | |||
772 | return $this->redirectBack(); |
||
773 | } |
||
774 | |||
775 | /** |
||
776 | * Uninstall hook form |
||
777 | * |
||
778 | * @return FormField |
||
779 | */ |
||
780 | View Code Duplication | public function UninstallHookForm() |
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
781 | { |
||
782 | $fields = new CompositeField(); |
||
783 | $fields->push(new LiteralField('Info', $this->MessageHelper( |
||
784 | _t('MailgunAdmin.WebhookInstalled', 'Webhook is installed and accessible at the following url {url}.', ['url' => $this->WebhookUrl()]), |
||
785 | 'good' |
||
786 | ))); |
||
787 | $fields->push(new LiteralField('doUninstallHook', $this->ButtonHelper( |
||
788 | $this->Link('doUninstallHook'), |
||
789 | _t('MailgunAdmin.DOUNINSTALL_WEBHOOK', 'Uninstall webhook'), |
||
790 | true |
||
791 | ))); |
||
792 | return $fields; |
||
793 | } |
||
794 | |||
795 | public function doUninstallHook($data, Form $form) |
||
796 | { |
||
797 | if (!$this->CanConfigureApi()) { |
||
798 | return $this->redirectBack(); |
||
799 | } |
||
800 | |||
801 | $client = MailgunHelper::getClient(); |
||
802 | |||
803 | try { |
||
804 | $response = $client->webhooks()->index(MailgunHelper::getDomain()); |
||
805 | if ($response) { |
||
806 | if ($response->getBounceUrl()) { |
||
807 | $client->webhooks()->delete(MailgunHelper::getDomain(), 'bounce'); |
||
808 | } |
||
809 | if ($response->getDeliverUrl()) { |
||
810 | $client->webhooks()->delete(MailgunHelper::getDomain(), 'deliver'); |
||
811 | } |
||
812 | if ($response->getDropUrl()) { |
||
813 | $client->webhooks()->delete(MailgunHelper::getDomain(), 'drop'); |
||
814 | } |
||
815 | if ($response->getSpamUrl()) { |
||
816 | $client->webhooks()->delete(MailgunHelper::getDomain(), 'spam'); |
||
817 | } |
||
818 | if ($response->getUnsubscribeUrl()) { |
||
819 | $client->webhooks()->delete(MailgunHelper::getDomain(), 'unsubscribe'); |
||
820 | } |
||
821 | if ($response->getClickUrl()) { |
||
822 | $client->webhooks()->delete(MailgunHelper::getDomain(), 'clicked'); |
||
823 | } |
||
824 | if ($response->getOpenUrl()) { |
||
825 | $client->webhooks()->delete(MailgunHelper::getDomain(), 'open'); |
||
826 | } |
||
827 | } |
||
828 | $this->getCache()->clear(); |
||
829 | } catch (Exception $ex) { |
||
830 | $this->getLogger()->debug($ex); |
||
831 | } |
||
832 | |||
833 | return $this->redirectBack(); |
||
834 | } |
||
835 | |||
836 | /** |
||
837 | * Check if sending domain is installed |
||
838 | * |
||
839 | * @return array |
||
840 | */ |
||
841 | public function SendingDomainInstalled() |
||
842 | { |
||
843 | $client = MailgunHelper::getClient(); |
||
844 | |||
845 | /* @var $response DomainIndexResponse */ |
||
846 | $response = $this->getCachedData('domains.index', $this->getDomain(), 60 * self::SENDINGDOMAIN_CACHE_MINUTES); |
||
847 | |||
848 | $domains = $response->getDomains(); |
||
849 | $defaultDomain = $this->getDomain(); |
||
850 | |||
851 | foreach ($domains as $domain) { |
||
852 | if ($domain->getName() == $defaultDomain) { |
||
853 | return true; |
||
854 | } |
||
855 | } |
||
856 | return false; |
||
857 | } |
||
858 | |||
859 | /** |
||
860 | * Trigger request to check if sending domain is verified |
||
861 | * |
||
862 | * @return array |
||
863 | */ |
||
864 | public function VerifySendingDomain() |
||
865 | { |
||
866 | $client = MailgunHelper::getClient(); |
||
867 | |||
868 | $host = $this->getDomain(); |
||
869 | |||
870 | $verification = $client->verifySendingDomain($host); |
||
871 | |||
872 | if (empty($verification)) { |
||
873 | return false; |
||
874 | } |
||
875 | return $verification; |
||
876 | } |
||
877 | |||
878 | /** |
||
879 | * Get content of the tab |
||
880 | * |
||
881 | * @return FormField |
||
882 | */ |
||
883 | public function DomainTab() |
||
884 | { |
||
885 | $defaultDomain = $this->getDomain(); |
||
886 | $defaultDomainInfos = null; |
||
887 | |||
888 | /* @var $response DomainIndexResponse */ |
||
889 | $response = $this->getCachedData('domains.index', null, 60 * self::SENDINGDOMAIN_CACHE_MINUTES); |
||
890 | $domains = []; |
||
891 | if ($response) { |
||
892 | $domains = $response->getDomains(); |
||
893 | } |
||
894 | |||
895 | /* |
||
896 | 0 => Domain {#2919 â–¼ |
||
897 | -createdAt: DateTimeImmutable @1573661800 {#2921 â–¶} |
||
898 | -smtpLogin: "[email protected]" |
||
899 | -name: "sandbox.mailgun.org" |
||
900 | -smtpPassword: "some-pass-word" |
||
901 | -wildcard: false |
||
902 | -spamAction: "disabled" |
||
903 | -state: "active" |
||
904 | } |
||
905 | */ |
||
906 | |||
907 | $fields = new CompositeField(); |
||
908 | |||
909 | $list = new ArrayList(); |
||
910 | if ($domains) { |
||
911 | foreach ($domains as $domain) { |
||
912 | $showResponse = $this->getCachedData('domains.show', [$domain->getName()], 60 * self::SENDINGDOMAIN_CACHE_MINUTES); |
||
913 | |||
914 | /* |
||
915 | "sending_dns_records": [ |
||
916 | { |
||
917 | "record_type": "TXT", |
||
918 | "valid": "valid", |
||
919 | "name": "domain.com", |
||
920 | "value": "v=spf1 include:mailgun.org ~all" |
||
921 | }, |
||
922 | { |
||
923 | "record_type": "TXT", |
||
924 | "valid": "valid", |
||
925 | "name": "domain.com", |
||
926 | "value": "k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUA...." |
||
927 | }, |
||
928 | { |
||
929 | "record_type": "CNAME", |
||
930 | "valid": "valid", |
||
931 | "name": "email.domain.com", |
||
932 | "value": "mailgun.org" |
||
933 | } |
||
934 | ] |
||
935 | */ |
||
936 | |||
937 | $dnsRecords = $showResponse->getOutboundDNSRecords(); |
||
938 | |||
939 | $spfOk = false; |
||
940 | $dkimOk = false; |
||
941 | |||
942 | foreach ($dnsRecords as $dnsRecord) { |
||
943 | $value = $dnsRecord->getValue(); |
||
944 | if (strpos($value, 'v=spf1') !== false) { |
||
945 | $spfOk = $dnsRecord->isValid(); |
||
946 | } |
||
947 | if (strpos($value, 'k=rsa') !== false) { |
||
948 | $dkimOk = $dnsRecord->isValid(); |
||
949 | } |
||
950 | } |
||
951 | |||
952 | $list->push(new ArrayData([ |
||
953 | 'Domain' => $domain->getName(), |
||
954 | 'SPF' => $spfOk, |
||
955 | 'DKIM' => $dkimOk, |
||
956 | 'Verified' => ($domain->getState() == 'active') ? true : false, |
||
957 | ])); |
||
958 | |||
959 | if ($domain->getName() == $defaultDomain) { |
||
960 | $defaultDomainInfos = $domain; |
||
961 | } |
||
962 | } |
||
963 | } |
||
964 | |||
965 | $config = GridFieldConfig::create(); |
||
966 | $config->addComponent(new GridFieldToolbarHeader()); |
||
967 | $config->addComponent(new GridFieldTitleHeader()); |
||
968 | $config->addComponent($columns = new GridFieldDataColumns()); |
||
969 | $columns->setDisplayFields(ArrayLib::valuekey(['Domain', 'SPF', 'DKIM', 'Verified'])); |
||
970 | $domainsList = new GridField('SendingDomains', _t('MailgunAdmin.ALL_SENDING_DOMAINS', 'Configured sending domains'), $list, $config); |
||
971 | $domainsList->addExtraClass('mb-2'); |
||
972 | $fields->push($domainsList); |
||
973 | |||
974 | if (!$defaultDomainInfos) { |
||
975 | $this->InstallDomainForm($fields); |
||
976 | } else { |
||
977 | $this->UninstallDomainForm($fields); |
||
978 | } |
||
979 | |||
980 | return $fields; |
||
981 | } |
||
982 | |||
983 | /** |
||
984 | * @return string |
||
985 | */ |
||
986 | public function InboundUrl() |
||
987 | { |
||
988 | $subdomain = self::config()->inbound_subdomain; |
||
989 | $domain = $this->getDomain(); |
||
990 | if ($domain) { |
||
991 | return $subdomain . '.' . $domain; |
||
992 | } |
||
993 | return false; |
||
994 | } |
||
995 | |||
996 | /** |
||
997 | * Get domain name from current host |
||
998 | * |
||
999 | * @return boolean|string |
||
1000 | */ |
||
1001 | public function getDomainFromHost() |
||
1002 | { |
||
1003 | $base = Environment::getEnv('SS_BASE_URL'); |
||
1004 | if (!$base) { |
||
1005 | $base = Director::protocolAndHost(); |
||
1006 | } |
||
1007 | $host = parse_url($base, PHP_URL_HOST); |
||
1008 | $hostParts = explode('.', $host); |
||
1009 | $parts = count($hostParts); |
||
1010 | if ($parts < 2) { |
||
1011 | return false; |
||
1012 | } |
||
1013 | $domain = $hostParts[$parts - 2] . "." . $hostParts[$parts - 1]; |
||
1014 | return $domain; |
||
1015 | } |
||
1016 | |||
1017 | /** |
||
1018 | * Get domain from admin email |
||
1019 | * |
||
1020 | * @return boolean|string |
||
1021 | */ |
||
1022 | public function getDomainFromEmail() |
||
1023 | { |
||
1024 | $email = MailgunHelper::resolveDefaultFromEmail(null, false); |
||
1025 | if ($email) { |
||
1026 | $domain = substr(strrchr($email, "@"), 1); |
||
1027 | return $domain; |
||
1028 | } |
||
1029 | return false; |
||
1030 | } |
||
1031 | |||
1032 | /** |
||
1033 | * Get domain |
||
1034 | * |
||
1035 | * @return boolean|string |
||
1036 | */ |
||
1037 | public function getDomain() |
||
1038 | { |
||
1039 | $domain = $this->getDomainFromEmail(); |
||
1040 | if (!$domain) { |
||
1041 | return $this->getDomainFromHost(); |
||
1042 | } |
||
1043 | return $domain; |
||
1044 | } |
||
1045 | |||
1046 | /** |
||
1047 | * Install domain form |
||
1048 | * |
||
1049 | * @param CompositeField $fieldsd |
||
1050 | * @return FormField |
||
1051 | */ |
||
1052 | View Code Duplication | public function InstallDomainForm(CompositeField $fields) |
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
1053 | { |
||
1054 | $host = $this->getDomain(); |
||
1055 | |||
1056 | $fields->push(new LiteralField('Info', $this->MessageHelper( |
||
1057 | _t('MailgunAdmin.DomainNotInstalled', 'Default sending domain {domain} is not installed.', ['domain' => $host]), |
||
1058 | "bad" |
||
1059 | ))); |
||
1060 | $fields->push(new LiteralField('doInstallDomain', $this->ButtonHelper( |
||
1061 | $this->Link('doInstallDomain'), |
||
1062 | _t('MailgunAdmin.DOINSTALLDOMAIN', 'Install domain') |
||
1063 | ))); |
||
1064 | } |
||
1065 | |||
1066 | View Code Duplication | public function doInstallDomain() |
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
1067 | { |
||
1068 | if (!$this->CanConfigureApi()) { |
||
1069 | return $this->redirectBack(); |
||
1070 | } |
||
1071 | |||
1072 | $client = MailgunHelper::getClient(); |
||
1073 | |||
1074 | $domain = $this->getDomain(); |
||
1075 | |||
1076 | if (!$domain) { |
||
1077 | return $this->redirectBack(); |
||
1078 | } |
||
1079 | |||
1080 | try { |
||
1081 | $client->domains()->create($domain); |
||
1082 | $this->getCache()->clear(); |
||
1083 | } catch (Exception $ex) { |
||
1084 | $this->getLogger()->debug($ex); |
||
1085 | } |
||
1086 | |||
1087 | return $this->redirectBack(); |
||
1088 | } |
||
1089 | |||
1090 | /** |
||
1091 | * Uninstall domain form |
||
1092 | * |
||
1093 | * @param CompositeField $fieldsd |
||
1094 | * @return FormField |
||
1095 | */ |
||
1096 | public function UninstallDomainForm(CompositeField $fields) |
||
1097 | { |
||
1098 | $domainInfos = $this->SendingDomainInstalled(); |
||
1099 | |||
1100 | $domain = $this->getDomain(); |
||
1101 | |||
1102 | if ($domainInfos && $domainInfos->getState() == 'active') { |
||
1103 | $fields->push(new LiteralField('Info', $this->MessageHelper( |
||
1104 | _t('MailgunAdmin.DomainInstalled', 'Default domain {domain} is installed.', ['domain' => $domain]), |
||
1105 | 'good' |
||
1106 | ))); |
||
1107 | } else { |
||
1108 | $fields->push(new LiteralField('Info', $this->MessageHelper( |
||
1109 | _t('MailgunAdmin.DomainInstalledBut', 'Default domain {domain} is installed, but is not properly configured.'), |
||
1110 | 'warning' |
||
1111 | ))); |
||
1112 | } |
||
1113 | $fields->push(new LiteralField('doUninstallHook', $this->ButtonHelper( |
||
1114 | $this->Link('doUninstallHook'), |
||
1115 | _t('MailgunAdmin.DOUNINSTALLDOMAIN', 'Uninstall domain'), |
||
1116 | true |
||
1117 | ))); |
||
1118 | } |
||
1119 | |||
1120 | View Code Duplication | public function doUninstallDomain($data, Form $form) |
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
1121 | { |
||
1122 | if (!$this->CanConfigureApi()) { |
||
1123 | return $this->redirectBack(); |
||
1124 | } |
||
1125 | |||
1126 | $client = MailgunHelper::getClient(); |
||
1127 | |||
1128 | $domain = $this->getDomain(); |
||
1129 | |||
1130 | if (!$domain) { |
||
1131 | return $this->redirectBack(); |
||
1132 | } |
||
1133 | |||
1134 | try { |
||
1135 | $el = $this->SendingDomainInstalled(); |
||
1136 | if ($el) { |
||
1137 | $client->domains()->delete($domain); |
||
1138 | } |
||
1139 | $this->getCache()->clear(); |
||
1140 | } catch (Exception $ex) { |
||
1141 | $this->getLogger()->debug($ex); |
||
1142 | } |
||
1143 | |||
1144 | return $this->redirectBack(); |
||
1145 | } |
||
1146 | } |
||
1147 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.