Issues (54)

src/MandrillAdmin.php (1 issue)

1
<?php
2
3
namespace LeKoala\Mandrill;
4
5
use Exception;
6
use Psr\Log\LoggerInterface;
7
use Psr\SimpleCache\CacheInterface;
8
use Psr\SimpleCache\InvalidArgumentException;
9
use SilverStripe\Admin\LeftAndMain;
10
use SilverStripe\Control\Director;
11
use SilverStripe\Control\Email\Email;
12
use SilverStripe\Control\Email\SwiftMailer;
13
use SilverStripe\Control\HTTPResponse;
14
use SilverStripe\Control\Session;
15
use SilverStripe\Forms\CompositeField;
16
use SilverStripe\Forms\DateField;
17
use SilverStripe\Forms\DropdownField;
18
use SilverStripe\Forms\FieldList;
19
use SilverStripe\Forms\Form;
20
use SilverStripe\Forms\FormAction;
21
use SilverStripe\Forms\FormField;
22
use SilverStripe\Forms\GridField\GridField;
23
use SilverStripe\Forms\GridField\GridFieldConfig;
24
use SilverStripe\Forms\GridField\GridFieldDataColumns;
25
use SilverStripe\Forms\GridField\GridFieldDetailForm;
26
use SilverStripe\Forms\GridField\GridFieldFooter;
27
use SilverStripe\Forms\GridField\GridFieldSortableHeader;
28
use SilverStripe\Forms\GridField\GridFieldToolbarHeader;
29
use SilverStripe\Forms\HiddenField;
30
use SilverStripe\Forms\LiteralField;
31
use SilverStripe\Forms\Tab;
32
use SilverStripe\Forms\TabSet;
33
use SilverStripe\Forms\TextField;
34
use SilverStripe\ORM\ArrayLib;
35
use SilverStripe\ORM\ArrayList;
36
use SilverStripe\Security\DefaultAdminService;
37
use SilverStripe\Security\Permission;
38
use SilverStripe\Security\PermissionProvider;
39
use SilverStripe\Security\Security;
40
use SilverStripe\SiteConfig\SiteConfig;
41
use SilverStripe\View\ArrayData;
42
use Symbiote\GridFieldExtensions\GridFieldTitleHeader;
43
use Symfony\Component\Mailer\MailerInterface;
44
use Symfony\Component\Mailer\Transport\AbstractApiTransport;
45
46
/**
47
 * Mandrill admin section
48
 *
49
 * Allow you to see messages sent through the api key used to send messages
50
 *
51
 * @package Mandrill
52
 * @author LeKoala <[email protected]>
53
 */
