This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
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
|
|||
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)) { |
||
262 | $query->user($element); |
||
0 ignored issues
–
show
$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);
![]() |
|||
263 | |||
264 | if ($this->sortable) { |
||
265 | $query->orderBy(['sortOrder' => SORT_ASC]); |
||
266 | } |
||
267 | |||
268 | if (!$this->allowMultipleSources && $this->source) { |
||
0 ignored issues
–
show
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 For '' == false // true
'' == null // true
'ab' == false // false
'ab' == null // false
// It is often better to use strict comparison
'' === false // false
'' === null // false
![]() |
|||
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
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 For 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
![]() |
|||
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
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. ![]() |
|||
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, |
||
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
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 For 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
![]() |
|||
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
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 For '' == false // true
'' == null // true
'ab' == false // false
'ab' == null // false
// It is often better to use strict comparison
'' === false // false
'' === null // false
![]() |
|||
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 |
In PHP, under loose comparison (like
==
, or!=
, orswitch
conditions), values of different types might be equal.For
integer
values, zero is a special case, in particular the following results might be unexpected: