User::getSearchKeywords()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 11
ccs 0
cts 8
cp 0
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 2
crap 6
1
<?php
2
3
/**
4
 * @copyright  Copyright (c) Flipbox Digital Limited
5
 * @license    https://flipboxfactory.com/software/organization/license
6
 * @link       https://www.flipboxfactory.com/software/organization/
7
 */
8
9
namespace flipbox\organization\fields;
10
11
use Craft;
12
use craft\base\EagerLoadingFieldInterface;
13
use craft\base\Element;
14
use craft\base\ElementInterface;
15
use craft\base\Field;
16
use craft\base\PreviewableFieldInterface;
17
use craft\db\Query;
18
use craft\elements\db\ElementQuery;
19
use craft\elements\db\ElementQueryInterface;
20
use craft\elements\User as UserElement;
21
use craft\helpers\ElementHelper;
22
use craft\helpers\StringHelper;
23
use craft\validators\ArrayValidator;
24
use flipbox\organization\elements\db\Organization as OrganizationQuery;
25
use flipbox\organization\elements\Organization as OrganizationElement;
26
use flipbox\organization\helpers\Query as QueryHelper;
27
use flipbox\organization\Organization as OrganizationPlugin;
28
use flipbox\organization\records\User as OrganizationUserRecord;
29
use flipbox\organization\queue\jobs\LocalizeRelations;
30
use flipbox\organization\validators\User as UserValidator;
31
use yii\base\Exception;
32
33
/**
34
 * @author Flipbox Factory <[email protected]>
35
 * @since 1.0.0
36
 */
