Passed
Push — v1 ( 91a074...1b853f )
by Andrew
11:14 queued 07:08
created

ImageOptimize::installElementEventHandlers()   B

Complexity

Conditions 4
Paths 1

Size

Total Lines 44
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 44
rs 8.5806
c 0
b 0
f 0
cc 4
eloc 27
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\models\Settings;
16
use nystudio107\imageoptimize\services\Optimize as OptimizeService;
17
use nystudio107\imageoptimize\services\OptimizedImages as OptimizedImagesService;
18
use nystudio107\imageoptimize\services\Placeholder as PlaceholderService;
19
use nystudio107\imageoptimize\variables\ImageOptimizeVariable;
20
21
use Craft;
22
use craft\base\Field;
23
use craft\base\Plugin;
24
use craft\base\Volume;
25
use craft\console\Application as ConsoleApplication;
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\ReplaceAssetEvent;
35
use craft\events\VolumeEvent;
36
use craft\helpers\UrlHelper;
37
use craft\models\FieldLayout;
38
use craft\services\Assets;
39
use craft\services\AssetTransforms;
40
use craft\services\Elements;
41
use craft\services\Fields;
42
use craft\services\Plugins;
43
use craft\services\Volumes;
44
use craft\web\twig\variables\CraftVariable;
45
use craft\web\Controller;
46
47
use yii\base\Event;
48
49
/** @noinspection MissingPropertyAnnotationsInspection */
50
51
/**
52
 * Class ImageOptimize
53
 *
54
 * @author    nystudio107
55
 * @package   ImageOptimize
56
 * @since     1.0.0
57
 *
58
 * @property OptimizeService        optimize
59
 * @property PlaceholderService     placeholder
60
 * @property OptimizedImagesService optimizedImages
61
 * @property Settings               $settings
62
 * @method   Settings               getSettings()
63
 */
