Provider   A
last analyzed

Complexity

Total Complexity 36

Size/Duplication

Total Lines 418
Duplicated Lines 0 %

Coupling/Cohesion

Components 3
Dependencies 13

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 36
lcom 3
cbo 13
dl 0
loc 418
ccs 0
cts 241
cp 0
rs 9.52
c 0
b 0
f 0

17 Methods

Rating   Name   Duplication   Size   Complexity  
A saveAndLock() 0 8 2
A findByCondition() 0 9 3
A isLocked() 0 4 1
A getInfo() 0 12 2
A getSettings() 0 14 2
A populateRecord() 0 12 2
B rules() 0 55 1
A getTokens() 0 16 2
A getLocks() 0 16 2
A addLock() 0 24 3
A removeLock() 0 15 4
A getPluginId() 0 14 2
A getPluginName() 0 14 2
A delete() 0 4 2
A canDelete() 0 44 4
A find() 0 5 1
A toProjectConfig() 0 12 1
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\Json;
15
use craft\helpers\StringHelper;
16
use flipbox\craft\ember\helpers\ObjectHelper;
17
use flipbox\craft\ember\helpers\QueryHelper;
18
use flipbox\craft\ember\models\HandleRulesTrait;
19
use flipbox\craft\ember\records\ActiveRecordWithId;
20
use flipbox\craft\ember\records\StateAttributeTrait;
21
use flipbox\patron\events\RegisterProviderSettings;
22
use flipbox\patron\helpers\ProviderHelper;
23
use flipbox\patron\Patron;
24
use flipbox\patron\queries\ProviderActiveQuery;
25
use flipbox\patron\settings\BaseSettings;
26
use flipbox\patron\settings\SettingsInterface;
27
use flipbox\patron\validators\ProviderSettings as ProviderSettingsValidator;
28
use flipbox\patron\validators\ProviderValidator;
29
use yii\helpers\ArrayHelper;
30
31
/**
32
 * @author Flipbox Factory <[email protected]>
33
 * @since 1.0.0
34
 *
35
 * @property string $clientId
36
 * @property string $clientSecret
37
 * @property string $class
38
 * @property ProviderLock[] $locks
39
 * @property Token[] $tokens
40
 * @property SettingsInterface $settings
41
 */