37
class User extends Field implements PreviewableFieldInterface, EagerLoadingFieldInterface
38
{
39
40
    /**
41
     * @inheritdoc
42
     */
43
    public static function displayName(): string
44
    {
45
        return Craft::t('app', 'User Organizations');
46
    }
47
48
    /**
49
     * @inheritdoc
50
     */
51
    public static function hasContentColumn(): bool
52
    {
53
        return false;
54
    }
55
56
    /**
57
     * @inheritdoc
58
     */
59
    public static function supportedTranslationMethods(): array
60
    {
61
        // Don't ever automatically propagate values to other sites.
62
        return [
63
            self::TRANSLATION_METHOD_SITE,
64
        ];
65
    }
66
67
    /**
68
     * @inheritdoc
69
     */
70
    protected static function elementType(): string
71
    {
72
        return OrganizationElement::class;
73
    }
74
75
    /**
76
     * Returns the default [[selectionLabel]] value.
77
     *
78
     * @return string The default selection label
79
     */
80
    public static function defaultSelectionLabel(): string
81
    {
82
        return Craft::t('app', 'Select organization');
83
    }
84
85
    // Properties
86
    // =========================================================================
87
88
    /**
89
     * @var string|string[]|null The source keys that this field can
90
     * relate elements from (used if [[allowMultipleSources]] is set to true)
91
     */
92
    public $sources = '*';
93
94
    /**
95
     * @var string|null The source key that this field can
96
     * relate elements from (used if [[allowMultipleSources]] is set to false)
97
     */
98
    public $source;
99
100
    /**
101
     * @var int|null The site that this field should relate elements from
102
     */
103
    public $targetSiteId;
104
105
    /**
106
     * @var string|null The view mode
107
     */
108
    public $viewMode;
109
110
    /**
111
     * @var int|null The maximum number of relations this field can have (used if [[allowLimit]] is set to true)
112
     */
113
    public $limit;
114
115
    /**
116
     * @var string|null The label that should be used on the selection input
117
     */
118
    public $selectionLabel;
119
120
    /**
121
     * @var int Whether each site should get its own unique set of relations
122
     */
123
    public $localizeRelations = false;
124
125
    /**
126
     * @var bool Whether to allow multiple source selection in the settings
127
     */
128
    public $allowMultipleSources = true;
129
130
    /**
131
     * @var bool Whether to allow the Limit setting
132
     */
133
    public $allowLimit = true;
134
135
    /**
136
     * @var bool Whether to allow the “Large Thumbnails” view mode
137
     */
138
    protected $allowLargeThumbsView = false;
139
140
    /**
141
     * @var string Temlpate to use for settings rendering
142
     */
143
    protected $settingsTemplate = '_components/fieldtypes/elementfieldsettings';
144
145
    /**
146
     * @var string Template to use for field rendering
147
     */
148
    protected $inputTemplate = '_includes/forms/elementSelect';
149
150
    /**
151
     * @var string|null The JS class that should be initialized for the input
152
     */
153
    protected $inputJsClass;
154
155
    /**
156
     * @var bool Whether the elements have a custom sort order
157
     */
158
    protected $sortable = true;
159
160
    /**
161
     * @var bool Whether existing relations should be made translatable after the field is saved
162
     */
163
    private $makeExistingRelationsTranslatable = false;
164
165
    /**
166
     * @inheritdoc
167
     */
168
    public function init()
169
    {
170
171
        parent::init();
172
173
        // Not possible to have no sources selected
174
        if (!$this->sources) {
175
            $this->sources = '*';
176
        }
177
178
//        // Restrict limits
179
//        if(OrganizationPlugin::getInstance()->getSettings()->singleUser) {
180
//            $this->allowLimit = true;
181
//            $this->limit = 1;
182
//        }
183
    }
184
185
    /**
186
     * @inheritdoc
187
     */
188
    public function settingsAttributes(): array
189
    {
190
        $attributes = parent::settingsAttributes();
191
        $attributes[] = 'sources';
192
        $attributes[] = 'source';
193
        $attributes[] = 'targetSiteId';
194
        $attributes[] = 'viewMode';
195
        $attributes[] = 'limit';
196
        $attributes[] = 'selectionLabel';
197
        $attributes[] = 'localizeRelations';
198
199
        return $attributes;
200
    }
201
202
    /**
203
     * @inheritdoc
204
     */
205
    public function getSettingsHtml()
206
    {
207
        return Craft::$app->getView()->renderTemplate($this->settingsTemplate, [
208
            'field' => $this,
209
        ]);
210
    }
211
212
    /**
213
     * @inheritdoc
214
     */
215
    public function getElementValidationRules(): array
216
    {
217
        return [
218
            [
219
                ArrayValidator::class,
220
                'min' => $this->required ? 1 : null,
221
                'max' => $this->allowLimit && $this->limit ? $this->limit : null,
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->limit of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

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

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
222
                'tooFew' => Craft::t(
223
                    'app',
224
                    '{attribute} should contain at least {min, number} '.
225
                    '{min, plural, one{selection} other{selections}}.'
226
                ),
227
                'tooMany' => Craft::t(
228
                    'app',
229
                    '{attribute} should contain at most {max, number} '.
230
                    '{max, plural, one{selection} other{selections}}.'
231
                ),
232
            ],
233
            [
234
                UserValidator::class
235
            ]
236
        ];
237
    }
238
239
    /**
240
     * @inheritdoc
241
     */
242
    public function normalizeValue($value, ElementInterface $element = null)
243
    {
244
245
        if ($value instanceof ElementQueryInterface) {
246
            return $value;
247
        }
248
249
        /** @var Element $element */
250
        /** @var Element $class */
251
        $class = static::elementType();
252
        /** @var OrganizationQuery $query */
253
        $query = $class::find()
254
            ->siteId($this->targetSiteId($element));
255
256
        // $value will be an array of element IDs if there was a validation error or we're loading a draft/version.
257
        if (is_array($value)) {
258
            $query
259
                ->id(array_values(array_filter($value)))
260
                ->fixedOrder();
261
        } elseif ($value !== '' && !empty($element->id)) {
1 ignored issue
show
Bug introduced by
Accessing id on the interface craft\base\ElementInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
262
            $query->user($element);
0 ignored issues
show
Documentation introduced by
$element is of type object<craft\base\ElementInterface>, but the function expects a string|array<integer,str...t<craft\elements\User>>.

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...
263
264
            if ($this->sortable) {
265
                $query->orderBy(['sortOrder' => SORT_ASC]);
266
            }
267
268
            if (!$this->allowMultipleSources && $this->source) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->source of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null 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...
269
                $source = ElementHelper::findSource($class, $this->source);
270
271
                // Does the source specify any criteria attributes?
272
                if (isset($source['criteria'])) {
273
                    Craft::configure($query, $source['criteria']);
274
                }
275
            }
276
        } else {
277
            $query->id(false);
278
        }
279
280
        if ($this->allowLimit && $this->limit) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->limit of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

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

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
281
            $query->limit($this->limit);
282
        }
283
284
        return $query;
285
    }
286
287
    /**
288
     * @inheritdoc
289
     */
290
    public function modifyElementsQuery(ElementQueryInterface $query, $value)
291
    {
292
293
        if (null === $value) {
294
            return null;
295
        }
296
297
        // String = members
298
        if (is_string($value) || is_numeric($value)) {
299
            $value = ['member' => $value];
300
        }
301
302
        QueryHelper::applyOrganizationParam(
303
            $query,
304
            $value
0 ignored issues
show
Bug introduced by
It seems like $value defined by parameter $value on line 290 can also be of type boolean or null or object; however, flipbox\organization\hel...pplyOrganizationParam() does only seem to accept array, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
305
        );
306
307
        return null;
308
    }
