Completed
Push — develop ( 04ac75...c0d878 )
by Nate
10:49
created

Provider::canDelete()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 44

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
dl 0
loc 44
rs 9.216
c 0
b 0
f 0
ccs 0
cts 0
cp 0
cc 4
nc 6
nop 1
crap 20
1
<?php
2
3
/**
4
 * @copyright  Copyright (c) Flipbox Digital Limited
5
 * @license    https://flipboxfactory.com/software/patron/license
6
 * @link       https://www.flipboxfactory.com/software/patron/
7
 */
8
9
namespace flipbox\patron\records;
10
11
use Craft;
12
use craft\base\PluginInterface;
13
use craft\db\Query;
14
use craft\helpers\StringHelper;
15
use craft\helpers\Template;
16
use flipbox\ember\helpers\ModelHelper;
17
use flipbox\ember\helpers\ObjectHelper;
18
use flipbox\ember\helpers\QueryHelper;
19
use flipbox\ember\records\ActiveRecordWithId;
20
use flipbox\ember\records\traits\StateAttribute;
21
use flipbox\ember\traits\HandleRules;
22
use flipbox\patron\db\ProviderActiveQuery;
23
use flipbox\patron\helpers\ProviderHelper;
24
use flipbox\patron\Patron;
25
use flipbox\patron\providers\SettingsInterface;
26
use flipbox\patron\validators\ProviderValidator;
27
use Twig_Markup;
28
use yii\helpers\ArrayHelper;
29
30
/**
31
 * @author Flipbox Factory <[email protected]>
32
 * @since 1.0.0
33
 *
34
 * @property string $clientId
35
 * @property string $clientSecret
36
 * @property string $class
37
 * @property array $settings
38
 * @property ProviderLock[] $locks
39
 * @property Token[] $tokens
40
 * @property ProviderEnvironment[] $environments
41
 */
