PluginProbe
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF / 2.3.0
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF v2.3.0
2.3.4 2.3.3 2.3.2 2.3.1 2.3.0 2.2.9 2.2.8 trunk 1.10 1.3.3 1.3.4 1.3.5 1.3.5.1 1.3.5.2 1.3.6 1.3.6.1 1.4 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.5 All 103 releases
imagify / classes / Abilities / UpdateSettings.php

UpdateSettings.php in Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF 2.3.0, at classes/Abilities/UpdateSettings.php

346 lines 10.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 declare(strict_types=1);
3
4 namespace Imagify\Abilities;
5
6 /**
7 * MCP ability: updates one or more Imagify configuration settings.
8 *
9 * Registers itself with the WP Abilities API under the slug
10 * `imagify/update-settings`. Accepts a partial settings object and
11 * updates only the supplied keys.
12 *
13 * @since 2.3.0
14 */
15 class UpdateSettings extends AbstractAbility {
16
17 /**
18 * Returns the ability slug.
19 *
20 * @return string
21 */
22 public function get_id(): string {
23 return 'imagify/update-settings';
24 }
25
26 /**
27 * Returns the ability label.
28 *
29 * @return string
30 */
31 public function get_name(): string {
32 return 'Update Imagify settings';
33 }
34
35 /**
36 * Register the ability with the WP Abilities API.
37 *
38 * No-ops gracefully when the API is not available (WP < 6.9).
39 *
40 * @return void
41 */
42 public function register(): void {
43 if ( ! function_exists( 'wp_register_ability' ) ) {
44 return;
45 }
46
47 wp_register_ability(
48 'imagify/update-settings',
49 [
50 'label' => __( 'Update Imagify settings', 'imagify' ),
51 'description' => __( 'Updates one or more Imagify configuration options. Only supplied keys are changed; others remain unchanged.', 'imagify' ),
52 'category' => 'imagify',
53 'execute_callback' => [ $this, 'execute' ],
54 'permission_callback' => [ $this, 'check_permissions' ],
55 'meta' => [
56 'show_in_rest' => true,
57 'mcp' => [ 'public' => true ],
58 'annotations' => [
59 'readonly' => false,
60 'destructive' => false,
61 'idempotent' => true,
62 ],
63 ],
64 'input_schema' => [
65 'type' => 'object',
66 'properties' => [
67 'api_key' => [
68 'type' => 'string',
69 'description' => __( 'Imagify API key. Cannot be updated when IMAGIFY_API_KEY constant is defined.', 'imagify' ),
70 ],
71 'optimization_level' => [
72 'type' => 'integer',
73 'minimum' => 0,
74 'maximum' => 2,
75 'description' => __( 'Optimization aggressiveness: 0 = normal, 1 = aggressive, 2 = ultra.', 'imagify' ),
76 ],
77 'lossless' => [
78 'type' => 'integer',
79 'enum' => [ 0, 1 ],
80 'description' => __( 'Enable lossless compression (0 = off, 1 = on).', 'imagify' ),
81 ],
82 'auto_optimize' => [
83 'type' => 'integer',
84 'enum' => [ 0, 1 ],
85 'description' => __( 'Automatically optimize images on upload (0 = off, 1 = on).', 'imagify' ),
86 ],
87 'backup' => [
88 'type' => 'integer',
89 'enum' => [ 0, 1 ],
90 'description' => __( 'Keep a backup of original images (0 = off, 1 = on).', 'imagify' ),
91 ],
92 'resize_larger' => [
93 'type' => 'integer',
94 'enum' => [ 0, 1 ],
95 'description' => __( 'Resize images wider than resize_larger_w (0 = off, 1 = on).', 'imagify' ),
96 ],
97 'resize_larger_w' => [
98 'type' => 'integer',
99 'description' => __( 'Maximum image width in pixels when resize_larger is enabled.', 'imagify' ),
100 ],
101 'display_nextgen' => [
102 'type' => 'integer',
103 'enum' => [ 0, 1 ],
104 'description' => __( 'Serve next-gen images to supported browsers (0 = off, 1 = on).', 'imagify' ),
105 ],
106 'display_nextgen_method' => [
107 'type' => 'string',
108 'enum' => [ 'picture', 'rewrite' ],
109 'description' => __( 'Delivery method for next-gen images: picture element or URL rewrite.', 'imagify' ),
110 ],
111 'display_webp' => [
112 'type' => 'integer',
113 'enum' => [ 0, 1 ],
114 'description' => __( 'Serve WebP images to supported browsers (0 = off, 1 = on).', 'imagify' ),
115 ],
116 'display_webp_method' => [
117 'type' => 'string',
118 'enum' => [ 'picture', 'rewrite' ],
119 'description' => __( 'Delivery method for WebP images: picture element or URL rewrite.', 'imagify' ),
120 ],
121 'cdn_url' => [
122 'type' => 'string',
123 'description' => __( 'CDN base URL for serving optimized images.', 'imagify' ),
124 ],
125 'disallowed-sizes' => [
126 'type' => 'object',
127 'description' => __( 'Image sizes excluded from optimization, keyed by size slug.', 'imagify' ),
128 ],
129 'admin_bar_menu' => [
130 'type' => 'integer',
131 'enum' => [ 0, 1 ],
132 'description' => __( 'Show Imagify entry in the WordPress admin bar (0 = off, 1 = on).', 'imagify' ),
133 ],
134 'partner_links' => [
135 'type' => 'integer',
136 'enum' => [ 0, 1 ],
137 'description' => __( 'Show partner links in the Imagify admin area (0 = off, 1 = on).', 'imagify' ),
138 ],
139 'convert_to_avif' => [
140 'type' => 'integer',
141 'enum' => [ 0, 1 ],
142 'description' => __( 'Convert images to AVIF format (0 = off, 1 = on).', 'imagify' ),
143 ],
144 'convert_to_webp' => [
145 'type' => 'integer',
146 'enum' => [ 0, 1 ],
147 'description' => __( 'Convert images to WebP format (0 = off, 1 = on).', 'imagify' ),
148 ],
149 'optimization_format' => [
150 'type' => 'string',
151 'enum' => [ 'off', 'webp', 'avif' ],
152 'description' => __( 'Output format for optimized images.', 'imagify' ),
153 ],
154 ],
155 ],
156 'output_schema' => [
157 'type' => 'object',
158 'required' => [ 'updated', 'settings' ],
159 'properties' => [
160 'updated' => [
161 'type' => 'array',
162 'items' => [ 'type' => 'string' ],
163 'description' => __( 'Keys whose values changed after the update.', 'imagify' ),
164 ],
165 'settings' => [
166 'type' => 'object',
167 'description' => __( 'Full post-update settings, excluding api_key and version.', 'imagify' ),
168 ],
169 ],
170 ],
171 ]
172 );
173 }
174
175 /**
176 * Check if the current user has permission to execute this ability.
177 *
178 * Routes through Imagify's capability abstraction so the `imagify_capacity`
179 * filter and multisite network-admin logic are honoured.
180 *
181 * @return bool True when the current user has the Imagify `manage` capability.
182 */
183 protected function has_permission(): bool {
184 return imagify_get_context( 'wp' )->current_user_can( 'manage' );
185 }
186
187 /**
188 * Validate a single setting value against known constraints.
189 *
190 * Returns WP_Error for clearly invalid constrained values; otherwise
191 * returns the value untouched and lets the WP option filter normalize it.
192 *
193 * @param string $key Setting key.
194 * @param mixed $value Proposed value.
195 * @return mixed|\WP_Error
196 */
197 protected function validate_value( string $key, $value ) {
198 switch ( $key ) {
199 case 'optimization_level':
200 if ( ! in_array( (int) $value, [ 0, 1, 2 ], true ) ) {
201 return new \WP_Error(
202 'imagify_invalid_value',
203 sprintf(
204 /* translators: 1: setting key name, 2: list of allowed values. */
205 __( 'Invalid value for "%1$s". Expected one of: %2$s.', 'imagify' ),
206 $key,
207 '0, 1, 2'
208 )
209 );
210 }
211 break;
212
213 case 'optimization_format':
214 if ( ! in_array( $value, [ 'off', 'webp', 'avif' ], true ) ) {
215 return new \WP_Error(
216 'imagify_invalid_value',
217 sprintf(
218 /* translators: 1: setting key name, 2: list of allowed values. */
219 __( 'Invalid value for "%1$s". Expected one of: %2$s.', 'imagify' ),
220 $key,
221 '"off", "webp", "avif"'
222 )
223 );
224 }
225 break;
226
227 case 'display_nextgen_method':
228 case 'display_webp_method':
229 if ( ! in_array( $value, [ 'picture', 'rewrite' ], true ) ) {
230 return new \WP_Error(
231 'imagify_invalid_value',
232 sprintf(
233 /* translators: 1: setting key name, 2: list of allowed values. */
234 __( 'Invalid value for "%1$s". Expected one of: %2$s.', 'imagify' ),
235 $key,
236 '"picture", "rewrite"'
237 )
238 );
239 }
240 break;
241 }
242
243 return $value;
244 }
245
246 /**
247 * Fetch the Imagify_Options singleton.
248 *
249 * Extracted so tests can override without re-mocking the global singleton.
250 *
251 * @return \Imagify_Options
252 */
253 protected function fetch_options_instance(): \Imagify_Options {
254 return \Imagify_Options::get_instance();
255 }
256
257 /**
258 * Execute the ability: update Imagify settings.
259 *
260 * Accepts a partial settings object. Only provided keys are updated;
261 * others remain unchanged. Invalid values are rejected with WP_Error.
262 *
263 * @param mixed $args Partial settings to update.
264 * @return array|\WP_Error Array with `updated` and `settings` keys on success.
265 */
266 public function execute( $args = [] ) {
267 $start_time = microtime( true );
268 $result = $this->do_execute( $args );
269
270 $this->fire_executed( $result, $start_time, is_array( $args ) ? $args : [] );
271
272 return $result;
273 }
274
275 /**
276 * Internal execution logic for the ability.
277 *
278 * @param mixed $args Partial settings to update.
279 * @return array|\WP_Error
280 */
281 private function do_execute( $args ) {
282 $options = $this->fetch_options_instance();
283 $defaults = $options->get_default_values();
284 $before = $options->get_all();
285
286 $to_update = [];
287
288 foreach ( $args as $key => $value ) {
289 if ( ! array_key_exists( $key, $defaults ) ) {
290 return new \WP_Error(
291 'imagify_unknown_setting',
292 sprintf(
293 /* translators: %s: the unknown setting key name. */
294 __( '"%s" is not a valid Imagify setting key.', 'imagify' ),
295 $key
296 )
297 );
298 }
299
300 if ( 'version' === $key ) {
301 return new \WP_Error(
302 'imagify_unknown_setting',
303 sprintf(
304 /* translators: %s: the unknown setting key name. */
305 __( '"%s" is not a valid Imagify setting key.', 'imagify' ),
306 $key
307 )
308 );
309 }
310
311 if ( 'api_key' === $key && defined( 'IMAGIFY_API_KEY' ) && IMAGIFY_API_KEY ) {
312 return new \WP_Error(
313 'imagify_api_key_immutable',
314 /* translators: IMAGIFY_API_KEY is a PHP constant name, do not translate it. */
315 __( 'api_key cannot be updated when IMAGIFY_API_KEY constant is defined.', 'imagify' )
316 );
317 }
318
319 $validated = $this->validate_value( $key, $value );
320 if ( is_wp_error( $validated ) ) {
321 return $validated;
322 }
323
324 $to_update[ $key ] = $validated;
325 }
326
327 $options->set( $to_update );
328
329 $after = $options->get_all();
330 $updated_keys = [];
331 foreach ( array_keys( $to_update ) as $key ) {
332 if ( ( $before[ $key ] ?? null ) !== ( $after[ $key ] ?? null ) ) {
333 $updated_keys[] = $key;
334 }
335 }
336
337 $settings = $after;
338 unset( $settings['version'], $settings['api_key'] );
339
340 return [
341 'updated' => $updated_keys,
342 'settings' => $settings,
343 ];
344 }
345 }
346