Test Setup Failed
Branch v1 (b1dea4)
by Andrew
03:56
created

ImageOptimize::customFrontendRoutes()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 2
dl 0
loc 5
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
/**
3
 * ImageOptimize plugin for Craft CMS 3.x
4
 *
5
 * Automatically optimize images after they've been transformed
6
 *
7
 * @link      https://nystudio107.com
8
 * @copyright Copyright (c) 2017 nystudio107
9
 */
10
11
namespace nystudio107\imageoptimize;
12
13
use nystudio107\imageoptimize\fields\OptimizedImages;
14
use nystudio107\imageoptimize\imagetransforms\ImageTransformInterface;
15
use nystudio107\imageoptimize\listeners\GetCraftQLSchema;
16
use nystudio107\imageoptimize\models\Settings;
17
use nystudio107\imageoptimize\services\Optimize as OptimizeService;
18
use nystudio107\imageoptimize\services\OptimizedImages as OptimizedImagesService;
19
use nystudio107\imageoptimize\services\Placeholder as PlaceholderService;
20
use nystudio107\imageoptimize\variables\ImageOptimizeVariable;
21
22
use Craft;
23
use craft\base\Field;
24
use craft\base\Plugin;
25
use craft\base\Volume;
26
use craft\elements\Asset;
27
use craft\events\AssetTransformImageEvent;
28
use craft\events\ElementEvent;
29
use craft\events\FieldEvent;
30
use craft\events\GetAssetUrlEvent;
31
use craft\events\GenerateTransformEvent;
32
use craft\events\PluginEvent;
33
use craft\events\RegisterComponentTypesEvent;
34
use craft\events\RegisterUrlRulesEvent;
35
use craft\events\ReplaceAssetEvent;
36
use craft\events\VolumeEvent;
37
use craft\helpers\UrlHelper;
38
use craft\models\FieldLayout;
39
use craft\services\Assets;
40
use craft\services\AssetTransforms;
41
use craft\services\Elements;
42
use craft\services\Fields;
43
use craft\services\Plugins;
44
use craft\services\Volumes;
45
use craft\web\twig\variables\CraftVariable;
46
use craft\web\Controller;
47
use craft\web\UrlManager;
48
49
use markhuot\CraftQL\CraftQL;
0 ignored issues
show
Bug introduced by
The type markhuot\CraftQL\CraftQL 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. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
50
51
use yii\base\Event;
52
use yii\base\Exception;
53
54
/** @noinspection MissingPropertyAnnotationsInspection */
55
56
/**
57
 * Class ImageOptimize
58
 *
59
 * @author    nystudio107
60
 * @package   ImageOptimize
61
 * @since     1.0.0
62
 *
63
 * @property OptimizeService        optimize
64
 * @property PlaceholderService     placeholder
65
 * @property OptimizedImagesService optimizedImages
66
 * @property Settings               $settings
67
 * @method   Settings               getSettings()
68
 */