309
310
    /**
311
     * @inheritdoc
312
     */
313
    public function getIsTranslatable(ElementInterface $element = null): bool
314
    {
315
        return $this->localizeRelations;
316
    }
317
318
    /**
319
     * @inheritdoc
320
     */
321
    public function getInputHtml($value, ElementInterface $element = null): string
322
    {
323
        /** @var Element $element */
324
        if ($element !== null && $element->hasEagerLoadedElements($this->handle)) {
325
            $value = $element->getEagerLoadedElements($this->handle);
326
        }
327
328
        /** @var ElementQuery|array $value */
329
        $variables = $this->inputTemplateVariables($value, $element);
330
331
        return Craft::$app->getView()->renderTemplate($this->inputTemplate, $variables);
332
    }
333
334
    /**
335
     * @inheritdoc
336
     */
337
    public function getSearchKeywords($value, ElementInterface $element): string
338
    {
339
        /** @var ElementQuery $value */
340
        $titles = [];
341
342
        foreach ($value->all() as $relatedElement) {
343
            $titles[] = (string)$relatedElement;
344
        }
345
346
        return parent::getSearchKeywords($titles, $element);
347
    }
348
349
    /**
350
     * @inheritdoc
351
     */
352
    public function getStaticHtml($value, ElementInterface $element): string
353
    {
354
        /** @var ElementQuery $value */
355
        if (count($value)) {
356
            $html = '<div class="elementselect"><div class="elements">';
357
358
            foreach ($value->all() as $relatedElement) {
359
                $html .= Craft::$app->getView()->renderTemplate(
360
                    '_elements/element',
361
                    [
362
                        'element' => $relatedElement
363
                    ]
364
                );
365
            }
366
367
            $html .= '</div></div>';
368
369
            return $html;
370
        }
371
372
        return '<p class="light">' . Craft::t('app', 'Nothing selected.') . '</p>';
373
    }
374
375
    /**
376
     * @inheritdoc
377
     */
378
    public function getTableAttributeHtml($value, ElementInterface $element): string
379
    {
380
        if ($value instanceof ElementQueryInterface) {
381
            $element = $value->first();
382
        } else {
383
            $element = $value[0] ?? null;
384
        }
385
386
        if ($element) {
387
            return Craft::$app->getView()->renderTemplate('_elements/element', [
388
                'element' => $element
389
            ]);
390
        }
391
392
        return '';
393
    }
394
395
    /**
396
     * @inheritdoc
397
     */
398
    public function getEagerLoadingMap(array $sourceElements)
399
    {
400
        /** @var Element|null $firstElement */
401
        $firstElement = $sourceElements[0] ?? null;
402
403
        // Get the source element IDs
404
        $sourceElementIds = [];
405
406
        foreach ($sourceElements as $sourceElement) {
407
            $sourceElementIds[] = $sourceElement->id;
408
        }
409
410
        // Return any relation data on these elements, defined with this field
411
        $map = (new Query())
412
            ->select(['userId as source', 'organizationId as target'])
413
            ->from([OrganizationUserRecord::tableName()])
414
            ->where([
415
                'and',
416
                [
417
                    'userId' => $sourceElementIds,
418
                ],
419
                [
420
                    'or',
421
                    ['siteId' => $firstElement ? $firstElement->siteId : null],
422
                    ['siteId' => null]
423
                ]
424
            ])
425
            ->orderBy(['sortOrder' => SORT_ASC])
426
            ->all();
427
428
        // Figure out which target site to use
429
        $targetSite = $this->targetSiteId($firstElement);
430
431
        return [
432
            'elementType' => static::elementType(),
433
            'map' => $map,
434
            'criteria' => [
435
                'siteId' => $targetSite
436
            ],
437
        ];
438
    }
439
440
    // Events
441
    // -------------------------------------------------------------------------
442
443
    /**
444
     * @inheritdoc
445
     */
446
    public function beforeSave(bool $isNew): bool
447
    {
448
        $this->makeExistingRelationsTranslatable = false;
449
450
        if ($this->id && $this->localizeRelations) {
451
            /** @var Field $existingField */
452
            $existingField = Craft::$app->getFields()->getFieldById($this->id);
453
454
            if ($existingField && $existingField instanceof User && !$existingField->localizeRelations) {
455
                $this->makeExistingRelationsTranslatable = true;
456
            }
457
        }
458
459
        return parent::beforeSave($isNew);
460
    }
