MailgunAdmin::Messages()   B
last analyzed

Complexity

Conditions 9
Paths 8

Size

Total Lines 66

Duplication

Lines 8
Ratio 12.12 %

Importance

Changes 0
Metric Value
dl 8
loc 66
rs 7.1862
c 0
b 0
f 0
cc 9
nc 8
nop 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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);
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (index() instead of settings()). Are you sure this is correct? If so, you might want to change this to $this->index().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
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);
0 ignored issues
show
Unused Code introduced by
$form is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
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) {
0 ignored issues
show
Unused Code introduced by
The parameter $item is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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)
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a null|string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
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');
0 ignored issues
show
Bug introduced by
The variable $settingsTab does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
244
        } elseif ($actionParam == 'messages') {
245
            $messagesTab->addExtraClass('ui-state-active');
246
        }
247
248
        $actions = new FieldList();
0 ignored issues
show
Unused Code introduced by
$actions is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
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);
0 ignored issues
show
Bug introduced by
The variable $cacheResult does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
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);
0 ignored issues
show
Bug introduced by
The variable $cache does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
Bug introduced by
The variable $key does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
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
Duplication introduced by
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.

Loading history...
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
Duplication introduced by
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.

Loading history...
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
Duplication introduced by
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.

Loading history...
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
Duplication introduced by
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.

Loading history...
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) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $messages of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
518
            $mergedMessages = [];
0 ignored issues
show
Unused Code introduced by
$mergedMessages is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
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
Duplication introduced by
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.

Loading history...
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
Duplication introduced by
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.

Loading history...
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();
0 ignored issues
show
Unused Code introduced by
$url is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
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);
0 ignored issues
show
Documentation introduced by
$el is of type boolean, but the function expects a object|array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
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
Duplication introduced by
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.

Loading history...
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;
0 ignored issues
show
Unused Code introduced by
$description is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
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
Duplication introduced by
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.

Loading history...
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();
0 ignored issues
show
Unused Code introduced by
$client is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
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);
0 ignored issues
show
Bug introduced by
The method verifySendingDomain() does not seem to exist on object<Mailgun\Mailgun>.

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...
871
872
        if (empty($verification)) {
873
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by LeKoala\Mailgun\MailgunAdmin::VerifySendingDomain of type array.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
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) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $domains of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
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();
0 ignored issues
show
Bug introduced by
The method getOutboundDNSRecords cannot be called on $showResponse (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
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) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $domain of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
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) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $domain of type string|false is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1041
            return $this->getDomainFromHost();
1042
        }
1043
        return $domain;
1044
    }
1045
1046
    /**
1047
     * Install domain form
1048
     *
1049
     * @param CompositeField $fieldsd
0 ignored issues
show
Documentation introduced by
There is no parameter named $fieldsd. Did you maybe mean $fields?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
1050
     * @return FormField
1051
     */
1052 View Code Duplication
    public function InstallDomainForm(CompositeField $fields)
0 ignored issues
show
Duplication introduced by
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.

Loading history...
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
Duplication introduced by
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.

Loading history...
1067
    {
1068
        if (!$this->CanConfigureApi()) {
1069
            return $this->redirectBack();
1070
        }
1071
1072
        $client = MailgunHelper::getClient();
1073
1074
        $domain = $this->getDomain();
1075
1076
        if (!$domain) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $domain of type false|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
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
0 ignored issues
show
Documentation introduced by
There is no parameter named $fieldsd. Did you maybe mean $fields?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
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') {
0 ignored issues
show
Bug introduced by
The method getState cannot be called on $domainInfos (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
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
Duplication introduced by
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.

Loading history...
1121
    {
1122
        if (!$this->CanConfigureApi()) {
1123
            return $this->redirectBack();
1124
        }
1125
1126
        $client = MailgunHelper::getClient();
1127
1128
        $domain = $this->getDomain();
1129
1130
        if (!$domain) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $domain of type false|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
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