Completed
Push — develop ( bc4d8c...856b14 )
by Nate
30:34
created

Provider::beforeSave()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 0
cts 7
cp 0
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 1
crap 6
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 = 255;
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
            $this->getId() ?: 0,
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
            $this->getId() ?: 0,
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
     */
425
    public function afterFind()
426
    {
427
        if ($this->clientSecret) {
428
            $this->clientSecret = ProviderHelper::decryptClientSecret($this->clientSecret);
429
        }
430
431
        parent::afterFind();
432
    }
433
434
    /**
435
     * @inheritdoc
436
     */
437
    public function beforeSave($insert)
438
    {
439
        if ($this->clientSecret) {
440
            $this->clientSecret = ProviderHelper::encryptClientSecret($this->clientSecret);
441
        }
442
443
        return parent::beforeSave($insert);
444
    }
445
446
    /**
447
     * @inheritdoc
448
     * @throws \Throwable
449
     */
450
    public function afterSave($insert, $changedAttributes)
451
    {
452
        if ($this->clientSecret) {
453
            $this->clientSecret = ProviderHelper::decryptClientSecret($this->clientSecret);
454
        }
455
456
        Patron::getInstance()->manageProviders()->saveEnvironments($this);
457
        parent::afterSave($insert, $changedAttributes);
458
    }
459
460
    /**
461
     * @return string
462
     */
463
    public function getDisplayName(): string
464
    {
465
        return ProviderHelper::displayName(
466
            $this->class
467
        );
468
    }
469
470
    /**
471
     * @return Twig_Markup
472
     * @throws \yii\base\InvalidConfigException
473
     */
474
    public function getSettingsHtml(): Twig_Markup
475
    {
476
        return Template::raw(
477
            $this->getSettingsModel()->inputHtml()
478
        );
479
    }
480
481
    /**
482
     * @return SettingsInterface
483
     * @throws \yii\base\InvalidConfigException
484
     */
485
    protected function getSettingsModel(): SettingsInterface
486
    {
487
        if (!$this->settingsModel instanceof SettingsInterface) {
488
            $this->settingsModel = Patron::getInstance()->manageProviders()->resolveSettings($this, $this->settings);
489
        }
490
491
        return $this->settingsModel;
492
    }
493
}
494