42
class Provider extends ActiveRecordWithId
43
{
44
    use HandleRulesTrait,
45
        StateAttributeTrait;
46
47
    /**
48
     * The table alias
49
     */
50
    const TABLE_ALIAS = 'patron_providers';
51
52
    const CLIENT_ID_LENGTH = 100;
53
    const CLIENT_SECRET_LENGTH = 255;
54
55
    protected $getterPriorityAttributes = ['settings'];
56
57
    /**
58
     * @inheritdoc
59
     * @return ProviderActiveQuery
60
     * @throws \yii\base\InvalidConfigException
61
     */
62
    public static function find()
63
    {
64
        /** @noinspection PhpIncompatibleReturnTypeInspection */
65
        return Craft::createObject(ProviderActiveQuery::class, [get_called_class()]);
66
    }
67
68
    /**
69
     * @inheritdoc
70
     */
71
    protected static function findByCondition($condition)
72
    {
73
        if (!is_numeric($condition) && is_string($condition)) {
74
            $condition = ['handle' => $condition];
75
        }
76
77
        /** @noinspection PhpInternalEntityUsedInspection */
78
        return parent::findByCondition($condition);
79
    }
80
81
    /**
82
     * An array of additional information about the provider.  As an example:
83
     *
84
     * ```
85
     * [
86
     *      'name' => 'Provider Name',
87
     *      'icon' => '/path/to/icon.svg'
88
     * ]
89
     * ```
90
     * @return array
91
     * @throws \ReflectionException
92
     */
93
    public function getInfo(): array
94
    {
95
        if ($this->class === null) {
96
            return [];
97
        }
98
99
        $info = Patron::getInstance()->getCp()->getProviderInfo();
100
101
        return $info[$this->class] ?? [
102
            'name' => ProviderHelper::displayName($this->class)
103
        ];
104
    }
105
106
    /**
107
     * @return SettingsInterface
108
     * @throws \yii\base\InvalidConfigException
109
     */
110
    public function getSettings(): SettingsInterface
111
    {
112
        $settings = $this->getAttribute('settings');
113
        if (!$settings instanceof SettingsInterface) {
114
            $settings = Patron::getInstance()->providerSettings(
115
                $this->class,
116
                $this->getAttribute('settings')
117
            );
118
119
            $this->setAttribute('settings', $settings);
120
        }
121
122
        return $settings;
123
    }
124
125
126
    /**
127
     * @param $record
128
     * @param $row
129
     */
130
    public static function populateRecord($record, $row)
131
    {
132
        // Apply override settings
133
        if (null !== ($handle = $row['handle'] ?? null)) {
134
            $row = array_merge(
135
                $row,
136
                Patron::getInstance()->getSettings()->getProvider($handle)
137
            );
138
        }
139
140
        parent::populateRecord($record, $row);
141
    }
142
143
    /**
144
     * @inheritdoc
145
     */
146
    public function rules()
147
    {
148
        return array_merge(
149
            parent::rules(),
150
            $this->handleRules(),
151
            $this->stateRules(),
152
            [
153
                [
154
                    [
155
                        'clientId'
156
                    ],
157
                    'string',
158
                    'max' => static::CLIENT_ID_LENGTH
159
                ],
160
                [
161
                    [
162
                        'clientSecret'
163
                    ],
164
                    'string',
165
                    'max' => static::CLIENT_SECRET_LENGTH
166
                ],
167
                [
168
                    [
169
                        'class'
170
                    ],
171
                    ProviderValidator::class
172
                ],
173
                [
174
                    [
175
                        'settings'
176
                    ],
177
                    ProviderSettingsValidator::class
178
                ],
179
                [
180
                    [
181
                        'class',
182
                        'clientId'
183
                    ],
184
                    'required'
185
                ],
186
                [
187
                    [
188
                        'class',
189
                        'clientId',
190
                        'clientSecret',
191
                        'settings',
192
                    ],
193
                    'safe',
194
                    'on' => [
195
                        self::SCENARIO_DEFAULT
196
                    ]
197
                ]
198
            ]
199
        );
200
    }
201
202
203
    /*******************************************
204
     * RELATIONS
205
     *******************************************/
206
207
    /**
208
     * Get all of the associated tokens.
209
     *
210
     * @param array $config
211
     * @return \yii\db\ActiveQuery
212
     */
213
    public function getTokens(array $config = [])
214
    {
215
        $query = $this->hasMany(
216
            Token::class,
217
            ['providerId' => 'id']
218
        );
219
220
        if (!empty($config)) {
221
            QueryHelper::configure(
222
                $query,
223
                $config
224
            );
225
        }
226
227
        return $query;
228
    }
229
230
    /**
231
     * Get all of the associated tokens.
232
     *
233
     * @param array $config
234
     * @return \yii\db\ActiveQuery
235
     */
236
    public function getLocks(array $config = [])
237
    {
238
        $query = $this->hasMany(
239
            ProviderLock::class,
240
            ['providerId' => 'id']
241
        );
242
243
        if (!empty($config)) {
244
            QueryHelper::configure(
245
                $query,
246
                $config
247
            );
248
        }
249
250
        return $query;
251
    }
252
253
    /*******************************************
254
     * SAVE
255
     *******************************************/
256
257
    /**
258
     * @param PluginInterface $plugin
259
     * @param bool $runValidation
260
     * @param null $attributeNames
261
     * @return bool
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
     */
281
    public function addLock(PluginInterface $plugin): bool
282
    {
283
        if (null === ($pluginId = $this->getPluginId($plugin))) {
284
            return false;
285
        }
286
287
        $record = ProviderLock::findOne([
288
            'providerId' => $this->getId(),
289
            'pluginId' => $pluginId
290
        ]);
291
292
        if (!empty($record)) {
293
            return true;
294
        }
295
296
        $record = new ProviderLock();
297
298
        $record->setAttributes([
299
            'providerId' => $this->getId(),
300
            'pluginId' => $pluginId
301
        ]);
302
303
        return (bool)$record->save();
304
    }
305
306
    /**
307
     * @param PluginInterface $plugin
308
     * @return bool
309
     * @throws \Throwable
310
     */
311
    public function removeLock(PluginInterface $plugin): bool
312
    {
313
        if (null === ($pluginId = $this->getPluginId($plugin))) {
314
            return false;
315
        }
316
317
        if (null === ($record = ProviderLock::findOne([
318
                'providerId' => $this->getId() ?: 0,
319
                'pluginId' => $pluginId
320
            ]))) {
321
            return true;
322
        }
323
324
        return (bool)$record->delete();
325
    }
326
327
    /**
328
     * @return bool
329
     */
330
    public function isLocked(): bool
331
    {
332
        return !empty($this->locks);
333
    }
334
335
    /**
336
     * @param PluginInterface $plugin
337
     * @return int|null
338
     */
339
    protected function getPluginId(PluginInterface $plugin)
340
    {
341
        $id = (new Query())
342
            ->select([
343
                'id',
344
            ])
345
            ->from(['{{%plugins}}'])
346
            ->where([
347
                'handle' => $plugin->getHandle()
348
            ])
349
            ->scalar();
350
351
        return $id ? (int)$id : null;
352
    }
353
354
    /**
355
     * @param PluginInterface $plugin
356
     * @return int|null
357
     */
358
    protected function getPluginName(PluginInterface $plugin)
359
    {
360
        $id = (new Query())
361
            ->select([
362
                'id',
363
            ])
364
            ->from(['{{%plugins}}'])
365
            ->where([
366
                'handle' => $plugin->getHandle()
367
            ])
368
            ->scalar();
369
370
        return $id ? (int)$id : null;
371
    }
372
373
374
    /*******************************************
375
     * DELETE
376
     *******************************************/
377
378
    /**
379
     * @param PluginInterface|null $plugin
380
     * @return bool|false|int
381
     * @throws \Throwable
382
     * @throws \yii\db\StaleObjectException
383
     */
384
    public function delete(PluginInterface $plugin = null)
385
    {
386
        return $this->canDelete($plugin) ? parent::delete() : false;
387
    }
388
389
    /**
390
     * @param PluginInterface|null $plugin
391
     * @return bool
392
     * @throws \craft\errors\InvalidPluginException
393
     */
394
    protected function canDelete(PluginInterface $plugin = null)
395
    {
396
        // If a plugin is locking this, prevent deletion
397
        $lockQuery = $this->getLocks();
398
        if (null !== $plugin) {
399
            $lockQuery->andWhere(
400
                ['<>', 'pluginId', $this->getPluginId($plugin)]
401
            );
402
        }
403
404
        $locks = $lockQuery->all();
405
406
        if (count($locks) > 0) {
407
            $handles = (new Query())
408
                ->select([
409
                    'handle',
410
                ])
411
                ->from(['{{%plugins}}'])
412
                ->where([
413
                    'id' => ArrayHelper::getColumn($locks, 'pluginId'),
414
                ])
415
                ->column();
416
417
            $names = [];
418
            foreach ($handles as $handle) {
419
                $plugin = Craft::$app->getPlugins()->getPluginInfo($handle);
420
                $names[] = $plugin['name'] ?? 'Unknown Plugin';
421
            }
422
423
            $this->addError(
424
                'locks',
425
                Craft::t(
426
                    'patron',
427
                    'The provider is locked by the following plugins: {plugins}',
428
                    [
429
                        'plugins' => StringHelper::toString($names, ', ')
430
                    ]
431
                )
432
            );
433
            return false;
434
        }
435
436
        return true;
437
    }
438
439
440
    /*******************************************
441
     * PROJECT CONFIG
442
     *******************************************/
443
444
    /**
445
     * Return an array suitable for Craft's Project config
446
     */
447
    public function toProjectConfig(): array
448
    {
449
        return $this->toArray([
450
            'handle',
451
            'clientId',
452
            'clientSecret',
453
            'class',
454
            'scopes',
455
            'enabled',
456
            'dateUpdated'
457
        ]);
458
    }
459
}
460