69
class ImageOptimize extends Plugin
70
{
71
72
    // Constants
73
    // =========================================================================
74
75
    const CRAFTQL_PLUGIN_HANDLE = 'craftql';
76
77
    // Static Properties
78
    // =========================================================================
79
80
    /**
81
     * @var ImageOptimize
82
     */
83
    public static $plugin;
84
85
    /**
86
     * @var ImageTransformInterface
87
     */
88
    public static $transformClass;
89
90
    /**
91
     * @var array
92
     */
93
    public static $transformParams;
94
95
    /**
96
     * @var bool
97
     */
98
    public static $generatePlaceholders = true;
99
100
    // Public Methods
101
    // =========================================================================
102
103
    /**
104
     * @inheritdoc
105
     */
106
    public function init()
107
    {
108
        parent::init();
109
        self::$plugin = $this;
110
        // Handle any console commands
111
        $request = Craft::$app->getRequest();
112
        if ($request->getIsConsoleRequest()) {
113
            $this->controllerNamespace = 'nystudio107\imageoptimize\console\controllers';
114
        }
115
        // Cache some settings
116
        $settings = $this->getSettings();
117
        self::$transformClass = ImageTransformInterface::IMAGE_TRANSFORM_MAP[$settings->transformMethod];
118
        self::$transformParams = self::$transformClass::getTransformParams();
119
        // Add in our Craft components
120
        $this->addComponents();
121
        // Install our global event handlers
122
        $this->installEventHandlers();
123
        // Log that the plugin has loaded
124
        Craft::info(
125
            Craft::t(
126
                'image-optimize',
127
                '{name} plugin loaded',
128
                ['name' => $this->name]
129
            ),
130
            __METHOD__
131
        );
132
    }
133
134
    /**
135
     * @inheritdoc
136
     */
137
    public function getSettingsResponse()
138
    {
139
        $view = Craft::$app->getView();
140
        $namespace = $view->getNamespace();
141
        $view->setNamespace('settings');
142
        $settingsHtml = $this->settingsHtml();
143
        $view->setNamespace($namespace);
144
        /** @var Controller $controller */
145
        $controller = Craft::$app->controller;
146
147
        return $controller->renderTemplate('image-optimize/_settings', [
148
            'plugin'       => $this,
149
            'settingsHtml' => $settingsHtml,
150
        ]);
151
    }
152
153
    /**
154
     * @inheritdoc
155
     */
156
    public function settingsHtml()
157
    {
158
        $imageProcessors = ImageOptimize::$plugin->optimize->getActiveImageProcessors();
159
        $variantCreators = ImageOptimize::$plugin->optimize->getActiveVariantCreators();
160
        // Get only the user-editable settings
161
        $settings = $this->getSettings();
162
163
        // Render the settings template
164
        try {
165
            return Craft::$app->getView()->renderTemplate(
166
                'image-optimize/settings',
167
                [
168
                    'settings'        => $settings,
169
                    'imageProcessors' => $imageProcessors,
170
                    'variantCreators' => $variantCreators,
171
                    'gdInstalled'     => \function_exists('imagecreatefromjpeg'),
172
                ]
173
            );
174
        } catch (\Twig_Error_Loader $e) {
175
            Craft::error($e->getMessage(), __METHOD__);
176
        } catch (Exception $e) {
177
            Craft::error($e->getMessage(), __METHOD__);
178
        }
179
180
        return '';
181
    }
182
183
    // Protected Methods
184
    // =========================================================================
185
186
    /**
187
     * @inheritdoc
188
     */
189
    protected function createSettingsModel()
190
    {
191
        return new Settings();
192
    }
193
194
    /**
195
     * Add in our Craft components
196
     */
197
    protected function addComponents()
198
    {
199
        // Register our variables
200
        Event::on(
201
            CraftVariable::class,
202
            CraftVariable::EVENT_INIT,
203
            function (Event $event) {
204
                /** @var CraftVariable $variable */
205
                $variable = $event->sender;
206
                $variable->set('imageOptimize', ImageOptimizeVariable::class);
207
            }
208
        );
209
210
        // Register our Field
211
        Event::on(
212
            Fields::class,
213
            Fields::EVENT_REGISTER_FIELD_TYPES,
214
            function (RegisterComponentTypesEvent $event) {
215
                Craft::debug(
216
                    'Fields::EVENT_REGISTER_FIELD_TYPES',
217
                    __METHOD__
218
                );
219
                $event->types[] = OptimizedImages::class;
220
            }
221
        );
222
    }
223
224
    /**
225
     * Install our event handlers
226
     */
227
    protected function installEventHandlers()
228
    {
229
        $this->installAssetEventHandlers();
230
        $this->installElementEventHandlers();
231
        $this->installMiscEventHandlers();
232
        $this->installCraftQLEventHandlers();
233
        $request = Craft::$app->getRequest();
234
        // Install only for non-console site requests
235
        if ($request->getIsSiteRequest() && !$request->getIsConsoleRequest()) {
236
            $this->installSiteEventListeners();
237
        }
238
    }
239
240
    /**
241
     * Install our Asset event handlers
242
     */
243
    protected function installAssetEventHandlers()
244
    {
245
        // Handler: Assets::EVENT_GET_ASSET_URL
246
        Event::on(
247
            Assets::class,
248
            Assets::EVENT_GET_ASSET_URL,
249
            function (GetAssetUrlEvent $event) {
250
                Craft::debug(
251
                    'Assets::EVENT_GET_ASSET_URL',
252
                    __METHOD__
253
                );
254
                // Return the URL to the asset URL or null to let Craft handle it
255
                $event->url = ImageOptimize::$plugin->optimize->handleGetAssetUrlEvent(
256
                    $event
257
                );
258
            }
259
        );
260
261
        // Handler: AssetTransforms::EVENT_GENERATE_TRANSFORM
262
        Event::on(
263
            AssetTransforms::class,
264
            AssetTransforms::EVENT_GENERATE_TRANSFORM,
265
            function (GenerateTransformEvent $event) {
266
                Craft::debug(
267
                    'AssetTransforms::EVENT_GENERATE_TRANSFORM',
268
                    __METHOD__
269
                );
270
                // Return the path to the optimized image to _createTransformForAsset()
271
                $event->tempPath = ImageOptimize::$plugin->optimize->handleGenerateTransformEvent(
272
                    $event
273
                );
274
            }
275
        );
276
277
        // Handler: AssetTransforms::EVENT_AFTER_DELETE_TRANSFORMS
278
        Event::on(
279
            AssetTransforms::class,
280
            AssetTransforms::EVENT_AFTER_DELETE_TRANSFORMS,
281
            function (AssetTransformImageEvent $event) {
282
                Craft::debug(
283
                    'AssetTransforms::EVENT_AFTER_DELETE_TRANSFORMS',
284
                    __METHOD__
285
                );
286
                // Clean up any stray variant files
287
                ImageOptimize::$plugin->optimize->handleAfterDeleteTransformsEvent(
288
                    $event
289
                );
290
            }
291
        );
292
293
        // Handler: Assets::EVENT_BEFORE_REPLACE_ASSET
294
        Event::on(
295
            Assets::class,
296
            Assets::EVENT_BEFORE_REPLACE_ASSET,
297
            function (ReplaceAssetEvent $event) {
298
                Craft::debug(
299
                    'Assets::EVENT_BEFORE_REPLACE_ASSET',
300
                    __METHOD__
301
                );
302
                /** @var Asset $element */
303
                $element = $event->asset;
304
                // Purge the URL
305
                $purgeUrl = ImageOptimize::$transformClass::getPurgeUrl(
306
                    $element,
307
                    ImageOptimize::$transformParams
308
                );
309
                if ($purgeUrl) {
310
                    ImageOptimize::$transformClass::purgeUrl($purgeUrl, ImageOptimize::$transformParams);
311
                }
312
            }
313
        );
314
315
        // Handler: Assets::EVENT_AFTER_REPLACE_ASSET
316
        Event::on(
317
            Assets::class,
318
            Assets::EVENT_AFTER_REPLACE_ASSET,
319
            function (ReplaceAssetEvent $event) {
320
                Craft::debug(
321
                    'Assets::EVENT_AFTER_REPLACE_ASSET',
322
                    __METHOD__
323
                );
324
                /** @var Asset $element */
325
                $element = $event->asset;
326
                if ($element->id !== null) {
327
                    ImageOptimize::$plugin->optimizedImages->resaveAsset($element->id);
328
                }
329
            }
330
        );
331
    }
332
333
    /**
334
     * Install our Element event handlers
335
     */
336
    protected function installElementEventHandlers()
337
    {
338
        // Handler: Elements::EVENT_BEFORE_SAVE_ELEMENT
339
        Event::on(
340
            Assets::class,
341
            Elements::EVENT_BEFORE_SAVE_ELEMENT,
342
            function (ElementEvent $event) {
343
                Craft::debug(
344
                    'Elements::EVENT_BEFORE_SAVE_ELEMENT',
345
                    __METHOD__
346
                );
347
                /** @var Asset $asset */
348
                $asset = $event->element;
349
                if (!$event->isNew) {
350
                    // Purge the URL
351
                    $purgeUrl = ImageOptimize::$transformClass::getPurgeUrl(
352
                        $asset,
353
                        ImageOptimize::$transformParams
354
                    );
355
                    if ($purgeUrl) {
356
                        ImageOptimize::$transformClass::purgeUrl($purgeUrl, ImageOptimize::$transformParams);
357
                    }
358
                }
359
            }
360
        );
361
362
        // Handler: Elements::EVENT_BEFORE_DELETE_ELEMENT
363
        Event::on(
364
            Asset::class,
365
            Elements::EVENT_BEFORE_DELETE_ELEMENT,
366
            function (ElementEvent $event) {
367
                Craft::debug(
368
                    'Elements::EVENT_BEFORE_DELETE_ELEMENT',
369
                    __METHOD__
370
                );
371
                /** @var Asset $asset */
372
                $asset = $event->element;
373
                // Purge the URL
374
                $purgeUrl = ImageOptimize::$transformClass::getPurgeUrl(
375
                    $asset,
376
                    ImageOptimize::$transformParams
377
                );
378
                if ($purgeUrl) {
379
                    ImageOptimize::$transformClass::purgeUrl($purgeUrl, ImageOptimize::$transformParams);
380
                }
381
            }
382
        );
383
    }
384
385
386
    /**
387
     * Install our miscellaneous event handlers
388
     */
389
    protected function installMiscEventHandlers()
390
    {
391
        // Handler: Fields::EVENT_AFTER_SAVE_FIELD
392
        Event::on(
393
            Fields::class,
394
            Fields::EVENT_AFTER_SAVE_FIELD,
395
            function (FieldEvent $event) {
396
                Craft::debug(
397
                    'Fields::EVENT_AFTER_SAVE_FIELD',
398
                    __METHOD__
399
                );
400
                $settings = $this->getSettings();
401
                /** @var Field $field */
402
                if (!$event->isNew && $settings->automaticallyResaveImageVariants) {
403
                    $this->checkForOptimizedImagesField($event);
404
                }
405
            }
406
        );
407
408
        // Handler: Plugins::EVENT_AFTER_SAVE_PLUGIN_SETTINGS
409
        Event::on(
410
            Plugins::class,
411
            Plugins::EVENT_AFTER_SAVE_PLUGIN_SETTINGS,
412
            function (PluginEvent $event) {
413
                if ($event->plugin === $this) {
414
                    Craft::debug(
415
                        'Plugins::EVENT_AFTER_SAVE_PLUGIN_SETTINGS',
416
                        __METHOD__
417
                    );
418
                    $settings = $this->getSettings();
419
                    if ($settings->automaticallyResaveImageVariants) {
420
                        // After they have changed the settings, resave all of the assets
421
                        ImageOptimize::$plugin->optimizedImages->resaveAllVolumesAssets();
422
                    }
423
                }
424
            }
425
        );
426
427
        // Handler: Volumes::EVENT_AFTER_SAVE_VOLUME
428
        Event::on(
429
            Volumes::class,
430
            Volumes::EVENT_AFTER_SAVE_VOLUME,
431
            function (VolumeEvent $event) {
432
                Craft::debug(
433
                    'Volumes::EVENT_AFTER_SAVE_VOLUME',
434
                    __METHOD__
435
                );
436
                $settings = $this->getSettings();
437
                // Only worry about this volume if it's not new
438
                if (!$event->isNew && $settings->automaticallyResaveImageVariants) {
439
                    /** @var Volume $volume */
440
                    $volume = $event->volume;
441
                    if ($volume !== null) {
442
                        ImageOptimize::$plugin->optimizedImages->resaveVolumeAssets($volume);
443
                    }
444
                }
445
            }
446
        );
447
448
        // Handler: Plugins::EVENT_AFTER_INSTALL_PLUGIN
449
        Event::on(
450
            Plugins::class,
451
            Plugins::EVENT_AFTER_INSTALL_PLUGIN,
452
            function (PluginEvent $event) {
453
                if ($event->plugin === $this) {
454
                    $request = Craft::$app->getRequest();
455
                    if ($request->isCpRequest) {
456
                        Craft::$app->getResponse()->redirect(UrlHelper::cpUrl('image-optimize/welcome'))->send();
457
                    }
458
                }
459
            }
460
        );
461
    }
462
463
    /**
464
     * Install our CraftQL event handlers
465
     */
466
    protected function installCraftQLEventHandlers()
467
    {
468
        if (class_exists(CraftQL::class)) {
469
            Event::on(
470
                OptimizedImages::class,
471
                GetCraftQLSchema::EVENT_GET_FIELD_SCHEMA,
472
                [new GetCraftQLSchema, 'handle']
473
            );
474
        }
475
    }
476
477
    /**
478
     * Install site event listeners for site requests only
479
     */
480
    protected function installSiteEventListeners()
481
    {
482
        // Handler: UrlManager::EVENT_REGISTER_SITE_URL_RULES
483
        Event::on(
484
            UrlManager::class,
485
            UrlManager::EVENT_REGISTER_SITE_URL_RULES,
486
            function (RegisterUrlRulesEvent $event) {
487
                Craft::debug(
488
                    'UrlManager::EVENT_REGISTER_SITE_URL_RULES',
489
                    __METHOD__
490
                );
491
                // Register our AdminCP routes
492
                $event->rules = array_merge(
493
                    $event->rules,
494
                    $this->customFrontendRoutes()
495
                );
496
            }
497
        );
498
    }
499
500
    /**
501
     * Return the custom frontend routes
502
     *
503
     * @return array
504
     */
505
    protected function customFrontendRoutes(): array
506
    {
507
        return [
508
            // Make webpack async bundle loading work out of published AssetBundles
509
            '/cpresources/imageoptimize/<resourceType:{handle}>/<fileName>' => 'image-optimize/cp-nav/resource',
510
        ];
511
    }
512
513
    /**
514
     * If the Field being saved is an OptimizedImages field, re-save the
515
     * responsive image variants automatically
516
     *
517
     * @param FieldEvent $event
518
     *
519
     * @throws \yii\base\InvalidConfigException
520
     */
521
    protected function checkForOptimizedImagesField(FieldEvent $event)
522
    {
523
        $thisField = $event->field;
524
        if ($thisField instanceof OptimizedImages) {
525
            $volumes = Craft::$app->getVolumes()->getAllVolumes();
526
            foreach ($volumes as $volume) {
527
                $needToReSave = false;
528
                /** @var FieldLayout $fieldLayout */
529
                /** @var Volume $volume */
530
                $fieldLayout = $volume->getFieldLayout();
531
                // Loop through the fields in the layout to see if it contains our field
532
                if ($fieldLayout) {
533
                    $fields = $fieldLayout->getFields();
534
                    foreach ($fields as $field) {
535
                        /** @var Field $field */
536
                        if ($thisField->handle === $field->handle) {
537
                            $needToReSave = true;
538
                        }
539
                    }
540
                    if ($needToReSave) {
541
                        ImageOptimize::$plugin->optimizedImages->resaveVolumeAssets($volume);
542
                    }
543
                }
544
            }
545
        }
546
    }
547
}
548