461
462
    /**
463
     * @inheritdoc
464
     */
465
    public function afterSave(bool $isNew)
466
    {
467
        if ($this->makeExistingRelationsTranslatable) {
468
            Craft::$app->getQueue()->push(new LocalizeRelations([
469
                'userId' => $this->id,
470
            ]));
471
        }
472
473
        parent::afterSave($isNew);
474
    }
475
476
    /**
477
     * @inheritdoc
478
     */
479
    public function beforeElementSave(ElementInterface $element, bool $isNew): bool
480
    {
481
482
        /** @var Element $element */
483
484
        if (!$element instanceof UserElement) {
485
            $element->addError(
486
                $this->handle,
487
                Craft::t('organization', 'Field can only be used with the User element.')
488
            );
489
490
            return false;
491
        }
492
493
        // Save relations
494
        OrganizationPlugin::getInstance()->getField()->beforeSaveUserRelations(
495
            $this,
496
            $element,
497
            $element->getFieldValue($this->handle)
498
        );
499
500
        return parent::beforeElementSave($element, $isNew);
501
    }
502
503
    /**
504
     * @inheritdoc
505
     */
506
    public function afterElementSave(ElementInterface $element, bool $isNew)
507
    {
508
509
        // Double check
510
        if (!$element instanceof UserElement) {
511
            throw new Exception("Invalid element type");
512
        }
513
514
        // Save relations
515
        OrganizationPlugin::getInstance()->getField()->afterSaveUserRelations(
516
            $this,
517
            $element,
518
            $element->getFieldValue($this->handle)
519
        );
520
521
        parent::afterElementSave($element, $isNew);
522
    }
523
524
    /**
525
     * Normalizes the available sources into select input options.
526
     *
527
     * @return array
528
     */
529
    public function getSourceOptions(): array
530
    {
531
        $options = [];
532
        $optionNames = [];
533
534
        foreach ($this->availableSources() as $source) {
535
            // Make sure it's not a heading
536
            if (!isset($source['heading'])) {
537
                $options[] = [
538
                    'label' => $source['label'],
539
                    'value' => $source['key']
540
                ];
541
                $optionNames[] = $source['label'];
542
            }
543
        }
544
545
        // Sort alphabetically
546
        array_multisort($optionNames, SORT_NATURAL | SORT_FLAG_CASE, $options);
547
548
        return $options;
549
    }
550
551
    /**
552
     * Returns the HTML for the Target Site setting.
553
     *
554
     * @return string|null
555
     */
556
    public function getTargetSiteFieldHtml()
557
    {
558
        /** @var Element $class */
559
        $class = static::elementType();
560
561
        if (Craft::$app->getIsMultiSite() && $class::isLocalized()) {
562
            $siteOptions = [
563
                ['label' => Craft::t('app', 'Same as source'), 'value' => null]
564
            ];
565
566
            foreach (Craft::$app->getSites()->getAllSites() as $site) {
567
                $siteOptions[] = [
568
                    'label' => Craft::t('site', $site->name),
569
                    'value' => $site->id
570
                ];
571
            }
572
573
            return Craft::$app->getView()->renderTemplateMacro(
574
                '_includes/forms',
575
                'selectField',
576
                [
577
                    [
578
                        'label' => Craft::t('app', 'Target Site'),
579
                        'instructions' => Craft::t(
580
                            'app',
581
                            'Which site do you want to select {type} in?',
582
                            [
583
                                    'type' => StringHelper::toLowerCase(static::displayName())
584
                                ]
585
                        ),
586
                        'id' => 'targetSiteId',
587
                        'name' => 'targetSiteId',
588
                        'options' => $siteOptions,
589
                        'value' => $this->targetSiteId
590
                    ]
591
                ]
592
            );
593
        }
594
595
        return null;
596
    }
597
598
    /**
599
     * Returns the HTML for the View Mode setting.
600
     *
601
     * @return string|null
602
     */
603
    public function getViewModeFieldHtml()
604
    {
605
        $supportedViewModes = $this->supportedViewModes();
606
607
        if (count($supportedViewModes) === 1) {
608
            return null;
609
        }
610
611
        $viewModeOptions = [];
612
613
        foreach ($supportedViewModes as $key => $label) {
614
            $viewModeOptions[] = ['label' => $label, 'value' => $key];
615
        }
616
617
        return Craft::$app->getView()->renderTemplateMacro('_includes/forms', 'selectField', [
618
            [
619
                'label' => Craft::t('app', 'View Mode'),
620
                'instructions' => Craft::t('app', 'Choose how the field should look for authors.'),
621
                'id' => 'viewMode',
622
                'name' => 'viewMode',
623
                'options' => $viewModeOptions,
624
                'value' => $this->viewMode
625
            ]
626
        ]);
627
    }
