1 | <?php |
||||||
2 | /** |
||||||
3 | * Image Optimize plugin for Craft CMS |
||||||
4 | * |
||||||
5 | * Automatically optimize images after they've been transformed |
||||||
6 | * |
||||||
7 | * @link https://nystudio107.com |
||||||
0 ignored issues
–
show
Coding Style
introduced
by
![]() |
|||||||
8 | * @copyright Copyright (c) 2017 nystudio107 |
||||||
0 ignored issues
–
show
|
|||||||
9 | */ |
||||||
0 ignored issues
–
show
|
|||||||
10 | |||||||
11 | namespace nystudio107\imageoptimize\fields; |
||||||
12 | |||||||
13 | use Craft; |
||||||
14 | use craft\base\ElementInterface; |
||||||
0 ignored issues
–
show
The type
craft\base\ElementInterface was not found. Maybe you did not declare it correctly or list all dependencies?
The issue could also be caused by a filter entry in the build configuration.
If the path has been excluded in your configuration, e.g. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths ![]() |
|||||||
15 | use craft\base\Field; |
||||||
16 | use craft\elements\Asset; |
||||||
17 | use craft\fields\Matrix; |
||||||
18 | use craft\helpers\Html; |
||||||
19 | use craft\helpers\Json; |
||||||
20 | use craft\models\FieldLayout; |
||||||
21 | use craft\models\Volume; |
||||||
22 | use craft\validators\ArrayValidator; |
||||||
23 | use GraphQL\Type\Definition\Type; |
||||||
24 | use nystudio107\imageoptimize\assetbundles\imageoptimize\ImageOptimizeAsset; |
||||||
25 | use nystudio107\imageoptimize\fields\OptimizedImages as OptimizedImagesField; |
||||||
26 | use nystudio107\imageoptimize\gql\types\generators\OptimizedImagesGenerator; |
||||||
27 | use nystudio107\imageoptimize\ImageOptimize; |
||||||
28 | use nystudio107\imageoptimize\models\OptimizedImage; |
||||||
29 | use nystudio107\imageoptimize\models\Settings; |
||||||
30 | use ReflectionClass; |
||||||
31 | use ReflectionException; |
||||||
32 | use yii\base\InvalidConfigException; |
||||||
33 | use yii\db\Exception; |
||||||
34 | use yii\db\Schema; |
||||||
35 | use function is_array; |
||||||
36 | use function is_string; |
||||||
37 | |||||||
38 | /** @noinspection MissingPropertyAnnotationsInspection */ |
||||||
0 ignored issues
–
show
|
|||||||
39 | |||||||
40 | /** |
||||||
0 ignored issues
–
show
|
|||||||
41 | * @author nystudio107 |
||||||
0 ignored issues
–
show
Content of the @author tag must be in the form "Display Name <[email protected]>"
![]() |
|||||||
42 | * @package ImageOptimize |
||||||
0 ignored issues
–
show
|
|||||||
43 | * @since 1.2.0 |
||||||
0 ignored issues
–
show
|
|||||||
44 | */ |
||||||
0 ignored issues
–
show
|
|||||||
45 | class OptimizedImages extends Field |
||||||
46 | { |
||||||
47 | // Constants |
||||||
48 | // ========================================================================= |
||||||
49 | |||||||
50 | public const DEFAULT_ASPECT_RATIOS = [ |
||||||
51 | ['x' => 16, 'y' => 9], |
||||||
52 | ]; |
||||||
53 | public const DEFAULT_IMAGE_VARIANTS = [ |
||||||
54 | [ |
||||||
55 | 'width' => 1200, |
||||||
56 | 'useAspectRatio' => true, |
||||||
57 | 'aspectRatioX' => 16.0, |
||||||
58 | 'aspectRatioY' => 9.0, |
||||||
59 | 'retinaSizes' => ['1'], |
||||||
60 | 'quality' => 82, |
||||||
61 | 'format' => 'jpg', |
||||||
62 | ], |
||||||
63 | ]; |
||||||
64 | |||||||
65 | public const MAX_VOLUME_SUBFOLDERS = 30; |
||||||
66 | |||||||
67 | // Public Properties |
||||||
68 | // ========================================================================= |
||||||
69 | |||||||
70 | /** |
||||||
0 ignored issues
–
show
|
|||||||
71 | * @var array |
||||||
72 | */ |
||||||
73 | public array $fieldVolumeSettings = []; |
||||||
74 | |||||||
75 | /** |
||||||
0 ignored issues
–
show
|
|||||||
76 | * @var array |
||||||
77 | */ |
||||||
78 | public array $ignoreFilesOfType = []; |
||||||
79 | |||||||
80 | /** |
||||||
0 ignored issues
–
show
|
|||||||
81 | * @var bool |
||||||
82 | */ |
||||||
83 | public bool $displayOptimizedImageVariants = true; |
||||||
84 | |||||||
85 | /** |
||||||
0 ignored issues
–
show
|
|||||||
86 | * @var bool |
||||||
87 | */ |
||||||
88 | public bool $displayDominantColorPalette = true; |
||||||
89 | |||||||
90 | /** |
||||||
0 ignored issues
–
show
|
|||||||
91 | * @var bool |
||||||
92 | */ |
||||||
93 | public bool $displayLazyLoadPlaceholderImages = true; |
||||||
94 | |||||||
95 | /** |
||||||
0 ignored issues
–
show
|
|||||||
96 | * @var array |
||||||
97 | */ |
||||||
98 | public array $variants = []; |
||||||
99 | |||||||
100 | // Private Properties |
||||||
101 | // ========================================================================= |
||||||
102 | |||||||
103 | /** |
||||||
0 ignored issues
–
show
|
|||||||
104 | * @var array |
||||||
105 | */ |
||||||
106 | private array $aspectRatios = []; |
||||||
0 ignored issues
–
show
|
|||||||
107 | |||||||
108 | // Static Methods |
||||||
109 | // ========================================================================= |
||||||
110 | |||||||
111 | /** |
||||||
0 ignored issues
–
show
|
|||||||
112 | * @inheritdoc |
||||||
113 | */ |
||||||
114 | public function __construct(array $config = []) |
||||||
115 | { |
||||||
116 | // Unset any deprecated properties |
||||||
117 | if (!empty($config)) { |
||||||
118 | unset($config['transformMethod'], $config['imgixDomain']); |
||||||
119 | } |
||||||
120 | parent::__construct($config); |
||||||
121 | } |
||||||
122 | |||||||
123 | /** |
||||||
0 ignored issues
–
show
|
|||||||
124 | * @inheritdoc |
||||||
125 | */ |
||||||
0 ignored issues
–
show
|
|||||||
126 | public static function displayName(): string |
||||||
127 | { |
||||||
128 | return 'OptimizedImages'; |
||||||
129 | } |
||||||
130 | |||||||
131 | /** |
||||||
0 ignored issues
–
show
|
|||||||
132 | * @inheritdoc |
||||||
133 | */ |
||||||
0 ignored issues
–
show
|
|||||||
134 | public static function icon(): string |
||||||
135 | { |
||||||
136 | return '@nystudio107/imageoptimize/icon-mask.svg'; |
||||||
137 | } |
||||||
138 | |||||||
139 | /** |
||||||
0 ignored issues
–
show
|
|||||||
140 | * @inheritdoc |
||||||
141 | */ |
||||||
0 ignored issues
–
show
|
|||||||
142 | public static function phpType(): string |
||||||
143 | { |
||||||
144 | return sprintf('\\%s', OptimizedImage::class); |
||||||
145 | } |
||||||
146 | |||||||
147 | /** |
||||||
0 ignored issues
–
show
|
|||||||
148 | * @inheritdoc |
||||||
149 | */ |
||||||
0 ignored issues
–
show
|
|||||||
150 | public static function dbType(): array|string|null |
||||||
151 | { |
||||||
152 | return Schema::TYPE_TEXT; |
||||||
153 | } |
||||||
154 | |||||||
155 | // Public Methods |
||||||
156 | // ========================================================================= |
||||||
157 | |||||||
158 | /** |
||||||
0 ignored issues
–
show
|
|||||||
159 | * @inheritdoc |
||||||
160 | */ |
||||||
0 ignored issues
–
show
|
|||||||
161 | public function init(): void |
||||||
162 | { |
||||||
163 | parent::init(); |
||||||
164 | |||||||
165 | // Handle cases where the plugin has been uninstalled |
||||||
166 | if (ImageOptimize::$plugin !== null) { |
||||||
167 | /** @var ?Settings $settings */ |
||||||
0 ignored issues
–
show
|
|||||||
168 | $settings = ImageOptimize::$plugin->getSettings(); |
||||||
169 | if ($settings) { |
||||||
0 ignored issues
–
show
|
|||||||
170 | if (empty($this->variants)) { |
||||||
171 | $this->variants = $settings->defaultVariants; |
||||||
172 | } |
||||||
173 | $this->aspectRatios = $settings->defaultAspectRatios; |
||||||
174 | } |
||||||
175 | } |
||||||
176 | // If the user has deleted all default aspect ratios, provide a fallback |
||||||
177 | if (empty($this->aspectRatios)) { |
||||||
178 | $this->aspectRatios = self::DEFAULT_ASPECT_RATIOS; |
||||||
179 | } |
||||||
180 | // If the user has deleted all default variants, provide a fallback |
||||||
181 | if (empty($this->variants)) { |
||||||
182 | $this->variants = self::DEFAULT_IMAGE_VARIANTS; |
||||||
183 | } |
||||||
184 | } |
||||||
185 | |||||||
186 | /** |
||||||
0 ignored issues
–
show
|
|||||||
187 | * @inheritdoc |
||||||
188 | */ |
||||||
0 ignored issues
–
show
|
|||||||
189 | public function rules(): array |
||||||
190 | { |
||||||
191 | $rules = parent::rules(); |
||||||
192 | return array_merge($rules, [ |
||||||
0 ignored issues
–
show
|
|||||||
193 | [ |
||||||
194 | [ |
||||||
195 | 'displayOptimizedImageVariants', |
||||||
196 | 'displayDominantColorPalette', |
||||||
197 | 'displayLazyLoadPlaceholderImages', |
||||||
198 | ], |
||||||
199 | 'boolean', |
||||||
200 | ], |
||||||
201 | [ |
||||||
202 | [ |
||||||
203 | 'ignoreFilesOfType', |
||||||
204 | 'variants', |
||||||
205 | ], |
||||||
206 | ArrayValidator::class, |
||||||
207 | ], |
||||||
208 | ]); |
||||||
0 ignored issues
–
show
For multi-line function calls, the closing parenthesis should be on a new line.
If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line: someFunctionCall(
$firstArgument,
$secondArgument,
$thirdArgument
); // Closing parenthesis on a new line.
![]() |
|||||||
209 | } |
||||||
210 | |||||||
211 | /** |
||||||
0 ignored issues
–
show
|
|||||||
212 | * @inheritdoc |
||||||
213 | * @since 1.6.2 |
||||||
0 ignored issues
–
show
|
|||||||
214 | */ |
||||||
0 ignored issues
–
show
|
|||||||
215 | public function getContentGqlType(): Type|array |
||||||
216 | { |
||||||
217 | $typeArray = OptimizedImagesGenerator::generateTypes($this); |
||||||
218 | |||||||
219 | return [ |
||||||
220 | 'name' => $this->handle, |
||||||
221 | 'description' => 'Optimized Images field', |
||||||
222 | 'type' => array_shift($typeArray), |
||||||
223 | ]; |
||||||
224 | } |
||||||
225 | |||||||
226 | /** |
||||||
0 ignored issues
–
show
|
|||||||
227 | * @inheritdoc |
||||||
228 | * @since 1.7.0 |
||||||
0 ignored issues
–
show
|
|||||||
229 | */ |
||||||
0 ignored issues
–
show
|
|||||||
230 | public function useFieldset(): bool |
||||||
231 | { |
||||||
232 | return true; |
||||||
233 | } |
||||||
234 | |||||||
235 | /** |
||||||
0 ignored issues
–
show
|
|||||||
236 | * @inheritdoc |
||||||
237 | */ |
||||||
0 ignored issues
–
show
|
|||||||
238 | public function afterElementSave(ElementInterface $element, bool $isNew): void |
||||||
239 | { |
||||||
240 | parent::afterElementSave($element, $isNew); |
||||||
241 | // Update our OptimizedImages Field data now that the Asset has been saved |
||||||
242 | // If this element is propagating, we don't need to redo the image saving for each site |
||||||
243 | if ($element instanceof Asset && $element->id !== null && !$element->propagating) { |
||||||
244 | // If the scenario is Asset::SCENARIO_FILEOPS or Asset::SCENARIO_MOVE (if using Craft > v3.7.1) treat it as a new asset |
||||||
245 | $scenario = $element->getScenario(); |
||||||
246 | $request = Craft::$app->getRequest(); |
||||||
247 | if ($isNew || $scenario === Asset::SCENARIO_FILEOPS || $scenario === Asset::SCENARIO_MOVE) { |
||||||
248 | /** |
||||||
249 | * If this is a newly uploaded/created Asset, we can save the variants |
||||||
250 | * via a queue job to prevent it from blocking |
||||||
251 | */ |
||||||
252 | ImageOptimize::$plugin->optimizedImages->resaveAsset($element->id); |
||||||
253 | } elseif (!$request->isConsoleRequest && $request->getPathInfo() === 'assets/save-image') { |
||||||
254 | /** |
||||||
255 | * If it's not a newly uploaded/created Asset, check to see if the image |
||||||
256 | * itself is being updated (via the ImageEditor). If so, update the |
||||||
257 | * variants immediately so the AssetSelectorHud displays the new images |
||||||
258 | */ |
||||||
259 | try { |
||||||
260 | ImageOptimize::$plugin->optimizedImages->updateOptimizedImageFieldData($this, $element); |
||||||
261 | } catch (Exception $e) { |
||||||
262 | Craft::error($e->getMessage(), __METHOD__); |
||||||
263 | } |
||||||
264 | } else { |
||||||
265 | ImageOptimize::$plugin->optimizedImages->resaveAsset($element->id); |
||||||
266 | } |
||||||
267 | } |
||||||
268 | } |
||||||
269 | |||||||
270 | /** |
||||||
0 ignored issues
–
show
|
|||||||
271 | * @inheritdoc |
||||||
272 | */ |
||||||
0 ignored issues
–
show
|
|||||||
273 | public function normalizeValue($value, ElementInterface $asset = null): mixed |
||||||
274 | { |
||||||
275 | // If we're passed in a string, assume it's JSON-encoded, and decode it |
||||||
276 | if (is_string($value) && !empty($value)) { |
||||||
277 | $value = Json::decodeIfJson($value); |
||||||
278 | } |
||||||
279 | // If we're passed in an array, make a model from it |
||||||
280 | if (is_array($value)) { |
||||||
281 | // Create a new OptimizedImage model and populate it |
||||||
282 | $model = new OptimizedImage($value); |
||||||
283 | } elseif ($value instanceof OptimizedImage) { |
||||||
284 | $model = $value; |
||||||
285 | } else { |
||||||
286 | // Just create a new empty model |
||||||
287 | $model = new OptimizedImage([]); |
||||||
288 | } |
||||||
289 | |||||||
290 | return $model; |
||||||
291 | } |
||||||
292 | |||||||
293 | /** |
||||||
0 ignored issues
–
show
|
|||||||
294 | * @inheritdoc |
||||||
295 | */ |
||||||
0 ignored issues
–
show
|
|||||||
296 | public function getSettingsHtml(): null|string |
||||||
297 | { |
||||||
298 | $namespace = Craft::$app->getView()->getNamespace(); |
||||||
299 | if (str_contains($namespace, Matrix::class)) { |
||||||
300 | // Render an error template, since the field only works when attached to an Asset |
||||||
301 | try { |
||||||
302 | return Craft::$app->getView()->renderTemplate( |
||||||
303 | 'image-optimize/_components/fields/OptimizedImages_error', |
||||||
304 | [ |
||||||
305 | ] |
||||||
306 | ); |
||||||
307 | } catch (Exception $e) { |
||||||
308 | Craft::error($e->getMessage(), __METHOD__); |
||||||
309 | } |
||||||
310 | } |
||||||
311 | // Register our asset bundle |
||||||
312 | try { |
||||||
313 | Craft::$app->getView()->registerAssetBundle(ImageOptimizeAsset::class); |
||||||
314 | } catch (InvalidConfigException $e) { |
||||||
315 | Craft::error($e->getMessage(), __METHOD__); |
||||||
316 | } |
||||||
317 | |||||||
318 | try { |
||||||
319 | $reflect = new ReflectionClass($this); |
||||||
320 | $thisId = $reflect->getShortName(); |
||||||
321 | } catch (ReflectionException $e) { |
||||||
322 | Craft::error($e->getMessage(), __METHOD__); |
||||||
323 | $thisId = 0; |
||||||
324 | } |
||||||
325 | // Get our id and namespace |
||||||
326 | $id = Html::id($thisId); |
||||||
327 | $namespacedId = Craft::$app->getView()->namespaceInputId($id); |
||||||
328 | $namespacePrefix = Craft::$app->getView()->namespaceInputName($thisId); |
||||||
329 | $sizesWrapperId = Craft::$app->getView()->namespaceInputId('sizes-wrapper'); |
||||||
330 | $view = Craft::$app->getView(); |
||||||
331 | $view->registerJs( |
||||||
332 | 'document.addEventListener("vite-script-loaded", function (e) {' . |
||||||
333 | 'if (e.detail.path === "../src/web/assets/src/js/OptimizedImagesField.js") {' . |
||||||
334 | 'new Craft.OptimizedImagesInput(' . |
||||||
335 | '"' . $namespacedId . '", ' . |
||||||
336 | '"' . $namespacePrefix . '",' . |
||||||
337 | '"' . $sizesWrapperId . '"' . |
||||||
338 | ');' . |
||||||
339 | '}' . |
||||||
340 | '});' |
||||||
341 | ); |
||||||
342 | |||||||
343 | // Prep our aspect ratios |
||||||
344 | $aspectRatios = []; |
||||||
345 | $index = 1; |
||||||
346 | foreach ($this->aspectRatios as $aspectRatio) { |
||||||
347 | if ($index % 6 === 0) { |
||||||
348 | $aspectRatio['break'] = true; |
||||||
349 | } |
||||||
350 | $aspectRatios[] = $aspectRatio; |
||||||
351 | $index++; |
||||||
352 | } |
||||||
353 | $aspectRatio = ['x' => 2, 'y' => 2, 'custom' => true]; |
||||||
354 | $aspectRatios[] = $aspectRatio; |
||||||
355 | // Get only the user-editable settings |
||||||
356 | /** @var Settings $settings */ |
||||||
0 ignored issues
–
show
|
|||||||
357 | $settings = ImageOptimize::$plugin->getSettings(); |
||||||
0 ignored issues
–
show
The method
getSettings() does not exist on null .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed. ![]() |
|||||||
358 | |||||||
359 | // Render the settings template |
||||||
360 | try { |
||||||
361 | return Craft::$app->getView()->renderTemplate( |
||||||
362 | 'image-optimize/_components/fields/OptimizedImages_settings', |
||||||
363 | [ |
||||||
364 | 'field' => $this, |
||||||
365 | 'settings' => $settings, |
||||||
366 | 'aspectRatios' => $aspectRatios, |
||||||
367 | 'id' => $id, |
||||||
368 | 'name' => $this->handle, |
||||||
369 | 'namespace' => $namespacedId, |
||||||
370 | 'fieldVolumes' => $this->getFieldVolumeInfo($this->handle), |
||||||
371 | ] |
||||||
372 | ); |
||||||
373 | } catch (Exception $e) { |
||||||
374 | Craft::error($e->getMessage(), __METHOD__); |
||||||
375 | } |
||||||
376 | |||||||
377 | return ''; |
||||||
378 | } |
||||||
379 | |||||||
380 | /** |
||||||
0 ignored issues
–
show
|
|||||||
381 | * @inheritdoc |
||||||
382 | */ |
||||||
0 ignored issues
–
show
|
|||||||
383 | public function getInputHtml($value, ElementInterface $element = null): string |
||||||
384 | { |
||||||
385 | if ($element instanceof Asset && $this->handle !== null) { |
||||||
386 | /** @var Asset $element */ |
||||||
0 ignored issues
–
show
|
|||||||
387 | // Register our asset bundle |
||||||
388 | try { |
||||||
389 | Craft::$app->getView()->registerAssetBundle(ImageOptimizeAsset::class); |
||||||
390 | } catch (InvalidConfigException $e) { |
||||||
391 | Craft::error($e->getMessage(), __METHOD__); |
||||||
392 | } |
||||||
393 | |||||||
394 | // Get our id and namespace |
||||||
395 | $id = Html::id($this->handle); |
||||||
396 | $nameSpaceId = Craft::$app->getView()->namespaceInputId($id); |
||||||
397 | |||||||
398 | // Variables to pass down to our field JavaScript to let it namespace properly |
||||||
399 | $jsonVars = [ |
||||||
400 | 'id' => $id, |
||||||
401 | 'name' => $this->handle, |
||||||
402 | 'namespace' => $nameSpaceId, |
||||||
403 | 'prefix' => Craft::$app->getView()->namespaceInputId(''), |
||||||
404 | ]; |
||||||
405 | $jsonVars = Json::encode($jsonVars); |
||||||
406 | $view = Craft::$app->getView(); |
||||||
407 | $view->registerJs( |
||||||
408 | 'document.addEventListener("vite-script-loaded", function (e) {' . |
||||||
409 | 'if (e.detail.path === "../src/web/assets/src/js/OptimizedImagesField.js") {' . |
||||||
410 | "$('#{$nameSpaceId}-field').ImageOptimizeOptimizedImages(" . |
||||||
411 | $jsonVars . |
||||||
412 | ");" . |
||||||
413 | '}' . |
||||||
414 | '});' |
||||||
415 | ); |
||||||
416 | |||||||
417 | /** @var Settings $settings */ |
||||||
0 ignored issues
–
show
|
|||||||
418 | $settings = ImageOptimize::$plugin->getSettings(); |
||||||
419 | $createVariants = ImageOptimize::$plugin->optimizedImages->shouldCreateVariants($this, $element); |
||||||
420 | |||||||
421 | // Render the input template |
||||||
422 | try { |
||||||
423 | return Craft::$app->getView()->renderTemplate( |
||||||
424 | 'image-optimize/_components/fields/OptimizedImages_input', |
||||||
425 | [ |
||||||
426 | 'name' => $this->handle, |
||||||
427 | 'value' => $value, |
||||||
428 | 'variants' => $this->variants, |
||||||
429 | 'field' => $this, |
||||||
430 | 'settings' => $settings, |
||||||
431 | 'elementId' => $element->id, |
||||||
432 | 'format' => $element->getExtension(), |
||||||
433 | 'id' => $id, |
||||||
434 | 'nameSpaceId' => $nameSpaceId, |
||||||
435 | 'createVariants' => $createVariants, |
||||||
436 | ] |
||||||
437 | ); |
||||||
438 | } catch (Exception $e) { |
||||||
439 | Craft::error($e->getMessage(), __METHOD__); |
||||||
440 | } |
||||||
441 | } |
||||||
442 | |||||||
443 | // Render an error template, since the field only works when attached to an Asset |
||||||
444 | try { |
||||||
445 | return Craft::$app->getView()->renderTemplate( |
||||||
446 | 'image-optimize/_components/fields/OptimizedImages_error', |
||||||
447 | [ |
||||||
448 | ] |
||||||
449 | ); |
||||||
450 | } catch (Exception $e) { |
||||||
451 | Craft::error($e->getMessage(), __METHOD__); |
||||||
452 | } |
||||||
453 | |||||||
454 | return ''; |
||||||
455 | } |
||||||
456 | |||||||
457 | // Protected Methods |
||||||
458 | // ========================================================================= |
||||||
459 | |||||||
460 | /** |
||||||
461 | * Returns an array of asset volumes and their sub-folders |
||||||
462 | * |
||||||
463 | * @param string|null $fieldHandle |
||||||
0 ignored issues
–
show
|
|||||||
464 | * |
||||||
465 | * @return array |
||||||
466 | * @throws InvalidConfigException |
||||||
467 | */ |
||||||
468 | protected function getFieldVolumeInfo(?string $fieldHandle): array |
||||||
469 | { |
||||||
470 | $result = []; |
||||||
471 | if ($fieldHandle !== null) { |
||||||
472 | $volumes = Craft::$app->getVolumes()->getAllVolumes(); |
||||||
473 | $assets = Craft::$app->getAssets(); |
||||||
474 | foreach ($volumes as $volume) { |
||||||
475 | if (($volume instanceof Volume) && $this->volumeHasField($volume, $fieldHandle)) { |
||||||
476 | $tree = $assets->getFolderTreeByVolumeIds([$volume->id]); |
||||||
477 | $result[] = [ |
||||||
478 | 'name' => $volume->name, |
||||||
479 | 'handle' => $volume->handle, |
||||||
480 | 'subfolders' => $this->assembleSourceList($tree), |
||||||
481 | ]; |
||||||
482 | } |
||||||
483 | } |
||||||
484 | } |
||||||
485 | // If there are too many sub-folders in an Asset volume, don't display them, return an empty array |
||||||
486 | if (count($result) > self::MAX_VOLUME_SUBFOLDERS) { |
||||||
487 | $result = []; |
||||||
488 | } |
||||||
489 | |||||||
490 | return $result; |
||||||
491 | } |
||||||
492 | |||||||
493 | /** |
||||||
494 | * See if the passed $volume has an OptimizedImagesField with the handle $fieldHandle |
||||||
495 | * |
||||||
496 | * @param Volume $volume |
||||||
0 ignored issues
–
show
|
|||||||
497 | * @param string $fieldHandle |
||||||
0 ignored issues
–
show
|
|||||||
498 | * |
||||||
499 | * @return bool |
||||||
500 | */ |
||||||
501 | protected function volumeHasField(Volume $volume, string $fieldHandle): bool |
||||||
502 | { |
||||||
503 | $result = false; |
||||||
504 | /** @var ?FieldLayout $fieldLayout */ |
||||||
0 ignored issues
–
show
|
|||||||
505 | $fieldLayout = $volume->getFieldLayout(); |
||||||
506 | // Loop through the fields in the layout to see if there is an OptimizedImages field |
||||||
507 | if ($fieldLayout) { |
||||||
508 | $fields = $fieldLayout->getCustomFields(); |
||||||
509 | foreach ($fields as $field) { |
||||||
510 | if ($field instanceof OptimizedImagesField && $field->handle === $fieldHandle) { |
||||||
511 | $result = true; |
||||||
512 | } |
||||||
513 | } |
||||||
514 | } |
||||||
515 | |||||||
516 | return $result; |
||||||
517 | } |
||||||
518 | |||||||
519 | /** |
||||||
520 | * Transforms an asset folder tree into a source list. |
||||||
521 | * |
||||||
522 | * @param array $folders |
||||||
0 ignored issues
–
show
|
|||||||
523 | * @param bool $includeNestedFolders |
||||||
0 ignored issues
–
show
|
|||||||
524 | * |
||||||
525 | * @return array |
||||||
526 | */ |
||||||
527 | protected function assembleSourceList(array $folders, bool $includeNestedFolders = true): array |
||||||
528 | { |
||||||
529 | $sources = []; |
||||||
530 | |||||||
531 | foreach ($folders as $folder) { |
||||||
532 | $children = $folder->getChildren(); |
||||||
533 | foreach ($children as $child) { |
||||||
534 | $sources[$child->name] = $child->name; |
||||||
535 | } |
||||||
536 | } |
||||||
537 | |||||||
538 | return $sources; |
||||||
539 | } |
||||||
540 | } |
||||||
541 |