Completed
Push — develop ( 89848b...82354e )
by Nate
15:15
created

IntegrationField::getActionHtml()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 16
ccs 0
cts 8
cp 0
rs 9.7333
c 0
b 0
f 0
cc 2
nc 2
nop 2
crap 6
1
<?php
2
3
/**
4
 * @copyright  Copyright (c) Flipbox Digital Limited
5
 * @license    https://github.com/flipboxfactory/craft-integration/blob/master/LICENSE
6
 * @link       https://github.com/flipboxfactory/craft-integration/
7
 */
8
9
namespace flipbox\craft\integration\services;
10
11
use Craft;
12
use craft\base\ElementInterface;
13
use craft\base\FieldInterface;
14
use craft\helpers\Component as ComponentHelper;
15
use craft\helpers\StringHelper;
16
use flipbox\craft\integration\db\IntegrationAssociationQuery;
17
use flipbox\craft\integration\events\RegisterIntegrationFieldActionsEvent;
18
use flipbox\craft\integration\fields\actions\IntegrationActionInterface;
19
use flipbox\craft\integration\fields\actions\IntegrationItemActionInterface;
20
use flipbox\craft\integration\fields\Integrations;
21
use flipbox\craft\integration\records\IntegrationAssociation;
22
use flipbox\craft\integration\web\assets\integrations\Integrations as IntegrationsAsset;
23
use flipbox\craft\sortable\associations\db\SortableAssociationQueryInterface;
24
use flipbox\craft\sortable\associations\records\SortableAssociationInterface;
25
use flipbox\craft\sortable\associations\services\SortableFields;
26
use yii\base\Exception;
27
28
/**
29
 * @author Flipbox Factory <[email protected]>
30
 * @since 1.0.0
31
 */