54
class MandrillAdmin extends LeftAndMain implements PermissionProvider
55
{
56
57
    const MESSAGE_CACHE_MINUTES = 5;
58
    const WEBHOOK_CACHE_MINUTES = 1440; // 1 day
59
    const SENDINGDOMAIN_CACHE_MINUTES = 1440; // 1 day
60
61
    private static $menu_title = "Mandrill";
62
    private static $url_segment = "mandrill";
63
    private static $menu_icon = "lekoala/silverstripe-mandrill:images/icon.png";
64
    private static $url_rule = '/$Action/$ID/$OtherID';
65
    private static $allowed_actions = array(
66
        "settings",
67
        "ListForm",
68
        "SearchForm",
69
        "doSearch",
70
        "InstallHookForm",
71
        "doInstallHook",
72
        "UninstallHookForm",
73
        "doUninstallHook",
74
        "send_test",
75
    );
76
    private static $cache_enabled = true;
77
78
    /**
79
     * @var Exception
80
     */
81
    protected $lastException;
82
83
    /**
84
     * Inject public dependencies into the controller
85
     *
86
     * @var array
87
     */
88
    private static $dependencies = [
89
        'logger' => '%$Psr\Log\LoggerInterface',
90
        'cache' => '%$Psr\SimpleCache\CacheInterface.mandrill', // see _config/cache.yml
91
    ];
92
93
    /**
94
     * @var LoggerInterface
95
     */
96
    public $logger;
97
98
    /**
99
     * @var CacheInterface
100
     */
101
    public $cache;
102
103
    public function init()
104
    {
105
        parent::init();
106
107
        if (isset($_GET['refresh'])) {
108
            $this->getCache()->clear();
109
        }
110
    }
111
112
    public function settings($request)
113
    {
114
        return parent::index($request);
115
    }
116
117
    public function send_test($request)
118
    {
119
        if (!$this->CanConfigureApi()) {
120
            return $this->httpError(404);
121
        }
122
        $service = DefaultAdminService::create();
123
        $to = $request->getVar('to');
124
        if (!$to) {
125
            $to = $service->findOrCreateDefaultAdmin()->Email;
126
        }
127
        $email = Email::create();
128
        $email->setSubject("Test email");
129
        $email->setBody("Test " . date('Y-m-d H:i:s'));
130
        $email->setTo($to);
131
132
        $result = $email->send();
133
        var_dump($result);
134
    }
135
136
    /**
137
     * @return Session
138
     */
139
    public function getSession()
140
    {
141
        return $this->getRequest()->getSession();
142
    }
143
144
    /**
145
     * Returns a GridField of messages
146
     *
147
     * @param int $id
148
     * @param FieldList $fields
149
     * @return Form
150
     * @throws InvalidArgumentException
151
     */
152
    public function getEditForm($id = null, $fields = null)
153
    {
154
        if (!$id) {
155
            $id = $this->currentPageID();
156
        }
157
158
        $record = $this->getRecord($id);
159
160
        // Check if this record is viewable
161
        if ($record && !$record->canView()) {
162
            $response = Security::permissionFailure($this);
163
            $this->setResponse($response);
164
            return null;
165
        }
166
167
        // Build gridfield
168
        $messageListConfig = GridFieldConfig::create()->addComponents(
169
            new GridFieldSortableHeader(),
170
            new GridFieldDataColumns(),
171
            new GridFieldFooter()
172
        );
173
174
        $messages = $this->Messages();
175
        if (is_string($messages)) {
176
            // The api returned an error
177
            $messagesList = new LiteralField("MessageAlert", $this->MessageHelper($messages, 'bad'));
178
        } else {
179
            $messagesList = GridField::create(
180
                'Messages',
181
                false,
182
                $messages,
183
                $messageListConfig
184
            )->addExtraClass("messages_grid");
185
186
            /** @var GridFieldDataColumns $columns */
187
            $columns = $messageListConfig->getComponentByType(GridFieldDataColumns::class);
188
            $columns->setDisplayFields(array(
189
                'date' => _t('MandrillAdmin.MessageDate', 'Date'),
190
                'state' => _t('MandrillAdmin.MessageStatus', 'Status'),
191
                'sender' => _t('MandrillAdmin.MessageSender', 'Sender'),
192
                'email' => _t('MandrillAdmin.MessageEmail', 'Email'),
193
                'subject' => _t('MandrillAdmin.MessageSubject', 'Subject'),
194
                'opens' => _t('MandrillAdmin.MessageOpens', 'Opens'),
195
                'clicks' => _t('MandrillAdmin.MessageClicks', 'Clicks'),
196
            ));
197
            $columns->setFieldFormatting(array(
198
                'state' => function ($value, &$item) {
199
                    switch ($value) {
200
                        case 'sent':
201
                            $color = 'green';
202
                            break;
203
                        default:
204
                            $color = '#333';
205
                            break;
206
                    }
207
                    return sprintf('<span style="color:%s">%s</span>', $color, $value);
208
                }
209
            ));
210
211
            // Validator setup
212
            $validator = null;
213
            if ($record && method_exists($record, 'getValidator')) {
214
                $validator = $record->getValidator();
215
            }
216
217
            if ($validator) {
218
                /** @var GridFieldDetailForm $detailForm */
219
                $detailForm = $messageListConfig->getComponentByType(GridFieldDetailForm::class);
220
                $detailForm->setValidator($validator);
221
            }
222
        }
223
224
        // Create tabs
225
        $messagesTab = new Tab(
226
            'Messages',
227
            _t('MandrillAdmin.Messages', 'Messages'),
228
            $this->SearchFields(),
229
            $messagesList,
230
            // necessary for tree node selection in LeftAndMain.EditForm.js
231
            new HiddenField('ID', false, 0)
232
        );
233
234
        $fields = new FieldList(
235
            $root = new TabSet('Root', $messagesTab)
236
        );
237
238
        if ($this->CanConfigureApi()) {
239
            $settingsTab = new Tab('Settings', _t('MandrillAdmin.Settings', 'Settings'));
240
241
            $domainTabData = $this->DomainTab();
242
            $settingsTab->push($domainTabData);
243
244
            $webhookTabData = $this->WebhookTab();
245
            $settingsTab->push($webhookTabData);
246
247
            // Add a refresh button
248
            $refreshButton = new LiteralField('RefreshButton', $this->ButtonHelper(
249
                $this->Link() . '?refresh=true',
250
                _t('MandrillAdmin.REFRESH', 'Force data refresh from the API')
251
            ));
252
            $settingsTab->push($refreshButton);
253
254
            $fields->addFieldToTab('Root', $settingsTab);
255
        }
256
257
        // Tab nav in CMS is rendered through separate template
258
        $root->setTemplate('SilverStripe\\Forms\\CMSTabSet');
259
260
        // Manage tabs state
261
        $actionParam = $this->getRequest()->param('Action');
262
        if ($actionParam == 'setting') {
263
            $settingsTab->addExtraClass('ui-state-active');
264
        } elseif ($actionParam == 'messages') {
265
            $messagesTab->addExtraClass('ui-state-active');
266
        }
267
268
        // Build replacement form
269
        $form = Form::create(
270
            $this,
271
            'EditForm',
272
            $fields,
273
            new FieldList()
274
        )->setHTMLID('Form_EditForm');
275
        $form->addExtraClass('cms-edit-form fill-height');
276
        $form->setTemplate($this->getTemplatesWithSuffix('_EditForm'));
277
        $form->addExtraClass('ss-tabset cms-tabset ' . $this->BaseCSSClasses());
278
        $form->setAttribute('data-pjax-fragment', 'CurrentForm');
279
280
        $this->extend('updateEditForm', $form);
281
282
        return $form;
283
    }
284
285
    /**
286
     * Get logger
287
     *
288
     * @return  LoggerInterface
289
     */
290
    public function getLogger()
291
    {
292
        return $this->logger;
293
    }
294
295
    /**
296
     * Get the cache
297
     *
298
     * @return CacheInterface
299
     */
300
    public function getCache()
301
    {
302
        return $this->cache;
303
    }
304
305
    /**
306
     * @return boolean
307
     */
308
    public function getCacheEnabled()
309
    {
310
        if (isset($_GET['disable_cache'])) {
311
            return false;
312
        }
313
        $v = $this->config()->cache_enabled;
314
        if ($v === null) {
315
            $v = self::$cache_enabled;
316
        }
317
        return $v;
318
    }
319
320
    /**
321
     * A simple cache helper
322
     *
323
     * @param string $method
324
     * @param array $params Params must be set in the right order!
325
     * @param int $expireInSeconds
326
     * @return array
327
     * @throws InvalidArgumentException
328
     */
329
    protected function getCachedData($method, $params, $expireInSeconds = 60)
330
    {
331
        $enabled = $this->getCacheEnabled();
332
        if ($enabled) {
333
            $cache = $this->getCache();
334
            $key = $method . md5(serialize($params));
335
            $cacheResult = $cache->get($key);
336
        }
337
        if ($enabled && $cacheResult) {
338
            $data = unserialize($cacheResult);
339
        } else {
340
            try {
341
                $client = MandrillHelper::getClient();
342
                $parts = explode('.', $method);
343
                $service = $parts[0];
344
                $func = $parts[1];
345
                $data = call_user_func_array([$client->$service, $func], $params);
346
            } catch (Exception $ex) {
347
                $this->lastException = $ex;
348
                $this->getLogger()->debug($ex);
349
                $data = false;
350
                $enabled = false;
351
            }
352
353
            //5 minutes cache
354
            if ($enabled) {
355
                $cache->set($key, serialize($data), $expireInSeconds);
356
            }
357
        }
358
359
        return $data;
360
    }
361
362
    public function getParams()
363
    {
364
        $params = $this->config()->default_search_params;
365
        if (!$params) {
366
            $params = [];
367
        }
368
        $data = $this->getSession()->get(__class__ . '.Search');
369
        if (!$data) {
370
            $data = [];
371
        }
372
373
        $params = array_merge($params, $data);
374
375
        // Remove empty params
376
        $params = array_filter($params);
377
378
        return $params;
379
    }
380
381
    public function getParam($name, $default = null)
382
    {
383
        $data = $this->getSession()->get(__class__ . '.Search');
384
        if (!$data) {
385
            return $default;
386
        }
387
        return (isset($data[$name]) && strlen($data[$name])) ? $data[$name] : $default;
388
    }
389
390
    public function SearchFields()
391
    {
392
        $fields = new CompositeField();
393
        $fields->push($from = new DateField('params[date_from]', _t('MandrillAdmin.DATEFROM', 'From'), $this->getParam('date_from', date('Y-m-d', strtotime('-30 days')))));
394
        $fields->push($to = new DateField('params[date_to]', _t('MandrillAdmin.DATETO', 'To'), $to = $this->getParam('date_to')));
395
        $fields->push($queryField = new TextField('params[query]', _t('Mandrill.QUERY', 'Query'), $this->getParam('query')));
396
        $queryField->setAttribute('placeholder', 'full_email:joe@domain.* AND sender:[email protected] OR subject:welcome');
397
        $queryField->setDescription(_t('Mandrill.QUERYDESC', 'For more information about query syntax, please visit <a target="_blank" href="https://mailchimp.com/developer/transactional/docs/activity-reports/#search-outbound-activity">Mandrill Support</a>'));
398
        $fields->push(new DropdownField('params[limit]', _t('MandrillAdmin.PERPAGE', 'Number of results'), array(
399
            100 => 100,
400
            500 => 500,
401
            1000 => 1000,
402
            10000 => 10000,
403
        ), $this->getParam('limit', 100)));
404
405
        foreach ($fields->FieldList() as $field) {
406
            $field->addExtraClass('no-change-track');
407
        }
408
409
        // This is a ugly hack to allow embedding a form into another form
410
        $fields->push($doSearch = new FormAction('doSearch', _t('MandrillAdmin.DOSEARCH', 'Search')));
411
        $doSearch->addExtraClass("btn btn-primary");
412
        $doSearch->setAttribute('onclick', "jQuery('#Form_SearchForm').append(jQuery('#Form_EditForm input,#Form_EditForm select').clone()).submit();");
413
414
        return $fields;
415
    }
416
417
    public function SearchForm()
418
    {
419
        $SearchForm = new Form($this, 'SearchForm', new FieldList(), new FieldList(new FormAction('doSearch')));
420
        $SearchForm->setAttribute('style', 'display:none');
421
        return $SearchForm;
422
    }
423
424
    public function doSearch($data, Form $form)
425
    {
426
        $post = $this->getRequest()->postVar('params');
427
        if (!$post) {
428
            return $this->redirectBack();
429
        }
430
        $params = [];
431
432
        $validFields = [];
433
        foreach ($this->SearchFields()->FieldList()->dataFields() as $field) {
434
            $validFields[] = str_replace(['params[', ']'], '', $field->getName());
435
        }
436
437
        foreach ($post as $k => $v) {
438
            if (in_array($k, $validFields)) {
439
                $params[$k] = $v;
440
            }
441
        }
442
443
        $this->getSession()->set(__class__ . '.Search', $params);
444
        $this->getSession()->save($this->getRequest());
445
446
        return $this->redirectBack();
447
    }
448
449
    /**
450
     * List of messages events
451
     *
452
     * Messages are cached to avoid hammering the api
453
     *
454
     * @link https://mandrillapp.com/api/docs/messages.JSON.html#method=search
455
     * @return ArrayList|string
456
     * @throws InvalidArgumentException
457
     */
458
    public function Messages()
459
    {
460
        $params = $this->getParams();
461
462
        // We need the parameters in order to be sent to the api
463
        $orderedParams = [];
464
        $orderedParams['query'] = isset($params['query']) ? $params['query'] : '*';
465
        $orderedParams['date_from'] = isset($params['date_from']) ? $params['date_from'] : null;
466
        $orderedParams['date_to'] = isset($params['date_to']) ? $params['date_to'] : null;
467
        $orderedParams['tags'] = isset($params['tags']) ? $params['tags'] : null;
468
        $orderedParams['senders'] = isset($params['senders']) ? $params['senders'] : null;
469
        $orderedParams['api_keys'] = isset($params['api_keys']) ? $params['api_keys'] : [MandrillHelper::getAPIKey()];
470
        $orderedParams['limit'] = isset($params['limit']) ? $params['limit'] : null;
471
472
        $messages = $this->getCachedData('messages.search', $orderedParams, 60 * self::MESSAGE_CACHE_MINUTES);
473
474
        if ($messages === false) {
475
            if ($this->lastException) {
476
                return $this->lastException->getMessage();
477
            }
478
            return _t('MandrillAdmin.NO_MESSAGES', 'No messages');
479
        }
480
481
        $list = new ArrayList();
482
        if ($messages) {
483
            foreach ($messages as $message) {
484
                $message['date'] = date('Y-m-d H:i:s', $message['ts']);
485
                $m = new ArrayData($message);
486
                $list->push($m);
487
            }
488
        }
489
490
        return $list;
491
    }
492
493
    /**
494
     * Provides custom permissions to the Security section
495
     *
496
     * @return array
497
     */
498
    public function providePermissions()
499
    {
500
        $title = _t("MandrillAdmin.MENUTITLE", LeftAndMain::menu_title('Mandrill'));
501
        return [
502
            "CMS_ACCESS_Mandrill" => [
503
                'name' => _t('MandrillAdmin.ACCESS', "Access to '{title}' section", ['title' => $title]),
504
                'category' => _t('Permission.CMS_ACCESS_CATEGORY', 'CMS Access'),
505
                'help' => _t(
506
                    'MandrillAdmin.ACCESS_HELP',
507
                    'Allow use of Mandrill admin section'
508
                )
509
            ],
510
        ];
511
    }
512
513
    /**
514
     * Message helper
515
     *
516
     * @param string $message
517
     * @param string $status
518
     * @return string
519
     */
520
    protected function MessageHelper($message, $status = 'info')
521
    {
522
        return '<div class="message ' . $status . '">' . $message . '</div>';
523
    }
524
525
    /**
526
     * Button helper
527
     *
528
     * @param string $link
529
     * @param string $text
530
     * @param boolean $confirm
531
     * @return string
532
     */
533
    protected function ButtonHelper($link, $text, $confirm = false)
534
    {
535
        $link = '<a class="btn btn-primary" href="' . $link . '"';
536
        if ($confirm) {
537
            $link .= ' onclick="return confirm(\'' . _t('MandrillAdmin.CONFIRM_MSG', 'Are you sure?') . '\')"';
538
        }
539
        $link .= '>' . $text . '</a>';
540
        return $link;
541
    }
542
543
    /**
544
     * A template accessor to check the ADMIN permission
545
     *
546
     * @return bool
547
     */
548
    public function IsAdmin()
549
    {
550
        return Permission::check("ADMIN");
551
    }
552
553
    /**
554
     * Check the permission for current user
555
     *
556
     * @return bool
557
     */
558
    public function canView($member = null)
559
    {
560
        $mailer = MandrillHelper::getMailer();
561
        $transport = MandrillHelper::getTransportFromMailer($mailer);
562
        // Another custom mailer has been set
563
        if (!($transport instanceof MandrillApiTransport)) {
564
            return false;
565
        }
566
        return Permission::check("CMS_ACCESS_Mandrill", 'any', $member);
567
    }
568
569
    /**
570
     *
571
     * @return bool
572
     */
573
    public function CanConfigureApi()
574
    {
575
        return Permission::check('ADMIN') || Director::isDev();
576
    }
577
578
    /**
579
     * Check if webhook is installed. Returns the webhook details if installed.
580
     *
581
     * @return bool|array
582
     * @throws InvalidArgumentException
583
     */
584
    public function WebhookInstalled()
585
    {
586
        $list = $this->getCachedData('webhooks.getList', [], 60 * self::WEBHOOK_CACHE_MINUTES);
587
588
        if (empty($list)) {
589
            return false;
590
        }
591
        $url = $this->WebhookUrl();
592
        foreach ($list as $el) {
593
            if (!empty($el['url']) && $el['url'] === $url) {
594
                return $el;
595
            }
596
        }
597
        return false;
598
    }
599
600
    /**
601
     * Hook details for template
602
     * @return ArrayData|null
603
     * @throws InvalidArgumentException
604
     */
605
    public function WebhookDetails()
606
    {
607
        $el = $this->WebhookInstalled();
608
        if ($el) {
609
            return new ArrayData($el);
610
        }
611
        return null;
612
    }
613
614
    /**
615
     * Get content of the tab
616
     *
617
     * @return FormField
618
     * @throws InvalidArgumentException
619
     */
620
    public function WebhookTab()
621
    {
622
        if ($this->WebhookInstalled()) {
623
            return $this->UninstallHookForm();
624
        }
625
        return $this->InstallHookForm();
626
    }
627
628
    /**
629
     * @return string
630
     */
631
    public function WebhookUrl()
632
    {
633
        if (self::config()->webhook_base_url) {
634
            return rtrim(self::config()->webhook_base_url, '/') . '/__mandrill/incoming';
635
        }
636
        if (Director::isLive()) {
637
            return Director::absoluteURL('/__mandrill/incoming');
638
        }
639
        $protocol = Director::protocol();
640
        return $protocol . $this->getDomain() . '/__mandrill/incoming';
641
    }
642
643
    /**
644
     * Install hook form
645
     *
646
     * @return FormField
647
     */
648
    public function InstallHookForm()
649
    {
650
        $fields = new CompositeField();
651
        $fields->push(new LiteralField('Info', $this->MessageHelper(
652
            _t('MandrillAdmin.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()]),
653
            'bad'
654
        )));
655
        $fields->push(new LiteralField('doInstallHook', $this->ButtonHelper(
656
            $this->Link('doInstallHook'),
657
            _t('MandrillAdmin.DOINSTALL_WEBHOOK', 'Install webhook')
658
        )));
659
        return $fields;
660
    }
661
662
    /**
663
     * @return HTTPResponse
664
     * @throws Exception
665
     */
666
    public function doInstallHook()
667
    {
668
        if (!$this->CanConfigureApi()) {
669
            return $this->redirectBack();
670
        }
671
672
        $client = MandrillHelper::getClient();
673
674
        $url = $this->WebhookUrl();
675
        $description = SiteConfig::current_site_config()->Title;
676
677
        try {
678
            $client->webhooks->add($url, $description);
679
            $this->getCache()->clear();
680
        } catch (Exception $ex) {
681
            $this->getLogger()->debug($ex);
682
        }
683
684
        return $this->redirectBack();
685
    }
686
687
    /**
688
     * Uninstall hook form
689
     *
690
     * @return FormField
691
     */
692
    public function UninstallHookForm()
693
    {
694
        $fields = new CompositeField();
695
        $fields->push(new LiteralField('Info', $this->MessageHelper(
696
            _t('MandrillAdmin.WebhookInstalled', 'Webhook is installed and accessible at the following url {url}.', ['url' => $this->WebhookUrl()]),
697
            'good'
698
        )));
699
        $fields->push(new LiteralField('doUninstallHook', $this->ButtonHelper(
700
            $this->Link('doUninstallHook'),
701
            _t('MandrillAdmin.DOUNINSTALL_WEBHOOK', 'Uninstall webhook'),
702
            true
703
        )));
704
        return $fields;
705
    }
706
707
    /**
708
     * @param array $data
709
     * @param Form $form
710
     * @return HTTPResponse
711
     * @throws Exception
712
     * @throws InvalidArgumentException
713
     */
714
    public function doUninstallHook($data, Form $form)
715
    {
716
        if (!$this->CanConfigureApi()) {
717
            return $this->redirectBack();
718
        }
719
720
        $client = MandrillHelper::getClient();
721
722
        try {
723
            $el = $this->WebhookInstalled();
724
            if ($el && !empty($el['id'])) {
725
                $client->webhooks->delete($el['id']);
726
            }
727
            $this->getCache()->clear();
728
        } catch (Exception $ex) {
729
            $this->getLogger()->debug($ex);
730
        }
731
732
        return $this->redirectBack();
733
    }
734
735
    /**
736
     * Check if sending domain is installed
737
     *
738
     * @return array|bool
739
     * @throws InvalidArgumentException
740
     */
741
    public function SendingDomainInstalled()
742
    {
743
        // @todo - Use $client or remove?
744
        $client = MandrillHelper::getClient();
0 ignored issues
show
The assignment to $client is dead and can be removed.
Loading history...
745
746
        $domains = $this->getCachedData('senders.domains', [$this->getDomain()], 60 * self::SENDINGDOMAIN_CACHE_MINUTES);
747
748
        if (empty($domains)) {
749
            return false;
750
        }
751
752
        // Filter
753
        $host = $this->getDomain();
754
        foreach ($domains as $domain) {
755
            if ($domain['domain'] == $host) {
756
                return $domain;
757
            }
758
        }
759
        return false;
760
    }
761
762
    /**
763
     * Trigger request to check if sending domain is verified
764
     *
765
     * @return array|bool
766
     * @throws Exception
767
     */
768
    public function VerifySendingDomain()
769
    {
770
        $client = MandrillHelper::getClient();
771
772
        $host = $this->getDomain();
773
774
        $verification = $client->senders->verifyDomain($host, 'postmaster@' . $host);
775
776
        if (empty($verification)) {
777
            return false;
778
        }
779
        return $verification;
780
    }
781
782
    /**
783
     * Get content of the tab
784
     *
785
     * @return FormField
786
     * @throws InvalidArgumentException
787
     */
788
    public function DomainTab()
789
    {
790
        $defaultDomain = $this->getDomain();
791
        $defaultDomainInfos = null;
792
793
        $domains = $this->getCachedData('senders.domains', [], 60 * self::SENDINGDOMAIN_CACHE_MINUTES);
794
        $validDomains = MandrillHelper::listValidDomains();
795
796
        $fields = new CompositeField();
797
798
        // @link https://mandrillapp.com/api/docs/senders.JSON.html#method=domains
799
        $list = new ArrayList();
800
        if ($domains) {
801
            foreach ($domains as $domain) {
802
                if (!empty($validDomains) && !in_array($domain['domain'], $validDomains)) {
803
                    continue;
804
                }
805
                $list->push(new ArrayData([
806
                    'Domain' => $domain['domain'],
807
                    'SPF' => $domain['spf']['valid'],
808
                    'DKIM' => $domain['dkim']['valid'],
809
                    'Compliance' => $domain['valid_signing'],
810
                    'Verified' => $domain['verified_at'],
811
                ]));
812
813
                if ($domain['domain'] == $defaultDomain) {
814
                    $defaultDomainInfos = $domain;
815
                }
816
            }
817
        }
818
819
        $config = GridFieldConfig::create();
820
        $config->addComponent(new GridFieldToolbarHeader());
821
        $config->addComponent(new GridFieldTitleHeader());
822
        $config->addComponent($columns = new GridFieldDataColumns());
823
        $columns->setDisplayFields(ArrayLib::valuekey(['Domain', 'SPF', 'DKIM', 'Compliance', 'Verified']));
824
        $domainsList = new GridField('SendingDomains', _t('MandrillAdmin.ALL_SENDING_DOMAINS', 'Configured sending domains'), $list, $config);
825
        $domainsList->addExtraClass('mb-2');
826
        $fields->push($domainsList);
827
828
        if (!$defaultDomainInfos) {
829
            $this->InstallDomainForm($fields);
830
        } else {
831
            $this->UninstallDomainForm($fields);
832
        }
833
834
        return $fields;
835
    }
836
837
    /**
838
     * @return string
839
     */
840
    public function InboundUrl()
841
    {
842
        $subdomain = self::config()->inbound_subdomain;
843
        $domain = $this->getDomain();
844
        if ($domain) {
845
            return $subdomain . '.' . $domain;
846
        }
847
        return false;
848
    }
849
850
    /**
851
     * Get domain
852
     *
853
     * @return boolean|string
854
     */
855
    public function getDomain()
856
    {
857
        return MandrillHelper::getDomain();
858
    }
859
860
    /**
861
     * Install domain form
862
     *
863
     * @param CompositeField $fields
864
     */
865
    public function InstallDomainForm(CompositeField $fields)
866
    {
867
        $host = $this->getDomain();
868
869
        $fields->push(new LiteralField('Info', $this->MessageHelper(
870
            _t('MandrillAdmin.DomainNotInstalled', 'Default sending domain {domain} is not installed.', ['domain' => $host]),
871
            "bad"
872
        )));
873
        $fields->push(new LiteralField('doInstallDomain', $this->ButtonHelper(
874
            $this->Link('doInstallDomain'),
875
            _t('MandrillAdmin.DOINSTALLDOMAIN', 'Install domain')
876
        )));
877
    }
878
879
    /**
880
     * @return HTTPResponse
881
     * @throws Exception
882
     */
883
    public function doInstallDomain()
884
    {
885
        if (!$this->CanConfigureApi()) {
886
            return $this->redirectBack();
887
        }
888
889
        $client = MandrillHelper::getClient();
890
891
        $domain = $this->getDomain();
892
893
        if (!$domain) {
894
            return $this->redirectBack();
895
        }
896
897
        try {
898
            $client->senders->addDomain($domain);
899
            $this->getCache()->clear();
900
        } catch (Exception $ex) {
901
            $this->getLogger()->debug($ex);
902
        }
903
904
        return $this->redirectBack();
905
    }
906
907
    /**
908
     * Uninstall domain form
909
     *
910
     * @param CompositeField $fields
911
     * @throws InvalidArgumentException
912
     */
913
    public function UninstallDomainForm(CompositeField $fields)
914
    {
915
        $domainInfos = $this->SendingDomainInstalled();
916
917
        $domain = $this->getDomain();
918
919
        if ($domainInfos && $domainInfos['valid_signing']) {
920
            $fields->push(new LiteralField('Info', $this->MessageHelper(
921
                _t('MandrillAdmin.DomainInstalled', 'Default domain {domain} is installed.', ['domain' => $domain]),
922
                'good'
923
            )));
924
        } else {
925
            $fields->push(new LiteralField('Info', $this->MessageHelper(
926
                _t('MandrillAdmin.DomainInstalledBut', 'Default domain {domain} is installed, but is not properly configured.'),
927
                'warning'
928
            )));
929
        }
930
        $fields->push(new LiteralField('doUninstallHook', $this->ButtonHelper(
931
            $this->Link('doUninstallHook'),
932
            _t('MandrillAdmin.DOUNINSTALLDOMAIN', 'Uninstall domain'),
933
            true
934
        )));
935
    }
936
}
937