42
class Provider extends ActiveRecordWithId
43
{
44
    use HandleRules,
45
        StateAttribute;
46
47
    /**
48
     * The table alias
49
     */
50
    const TABLE_ALIAS = 'patron_providers';
51
52
    /**
53
     * @var SettingsInterface
54
     */
55
    private $settingsModel;
56
57
    /**
58
     * The length of the identifier
59
     */
60
    const CLIENT_ID_LENGTH = 100;
61
62
    /**
63
     * The length of the secret
64
     */
65
    const CLIENT_SECRET_LENGTH = 100;
66
67
    /**
68
     * @return string|null
69
     */
70
    public function getIcon()
71
    {
72
        if ($this->class === null) {
73
            return null;
74
        }
75
76
        return Patron::getInstance()->getCp()->getProviderIcon(
77
            $this->class
78
        );
79
    }
80
81
    /**
82
     * @inheritdoc
83
     * @return ProviderActiveQuery
84
     * @throws \yii\base\InvalidConfigException
85
     */
86
    public static function find()
87
    {
88
        /** @noinspection PhpIncompatibleReturnTypeInspection */
89
        return Craft::createObject(ProviderActiveQuery::class, [get_called_class()]);
90
    }
91
92
    /**
93
     * @inheritdoc
94
     */
95
    public function rules()
96
    {
97
        return array_merge(
98
            parent::rules(),
99
            $this->handleRules(),
100
            $this->stateRules(),
101
            [
102
                [
103
                    [
104
                        'clientId'
105
                    ],
106
                    'string',
107
                    'max' => static::CLIENT_ID_LENGTH
108
                ],
109
                [
110
                    [
111
                        'clientSecret'
112
                    ],
113
                    'string',
114
                    'max' => static::CLIENT_SECRET_LENGTH
115
                ],
116
                [
117
                    [
118
                        'class'
119
                    ],
120
                    ProviderValidator::class
121
                ],
122
                [
123
                    [
124
                        'clientId',
125
                        'class'
126
                    ],
127
                    'required'
128
                ],
129
                [
130
                    [
131
                        'clientId',
132
                        'clientSecret',
133
                        'class',
134
                        'settings'
135
                    ],
136
                    'safe',
137
                    'on' => [
138
                        ModelHelper::SCENARIO_DEFAULT
139
                    ]
140
                ]
141
            ]
142
        );
143
    }
144
145
    /**
146
     * Get all of the associated tokens.
147
     *
148
     * @param array $config
149
     * @return \yii\db\ActiveQuery
150
     */
151
    public function getTokens(array $config = [])
152
    {
153
        $query = $this->hasMany(
154
            Token::class,
155
            ['providerId' => 'id']
156
        );
157
158
        if (!empty($config)) {
159
            QueryHelper::configure(
160
                $query,
161
                $config
162
            );
163
        }
164
165
        return $query;
166
    }
167
168
    /**
169
     * Get all of the associated tokens.
170
     *
171
     * @param array $config
172
     * @return \yii\db\ActiveQuery
173
     */
174
    public function getLocks(array $config = [])
175
    {
176
        $query = $this->hasMany(
177
            ProviderLock::class,
178
            ['providerId' => 'id']
179
        );
180
181
        if (!empty($config)) {
182
            QueryHelper::configure(
183
                $query,
184
                $config
185
            );
186
        }
187
188
        return $query;
189
    }
190
191
    /**
192
     * Get all of the associated environments.
193
     *
194
     * @param array $config
195
     * @return \yii\db\ActiveQuery
196
     */
197
    public function getEnvironments(array $config = [])
198
    {
199
        $query = $this->hasMany(
200
            ProviderEnvironment::class,
201
            ['providerId' => 'id']
202
        )->indexBy('environment');
203
204
        if (!empty($config)) {
205
            QueryHelper::configure(
206
                $query,
207
                $config
208
            );
209
        }
210
211
        return $query;
212
    }
213
214
    /**
215
     * @param array $environments
216
     * @return $this
217
     */
218
    public function setEnvironments(array $environments = [])
219
    {
220
        $records = [];
221
        foreach (array_filter($environments) as $key => $environment) {
222
            $records[] = $this->resolveEnvironment($key, $environment);
223
        }
224
225
        $this->populateRelation('environments', $records);
226
        return $this;
227
    }
228
229
    /**
230
     * @param string $key
231
     * @param $environment
232
     * @return ProviderEnvironment
233
     */
234
    protected function resolveEnvironment(string $key, $environment): ProviderEnvironment
235
    {
236
        if (!$record = $this->environments[$key] ?? null) {
237
            $record = new ProviderEnvironment();
238
        }
239
240
        if (!is_array($environment)) {
241
            $environment = ['environment' => $environment];
242
        }
243
244
        /** @noinspection PhpIncompatibleReturnTypeInspection */
245
        return ObjectHelper::populate(
246
            $record,
247
            $environment
248
        );
249
    }
250
251
    /*******************************************
252
     * SAVE
253
     *******************************************/
254
255
    /**
256
     * @param PluginInterface $plugin
257
     * @param bool $runValidation
258
     * @param null $attributeNames
259
     * @return bool
260
     * @throws \Throwable
261
     * @throws \yii\db\StaleObjectException
262
     */
263
    public function saveAndLock(PluginInterface $plugin, $runValidation = true, $attributeNames = null): bool
264
    {
265
        if (!$this->save($runValidation, $attributeNames)) {
266
            return false;
267
        }
268
269
        return $this->addLock($plugin);
270
    }
271
272
273
    /*******************************************
274
     * LOCK
275
     *******************************************/
276
277
    /**
278
     * @param PluginInterface $plugin
279
     * @return bool
280
     * @throws \Throwable
281
     * @throws \yii\db\StaleObjectException
282
     */
283
    public function addLock(PluginInterface $plugin): bool
284
    {
285
        if (null === ($pluginId = $this->getPluginId($plugin))) {
286
            return false;
287
        }
288
289
        return Patron::getInstance()->getProviderLocks()->associateByIds(
290
            $provider->getId(),
0 ignored issues
show
Bug introduced by
The variable $provider does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
291
            $pluginId
292
        );
293
    }
294
295
    /**
296
     * @param PluginInterface $plugin
297
     * @return bool
298
     */
299
    public function removeLock(PluginInterface $plugin): bool
300
    {
301
        if (null === ($pluginId = $this->getPluginId($plugin))) {
302
            return false;
303
        }
304
305
        return Patron::getInstance()->getProviderLocks()->dissociateByIds(
306
            $provider->getId(),
0 ignored issues
show
Bug introduced by
The variable $provider does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
307
            $pluginId
308
        );
309
    }
310
311
    /**
312
     * @return bool
313
     */
314
    public function isLocked(): bool
315
    {
316
        return !empty($this->locks);
317
    }
318
319
    /**
320
     * @param PluginInterface $plugin
321
     * @return int|null
322
     */
323
    protected function getPluginId(PluginInterface $plugin)
324
    {
325
        $id = (new Query())
326
            ->select([
327
                'id',
328
            ])
329
            ->from(['{{%plugins}}'])
330
            ->where([
331
                'handle' => $plugin->getHandle()
332
            ])
333
            ->scalar();
334
335
        return $id ? (int)$id : null;
336
    }
337
338
    /**
339
     * @param PluginInterface $plugin
340
     * @return int|null
341
     */
342
    protected function getPluginName(PluginInterface $plugin)
343
    {
344
        $id = (new Query())
345
            ->select([
346
                'id',
347
            ])
348
            ->from(['{{%plugins}}'])
349
            ->where([
350
                'handle' => $plugin->getHandle()
351
            ])
352
            ->scalar();
353
354
        return $id ? (int)$id : null;
355
    }
356
357
    /*******************************************
358
     * EVENTS
359
     *******************************************/
360
361
    /**
362
     * @param PluginInterface|null $plugin
363
     * @return bool|false|int
364
     * @throws \Throwable
365
     * @throws \yii\db\StaleObjectException
366
     */
367
    public function delete(PluginInterface $plugin = null)
368
    {
369
        return $this->canDelete($plugin) ? parent::delete() : false;
370
    }
371
372
    /**
373
     * @param PluginInterface|null $plugin
374
     * @return bool
375
     * @throws \craft\errors\InvalidPluginException
376
     */
377
    protected function canDelete(PluginInterface $plugin = null)
378
    {
379
        // If a plugin is locking this, prevent deletion
380
        $lockQuery = $this->getLocks();
381
        if (null !== $plugin) {
382
            $lockQuery->andWhere(
383
                ['<>', 'pluginId', $this->getPluginId($plugin)]
384
            );
385
        }
386
387
        $locks = $lockQuery->all();
388
389
        if (count($locks) > 0) {
390
            $handles = (new Query())
391
                ->select([
392
                    'handle',
393
                ])
394
                ->from(['{{%plugins}}'])
395
                ->where([
396
                    'id' => ArrayHelper::getColumn($locks, ['pluginId']),
0 ignored issues
show
Documentation introduced by
array('pluginId') is of type array<integer,string,{"0":"string"}>, but the function expects a string|object<Closure>.

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...
397
                ])
398
                ->column();
399
400
            $names = [];
401
            foreach ($handles as $handle) {
402
                $plugin = Craft::$app->getPlugins()->getPluginInfo($handle);
403
                $names[] = $plugin['name'] ?? 'Unknown Plugin';
404
            }
405
406
            $this->addError(
407
                'locks',
408
                Craft::t(
409
                    'patron',
410
                    'The provider is locked by the following plugins: {plugins}',
411
                    [
412
                        'plugins' => StringHelper::toString($names, ', ')
413
                    ]
414
                )
415
            );
416
            return false;
417
        }
418
419
        return true;
420
    }
421
422
    /**
423
     * @inheritdoc
424
     * @throws \Throwable
425
     */
426
    public function afterSave($insert, $changedAttributes)
427
    {
428
        Patron::getInstance()->manageProviders()->saveEnvironments($this);
429
        parent::afterSave($insert, $changedAttributes);
430
    }
431
432
    /**
433
     * @return string
434
     */
435
    public function getDisplayName(): string
436
    {
437
        return ProviderHelper::displayName(
438
            $this->class
439
        );
440
    }
441
442
    /**
443
     * @return Twig_Markup
444
     * @throws \yii\base\InvalidConfigException
445
     */
446
    public function getSettingsHtml(): Twig_Markup
447
    {
448
        return Template::raw(
449
            $this->getSettingsModel()->inputHtml()
450
        );
451
    }
452
453
    /**
454
     * @return SettingsInterface
455
     * @throws \yii\base\InvalidConfigException
456
     */
457
    protected function getSettingsModel(): SettingsInterface
458
    {
459
        if (!$this->settingsModel instanceof SettingsInterface) {
460
            $this->settingsModel = Patron::getInstance()->manageProviders()->resolveSettings($this, $this->settings);
461
        }
462
463
        return $this->settingsModel;
464
    }
465
}
466