32
abstract class IntegrationField extends SortableFields
33
{
34
    /**
35
     * @inheritdoc
36
     */
37
    const TARGET_ATTRIBUTE = IntegrationAssociation::TARGET_ATTRIBUTE;
38
39
    /**
40
     * @inheritdoc
41
     */
42
    const SOURCE_ATTRIBUTE = IntegrationAssociation::SOURCE_ATTRIBUTE;
43
44
    /**
45
     * @return IntegrationAssociations
46
     */
47
    abstract protected function associationService(): IntegrationAssociations;
48
49
    /**
50
     * @var Integrations[]
51
     */
52
    private $fields = [];
53
54
    /**
55
     * @var IntegrationActionInterface[]
56
     */
57
    protected $defaultAvailableActions = [];
58
59
    /**
60
     * @var IntegrationItemActionInterface[]
61
     */
62
    protected $defaultAvailableItemActions = [];
63
64
    /**
65
     * @param int $id
66
     * @return Integrations|null
67
     */
68
    public function findById(int $id)
69
    {
70
        if (!array_key_exists($id, $this->fields)) {
71
            $objectField = Craft::$app->getFields()->getFieldById($id);
72
            if (!$objectField instanceof Integrations) {
73
                $objectField = null;
74
            }
75
76
            $this->fields[$id] = $objectField;
77
        }
78
79
        return $this->fields[$id];
80
    }
81
82
83
    /**
84
     * @inheritdoc
85
     * @param Integrations $field
86
     * @return IntegrationAssociationQuery
87
     * @throws Exception
88
     */
89
    public function getQuery(
90
        FieldInterface $field,
91
        ElementInterface $element = null
92
    ): SortableAssociationQueryInterface {
93
        $query = $this->baseQuery($field, $element);
94
95
        /** @var Integrations $field */
96
97
        if ($field->max !== null) {
98
            $query->limit($field->max);
99
        }
100
101
        return $query;
102
    }
103
104
    /**
105
     * @param FieldInterface $field
106
     * @param ElementInterface|null $element
107
     * @return IntegrationAssociationQuery
108
     * @throws Exception
109
     */
110
    private function baseQuery(
111
        FieldInterface $field,
112
        ElementInterface $element = null
113
    ): IntegrationAssociationQuery {
114
        if (!$field instanceof Integrations) {
115
            throw new Exception(sprintf(
116
                "The field must be an instance of '%s', '%s' given.",
117
                (string)Integrations::class,
118
                (string)get_class($field)
119
            ));
120
        }
121
122
        /** @var IntegrationAssociationQuery $query */
123
        $query = $this->associationService()->getQuery()
124
            ->field($field->id)
125
            ->site($this->targetSiteId($element));
126
127
        $query->element = $element;
128
129
        return $query;
130
    }
131
132
133
    /*******************************************
134
     * NORMALIZE VALUE
135
     *******************************************/
136
137
    /**
138
     * @inheritdoc
139
     * @throws \Throwable
140
     */
141
    protected function normalizeQueryInputValue(
142
        FieldInterface $field,
143
        $value,
144
        int &$sortOrder,
145
        ElementInterface $element = null
146
    ): SortableAssociationInterface {
147
        if (!$field instanceof Integrations) {
148
            throw new Exception(sprintf(
149
                "The field must be an instance of '%s', '%s' given.",
150
                (string)Integrations::class,
151
                (string)get_class($field)
152
            ));
153
        }
154
155
        if (is_array($value)) {
156
            $value = StringHelper::toString($value);
157
        }
158
159
        return $this->associationService()->create(
160
            [
161
                'field' => $field,
162
                'element' => $element,
163
                'objectId' => $value,
164
                'siteId' => $this->targetSiteId($element),
165
                'sortOrder' => $sortOrder++
166
            ]
167
        );
168
    }
169
170
    /**
171
     * @param Integrations $field
172
     * @param IntegrationAssociationQuery $query
173
     * @param ElementInterface|null $element
174
     * @param bool $static
175
     * @return null|string
176
     * @throws Exception
177
     * @throws \Twig_Error_Loader
178
     */
179
    public function getInputHtml(
180
        Integrations $field,
181
        IntegrationAssociationQuery $query,
182
        ElementInterface $element = null,
183
        bool $static
184
    ) {
185
        Craft::$app->getView()->registerAssetBundle(IntegrationsAsset::class);
186
187
        return Craft::$app->getView()->renderTemplate(
188
            $field::INPUT_TEMPLATE_PATH,
189
            $this->inputHtmlVariables($field, $query, $element, $static)
190
        );
191
    }
192
193
    /**
194
     * @param Integrations $field
195
     * @param IntegrationAssociationQuery $query
196
     * @param ElementInterface|null $element
197
     * @param bool $static
198
     * @return array
199
     * @throws \craft\errors\MissingComponentException
200
     * @throws \yii\base\InvalidConfigException
201
     */
202
    protected function inputHtmlVariables(
203
        Integrations $field,
204
        IntegrationAssociationQuery $query,
205
        ElementInterface $element = null,
206
        bool $static
207
    ): array {
208
        return [
209
            'field' => $field,
210
            'element' => $element,
211
            'value' => $query,
212
            'objectLabel' => $this->getObjectLabel($field),
213
            'static' => $static,
214
            'itemTemplate' => $field::INPUT_ITEM_TEMPLATE_PATH,
215
            'settings' => [
216
                'translationCategory' => $field::TRANSLATION_CATEGORY,
217
                'limit' => $field->max ? $field->max : null,
218
                'data' => [
219
                    'field' => $field->id,
220
                    'element' => $element ? $element->getId() : null
221
                ],
222
                'actions' => $this->getActionHtml($field, $element),
223
                'actionAction' => $field::ACTION_PREFORM_ACTION_PATH,
224
                'createItemAction' => $field::ACTION_CREATE_ITEM_PATH,
225
                'itemData' => [
226
                    'field' => $field->id,
227
                    'element' => $element ? $element->getId() : null
228
                ],
229
                'itemSettings' => [
230
                    'translationCategory' => $field::TRANSLATION_CATEGORY,
231
                    'actionAction' => $field::ACTION_PREFORM_ITEM_ACTION_PATH,
232
                    'associateAction' => $field::ACTION_ASSOCIATION_ITEM_PATH,
233
                    'dissociateAction' => $field::ACTION_DISSOCIATION_ITEM_PATH,
234
                    'data' => [
235
                        'field' => $field->id,
236
                        'element' => $element ? $element->getId() : null
237
                    ],
238
                    'actions' => $this->getItemActionHtml($field, $element),
239
                ]
240
            ]
241
        ];
242
    }
243
244
    /**
245
     * @param Integrations $field
246
     * @return string
247
     * @throws Exception
248
     * @throws \Twig_Error_Loader
249
     * @throws \craft\errors\MissingComponentException
250
     * @throws \yii\base\InvalidConfigException
251
     */
252
    public function getSettingsHtml(
253
        Integrations $field
254
    ) {
255
        return Craft::$app->getView()->renderTemplate(
256
            $field::SETTINGS_TEMPLATE_PATH,
257
            $this->settingsHtmlVariables($field)
258
        );
259
    }
260
261
    /**
262
     * @param Integrations $field
263
     * @return array
264
     * @throws \craft\errors\MissingComponentException
265
     * @throws \yii\base\InvalidConfigException
266
     */
267
    protected function settingsHtmlVariables(Integrations $field): array
268
    {
269
        return [
270
            'field' => $field,
271
            'availableActions' => $this->getAvailableActions($field),
272
            'availableItemActions' => $this->getAvailableItemActions($field),
273
            'translationCategory' => $field::TRANSLATION_CATEGORY,
274
        ];
275
    }
276
277
278
    /*******************************************
279
     * OBJECT
280
     *******************************************/
281
282
    /**
283
     * @param Integrations $field
284
     * @return string
285
     */
286
    public function getObjectLabel(Integrations $field): string
287
    {
288
        return StringHelper::titleize($field->object);
289
    }
290
291
    /*******************************************
292
     * ACTIONS
293
     *******************************************/
294
295
    /**
296
     * @param Integrations $field
297
     * @return IntegrationActionInterface[]
298
     * @throws \craft\errors\MissingComponentException
299
     * @throws \yii\base\InvalidConfigException
300
     */
301
    public function getAvailableActions(Integrations $field): array
302
    {
303
        $event = new RegisterIntegrationFieldActionsEvent([
304
            'actions' => $this->defaultAvailableActions
305
        ]);
306
307
        $field->trigger(
308
            $field::EVENT_REGISTER_AVAILABLE_ACTIONS,
309
            $event
310
        );
311
312
        return $this->resolveActions(
313
            array_filter((array)$event->actions),
314
            IntegrationActionInterface::class
315
        );
316
    }
317
318
    /**
319
     * @param Integrations $field
320
     * @param ElementInterface|null $element
321
     * @return IntegrationActionInterface[]
322
     * @throws \craft\errors\MissingComponentException
323
     * @throws \yii\base\InvalidConfigException
324
     */
325
    public function getActions(Integrations $field, ElementInterface $element = null): array
326
    {
327
        $event = new RegisterIntegrationFieldActionsEvent([
328
            'actions' => $field->selectedActions,
329
            'element' => $element
330
        ]);
331
332
        $field->trigger(
333
            $field::EVENT_REGISTER_ACTIONS,
334
            $event
335
        );
336
337
        return $this->resolveActions(
338
            array_filter((array)$event->actions),
339
            IntegrationActionInterface::class
340
        );
341
    }
342
343
    /**
344
     * @param Integrations $field
345
     * @return IntegrationActionInterface[]
346
     * @throws \craft\errors\MissingComponentException
347
     * @throws \yii\base\InvalidConfigException
348
     */
349
    public function getAvailableItemActions(Integrations $field): array
350
    {
351
        $event = new RegisterIntegrationFieldActionsEvent([
352
            'actions' => $this->defaultAvailableItemActions
353
        ]);
354
355
        $field->trigger(
356
            $field::EVENT_REGISTER_AVAILABLE_ITEM_ACTIONS,
357
            $event
358
        );
359
360
        return $this->resolveActions(
361
            array_filter((array)$event->actions),
362
            IntegrationItemActionInterface::class
363
        );
364
    }
365
366
    /**
367
     * @param Integrations $field
368
     * @param ElementInterface|null $element
369
     * @return IntegrationItemActionInterface[]
370
     * @throws \craft\errors\MissingComponentException
371
     * @throws \yii\base\InvalidConfigException
372
     */
373
    public function getItemActions(Integrations $field, ElementInterface $element = null): array
374
    {
375
        $event = new RegisterIntegrationFieldActionsEvent([
376
            'actions' => $field->selectedItemActions,
377
            'element' => $element
378
        ]);
379
380
        $field->trigger(
381
            $field::EVENT_REGISTER_ITEM_ACTIONS,
382
            $event
383
        );
384
385
        return $this->resolveActions(
386
            array_filter((array)$event->actions),
387
            IntegrationItemActionInterface::class
388
        );
389
    }
390
391
    /**
392
     * @param array $actions
393
     * @param string $instance
394
     * @return array
395
     * @throws \craft\errors\MissingComponentException
396
     * @throws \yii\base\InvalidConfigException
397
     */
398
    protected function resolveActions(array $actions, string $instance)
399
    {
400
        foreach ($actions as $i => $action) {
401
            // $action could be a string or config array
402
            if (!$action instanceof $instance) {
403
                $actions[$i] = $action = ComponentHelper::createComponent($action, $instance);
404
405
                if ($actions[$i] === null) {
406
                    unset($actions[$i]);
407
                }
408
            }
409
        }
410
411
        return array_values($actions);
412
    }
413
414
    /**
415
     * @param Integrations $field
416
     * @param ElementInterface|null $element
417
     * @return array
418
     * @throws \craft\errors\MissingComponentException
419
     * @throws \yii\base\InvalidConfigException
420
     */
421
    protected function getActionHtml(Integrations $field, ElementInterface $element = null): array
422
    {
423
        $actionData = [];
424
425
        foreach ($this->getActions($field, $element) as $action) {
426
            $actionData[] = [
427
                'type' => get_class($action),
428
                'destructive' => $action->isDestructive(),
429
                'name' => $action->getTriggerLabel(),
430
                'trigger' => $action->getTriggerHtml(),
431
                'confirm' => $action->getConfirmationMessage(),
432
            ];
433
        }
434
435
        return $actionData;
436
    }
437
438
    /**
439
     * @param Integrations $field
440
     * @param ElementInterface|null $element
441
     * @return array
442
     * @throws \craft\errors\MissingComponentException
443
     * @throws \yii\base\InvalidConfigException
444
     */
445
    protected function getItemActionHtml(Integrations $field, ElementInterface $element = null): array
446
    {
447
        $actionData = [];
448
449
        foreach ($this->getItemActions($field, $element) as $action) {
450
            $actionData[] = [
451
                'type' => get_class($action),
452
                'destructive' => $action->isDestructive(),
453
                'name' => $action->getTriggerLabel(),
454
                'trigger' => $action->getTriggerHtml(),
455
                'confirm' => $action->getConfirmationMessage(),
456
            ];
457
        }
458
459
        return $actionData;
460
    }
461
}
462