628
629
    // Protected Methods
630
    // =========================================================================
631
632
    /**
633
     * Returns an array of variables that should be passed to the input template.
634
     *
635
     * @param ElementQueryInterface|array|null $value
636
     * @param ElementInterface|null $element
637
     *
638
     * @return array
639
     */
640
    protected function inputTemplateVariables($value = null, ElementInterface $element = null): array
641
    {
642
        if ($value instanceof ElementQueryInterface) {
643
            $value
644
                ->status(null)
645
                ->enabledForSite(false);
646
        } elseif (!is_array($value)) {
647
            /** @var Element $class */
648
            $class = static::elementType();
649
            $value = $class::find()
650
                ->id(false);
651
        }
652
653
        $selectionCriteria = $this->inputSelectionCriteria();
654
        $selectionCriteria['enabledForSite'] = null;
655
        $selectionCriteria['siteId'] = $this->targetSiteId($element);
656
657
        return [
658
            'jsClass' => $this->inputJsClass,
659
            'elementType' => static::elementType(),
660
            'id' => Craft::$app->getView()->formatInputId($this->handle),
661
            'fieldId' => $this->id,
662
            'storageKey' => 'field.' . $this->id,
663
            'name' => $this->handle,
664
            'elements' => $value,
665
            'sources' => $this->inputSources(),
666
            'criteria' => $selectionCriteria,
667
            'sourceElementId' => !empty($element->id) ? $element->id : null,
1 ignored issue
show
Bug introduced by
Accessing id on the interface craft\base\ElementInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
668
            'limit' => $this->allowLimit ? $this->limit : null,
669
            'viewMode' => $this->viewMode(),
670
            'selectionLabel' => $this->selectionLabel ?
671
                Craft::t('site', $this->selectionLabel) :
672
                static::defaultSelectionLabel()
673
        ];
674
    }
675
676
    /**
677
     * Returns an array of the source keys the field should be able to select elements from.
678
     *
679
     * @return array|string
680
     */
681
    protected function inputSources()
682
    {
683
        if ($this->allowMultipleSources) {
684
            $sources = $this->sources;
685
        } else {
686
            $sources = [$this->source];
687
        }
688
689
        return $sources;
690
    }
691
692
    /**
693
     * Returns any additional criteria parameters limiting which elements the field should be able to select.
694
     *
695
     * @return array
696
     */
697
    protected function inputSelectionCriteria(): array
698
    {
699
        return [];
700
    }
701
702
    /**
703
     * Returns the site ID that target elements should have.
704
     *
705
     * @param ElementInterface|null $element
706
     *
707
     * @return int
708
     */
709
    protected function targetSiteId(ElementInterface $element = null): int
710
    {
711
        /** @var Element|null $element */
712
        if (Craft::$app->getIsMultiSite()) {
713
            if ($this->targetSiteId) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->targetSiteId of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

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

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
714
                return $this->targetSiteId;
715
            }
716
717
            if ($element !== null) {
718
                return $element->siteId;
719
            }
720
        }
721
722
        return Craft::$app->getSites()->currentSite->id;
723
    }
724
725
    /**
726
     * Returns the field’s supported view modes.
727
     *
728
     * @return array
729
     */
730
    protected function supportedViewModes(): array
731
    {
732
        $viewModes = [
733
            'list' => Craft::t('app', 'List'),
734
        ];
735
736
        if ($this->allowLargeThumbsView) {
737
            $viewModes['large'] = Craft::t('app', 'Large Thumbnails');
738
        }
739
740
        return $viewModes;
741
    }
742
743
    /**
744
     * Returns the field’s current view mode.
745
     *
746
     * @return string
747
     */
748
    protected function viewMode(): string
749
    {
750
        $supportedViewModes = $this->supportedViewModes();
751
        $viewMode = $this->viewMode;
752
753
        if ($viewMode && isset($supportedViewModes[$viewMode])) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $viewMode of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null 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...
754
            return $viewMode;
755
        }
756
757
        return 'list';
758
    }
759
760
    /**
761
     * Returns the sources that should be available to choose from within the field's settings
762
     *
763
     * @return array
764
     */
765
    protected function availableSources(): array
766
    {
767
        return Craft::$app->getElementIndexes()->getSources(static::elementType(), 'modal');
768
    }
769
}
770