64
class ImageOptimize extends Plugin
65
{
66
    // Static Properties
67
    // =========================================================================
68
69
    /**
70
     * @var ImageOptimize
71
     */
72
    public static $plugin;
73
74
    /**
75
     * @var ImageTransformInterface
76
     */
77
    public static $transformClass;
78
79
    /**
80
     * @var array
81
     */
82
    public static $transformParams;
83
84
    /**
85
     * @var bool
86
     */
87
    public static $generatePlacholders = true;
88
89
    // Public Methods
90
    // =========================================================================
91
92
    /**
93
     * @inheritdoc
94
     */
95
    public function init()
96
    {
97
        parent::init();
98
        self::$plugin = $this;
99
        // Handle any console commands
100
        $request = Craft::$app->getRequest();
101
        if ($request->getIsConsoleRequest()) {
102
            $this->controllerNamespace = 'nystudio107\imageoptimize\console\controllers';
103
        }
104
        // Cache some settings
105
        $settings = $this->getSettings();
106
        self::$transformClass = ImageTransformInterface::IMAGE_TRANSFORM_MAP[$settings->transformMethod];
107
        self::$transformParams = self::$transformClass::getTransformParams();
108
        // Add in our Craft components
109
        $this->addComponents();
110
        // Install our global event handlers
111
        $this->installEventHandlers();
112
        // Log that the plugin has loaded
113
        Craft::info(
114
            Craft::t(
115
                'image-optimize',
116
                '{name} plugin loaded',
117
                ['name' => $this->name]
118
            ),
119
            __METHOD__
120
        );
121
    }
122
123
    /**
124
     * @inheritdoc
125
     */
126
    public function getSettingsResponse()
127
    {
128
        $view = Craft::$app->getView();
129
        $namespace = $view->getNamespace();
130
        $view->setNamespace('settings');
131
        $settingsHtml = $this->settingsHtml();
132
        $view->setNamespace($namespace);
133
        /** @var Controller $controller */
134
        $controller = Craft::$app->controller;
135
136
        return $controller->renderTemplate('image-optimize/_settings', [
137
            'plugin'       => $this,
138
            'settingsHtml' => $settingsHtml,
139
        ]);
140
    }
141
142
    /**
143
     * @inheritdoc
144
     */
145
    public function settingsHtml()
146
    {
147
        $imageProcessors = ImageOptimize::$plugin->optimize->getActiveImageProcessors();
148
        $variantCreators = ImageOptimize::$plugin->optimize->getActiveVariantCreators();
149
        // Get only the user-editable settings
150
        $settings = $this->getSettings();
151
152
        // Render the settings template
153
        return Craft::$app->getView()->renderTemplate(
154
            'image-optimize/settings',
155
            [
156
                'settings'        => $settings,
157
                'imageProcessors' => $imageProcessors,
158
                'variantCreators' => $variantCreators,
159
                'gdInstalled'     => function_exists('imagecreatefromjpeg'),
160
            ]
161
        );
162
    }
163
164
    // Protected Methods
165
    // =========================================================================
166
167
    /**
168
     * @inheritdoc
169
     */
170
    protected function createSettingsModel()
171
    {
172
        return new Settings();
173
    }
174
175
    /**
176
     * Add in our Craft components
177
     */
178
    protected function addComponents()
179
    {
180
        // Register our variables
181
        Event::on(
182
            CraftVariable::class,
183
            CraftVariable::EVENT_INIT,
184
            function (Event $event) {
185
                /** @var CraftVariable $variable */
186
                $variable = $event->sender;
187
                $variable->set('imageOptimize', ImageOptimizeVariable::class);
188
            }
189
        );
190
191
        // Register our Field
192
        Event::on(
193
            Fields::class,
194
            Fields::EVENT_REGISTER_FIELD_TYPES,
195
            function (RegisterComponentTypesEvent $event) {
196
                Craft::debug(
197
                    'Fields::EVENT_REGISTER_FIELD_TYPES',
198
                    __METHOD__
199
                );
200
                $event->types[] = OptimizedImages::class;
201
            }
202
        );
203
    }
204
205
    /**
206
     * Install our event handlers
207
     */
208
    protected function installEventHandlers()
209
    {
210
        $this->installAssetEventHandlers();
211
        $this->installElementEventHandlers();
212
        $this->installMiscEventHandlers();
213
    }
214
215
    /**
216
     * Install our miscellaneous event handlers
217
     */
218
    protected function installMiscEventHandlers()
219
    {
220
        // Handler: Fields::EVENT_AFTER_SAVE_FIELD
221
        Event::on(
222
            Fields::class,
223
            Fields::EVENT_AFTER_SAVE_FIELD,
224
            function (FieldEvent $event) {
225
                Craft::debug(
226
                    'Fields::EVENT_AFTER_SAVE_FIELD',
227
                    __METHOD__
228
                );
229
                $settings = $this->getSettings();
230
                /** @var Field $field */
231
                if (!$event->isNew && $settings->automaticallyResaveImageVariants) {
232
                    $this->checkForOptimizedImagesField($event);
233
                }
234
            }
235
        );
236
237
        // Handler: Plugins::EVENT_AFTER_SAVE_PLUGIN_SETTINGS
238
        Event::on(
239
            Plugins::class,
240
            Plugins::EVENT_AFTER_SAVE_PLUGIN_SETTINGS,
241
            function (PluginEvent $event) {
242
                if ($event->plugin === $this) {
243
                    Craft::debug(
244
                        'Plugins::EVENT_AFTER_SAVE_PLUGIN_SETTINGS',
245
                        __METHOD__
246
                    );
247
                    $settings = $this->getSettings();
248
                    if ($settings->automaticallyResaveImageVariants) {
249
                        // After they have changed the settings, resave all of the assets
250
                        ImageOptimize::$plugin->optimizedImages->resaveAllVolumesAssets();
251
                    }
252
                }
253
            }
254
        );
255
256
        // Handler: Volumes::EVENT_AFTER_SAVE_VOLUME
257
        Event::on(
258
            Volumes::class,
259
            Volumes::EVENT_AFTER_SAVE_VOLUME,
260
            function (VolumeEvent $event) {
261
                Craft::debug(
262
                    'Volumes::EVENT_AFTER_SAVE_VOLUME',
263
                    __METHOD__
264
                );
265
                $settings = $this->getSettings();
266
                // Only worry about this volume if it's not new
267
                if (!$event->isNew && $settings->automaticallyResaveImageVariants) {
268
                    /** @var Volume $volume */
269
                    $volume = $event->volume;
270
                    if (!empty($volume)) {
271
                        ImageOptimize::$plugin->optimizedImages->resaveVolumeAssets($volume);
272
                    }
273
                }
274
            }
275
        );
276
277
        // Handler: Plugins::EVENT_AFTER_INSTALL_PLUGIN
278
        Event::on(
279
            Plugins::class,
280
            Plugins::EVENT_AFTER_INSTALL_PLUGIN,
281
            function (PluginEvent $event) {
282
                if ($event->plugin === $this) {
283
                    $request = Craft::$app->getRequest();
284
                    if ($request->isCpRequest) {
285
                        Craft::$app->getResponse()->redirect(UrlHelper::cpUrl('image-optimize/welcome'))->send();
286
                    }
287
                }
288
            }
289
        );
290
    }
291
292
    /**
293
     * Install our Asset event handlers
294
     */
295
    protected function installAssetEventHandlers()
296
    {
297
        // Handler: Assets::EVENT_GET_ASSET_URL
298
        Event::on(
299
            Assets::class,
300
            Assets::EVENT_GET_ASSET_URL,
301
            function (GetAssetUrlEvent $event) {
302
                Craft::debug(
303
                    'Assets::EVENT_GET_ASSET_URL',
304
                    __METHOD__
305
                );
306
                // Return the URL to the asset URL or null to let Craft handle it
307
                $event->url = ImageOptimize::$plugin->optimize->handleGetAssetUrlEvent(
308
                    $event
309
                );
310
            }
311
        );
312
313
        // Handler: AssetTransforms::EVENT_GENERATE_TRANSFORM
314
        Event::on(
315
            AssetTransforms::class,
316
            AssetTransforms::EVENT_GENERATE_TRANSFORM,
317
            function (GenerateTransformEvent $event) {
318
                Craft::debug(
319
                    'AssetTransforms::EVENT_GENERATE_TRANSFORM',
320
                    __METHOD__
321
                );
322
                // Return the path to the optimized image to _createTransformForAsset()
323
                $event->tempPath = ImageOptimize::$plugin->optimize->handleGenerateTransformEvent(
324
                    $event
325
                );
326
            }
327
        );
328
329
        // Handler: AssetTransforms::EVENT_AFTER_DELETE_TRANSFORMS
330
        Event::on(
331
            AssetTransforms::class,
332
            AssetTransforms::EVENT_AFTER_DELETE_TRANSFORMS,
333
            function (AssetTransformImageEvent $event) {
334
                Craft::debug(
335
                    'AssetTransforms::EVENT_AFTER_DELETE_TRANSFORMS',
336
                    __METHOD__
337
                );
338
                // Clean up any stray variant files
339
                ImageOptimize::$plugin->optimize->handleAfterDeleteTransformsEvent(
340
                    $event
341
                );
342
            }
343
        );
344
345
        // Handler: Assets::EVENT_BEFORE_REPLACE_ASSET
346
        Event::on(
347
            Assets::class,
348
            Assets::EVENT_BEFORE_REPLACE_ASSET,
349
            function (ReplaceAssetEvent $event) {
350
                Craft::debug(
351
                    'Assets::EVENT_BEFORE_REPLACE_ASSET',
352
                    __METHOD__
353
                );
354
                /** @var Asset $element */
355
                $element = $event->asset;
356
                // Purge the URL
357
                $purgeUrl = ImageOptimize::$transformClass::getPurgeUrl(
358
                    $element,
359
                    ImageOptimize::$transformParams
360
                );
361
                if ($purgeUrl) {
362
                    ImageOptimize::$transformClass::purgeUrl($purgeUrl, ImageOptimize::$transformParams);
363
                }
364
            }
365
        );
366
367
        // Handler: Assets::EVENT_AFTER_REPLACE_ASSET
368
        Event::on(
369
            Assets::class,
370
            Assets::EVENT_AFTER_REPLACE_ASSET,
371
            function (ReplaceAssetEvent $event) {
372
                Craft::debug(
373
                    'Assets::EVENT_AFTER_REPLACE_ASSET',
374
                    __METHOD__
375
                );
376
                /** @var Asset $element */
377
                $element = $event->asset;
378
                if (!empty($element->id)) {
379
                    ImageOptimize::$plugin->optimizedImages->resaveAsset($element->id);
380
                }
381
            }
382
        );
383
    }
384
385
    /**
386
     * Install our Element event handlers
387
     */
388
    protected function installElementEventHandlers()
389
    {
390
        // Handler: Elements::EVENT_BEFORE_SAVE_ELEMENT
391
        Event::on(
392
            Assets::class,
393
            Elements::EVENT_BEFORE_SAVE_ELEMENT,
394
            function (ElementEvent $event) {
395
                Craft::debug(
396
                    'Elements::EVENT_BEFORE_SAVE_ELEMENT',
397
                    __METHOD__
398
                );
399
                /** @var Asset $asset */
400
                $asset = $event->element;
401
                if (!$event->isNew) {
402
                    // Purge the URL
403
                    $purgeUrl = ImageOptimize::$transformClass::getPurgeUrl(
404
                        $asset,
405
                        ImageOptimize::$transformParams
406
                    );
407
                    if ($purgeUrl) {
408
                        ImageOptimize::$transformClass::purgeUrl($purgeUrl, ImageOptimize::$transformParams);
409
                    }
410
                }
411
            }
412
        );
413
414
        // Handler: Elements::EVENT_BEFORE_DELETE_ELEMENT
415
        Event::on(
416
            Asset::class,
417
            Elements::EVENT_BEFORE_DELETE_ELEMENT,
418
            function (ElementEvent $event) {
419
                Craft::debug(
420
                    'Elements::EVENT_BEFORE_DELETE_ELEMENT',
421
                    __METHOD__
422
                );
423
                /** @var Asset $asset */
424
                $asset = $event->element;
425
                // Purge the URL
426
                $purgeUrl = ImageOptimize::$transformClass::getPurgeUrl(
427
                    $asset,
428
                    ImageOptimize::$transformParams
429
                );
430
                if ($purgeUrl) {
431
                    ImageOptimize::$transformClass::purgeUrl($purgeUrl, ImageOptimize::$transformParams);
432
                }
433
            }
434
        );
435
    }
436
437
    /**
438
     * If the Field being saved is an OptimizedImages field, re-save the responsive
439
     * image variants automatically
440
     *
441
     * @param FieldEvent $event
442
     *
443
     * @throws \yii\base\InvalidConfigException
444
     */
445
    protected function checkForOptimizedImagesField(FieldEvent $event)
446
    {
447
        $thisField = $event->field;
448
        if ($thisField instanceof OptimizedImages) {
449
            $volumes = Craft::$app->getVolumes()->getAllVolumes();
450
            foreach ($volumes as $volume) {
451
                $needToReSave = false;
452
                /** @var FieldLayout $fieldLayout */
453
                /** @var Volume $volume */
454
                $fieldLayout = $volume->getFieldLayout();
455
                // Loop through the fields in the layout to see if it contains our field
456
                if ($fieldLayout) {
457
                    $fields = $fieldLayout->getFields();
458
                    foreach ($fields as $field) {
459
                        if ($thisField->handle == $field->handle) {
460
                            $needToReSave = true;
461
                        }
462
                    }
463
                    if ($needToReSave) {
464
                        ImageOptimize::$plugin->optimizedImages->resaveVolumeAssets($volume);
465
                    }
466
                }
467
            }
468
        }
469
    }
470
}
471