PluginProbe
Elementor Website Builder – more than just a page builder / 3.34.1
Elementor Website Builder – more than just a page builder v3.34.1
4.3.0-beta2 4.3.0-beta1 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.5 4.2.0-beta2 4.2.0-dev2 4.2.0-beta1 4.1.4 4.1.3 4.1.2 4.1.1 4.1.0 4.1.0-beta3 4.1.0-dev3 4.0.9 4.1.0-beta2 4.1.0-dev2 4.0.8 4.1.0-beta1 4.1.0-dev1 4.0.7 All 451 releases
← All changes | modules/interactions/validation.php +19 -302 4.2.0-dev23.34.1 View file →
@@ -1,25 +1,20 @@
1 1 <?php
2 2
3 3 namespace Elementor\Modules\Interactions;
4 4
5 -use Elementor\Modules\Interactions\Validators\Breakpoints_Value as BreakpointsValueValidator;
6 -use Elementor\Modules\Interactions\Validators\Custom_Effect_Value;
7 -use Elementor\Modules\Interactions\Validators\String_Value as StringValueValidator;
8 -use Elementor\Modules\Interactions\Validators\Trigger_Value as TriggerValueValidator;
9 -
10 5 if ( ! defined( 'ABSPATH' ) ) {
11 6 exit; // Exit if accessed directly.
12 7 }
13 8
14 9 class Validation {
10 + private $valid_ids = [];
15 11 private $elements_to_interactions_counter = [];
16 12 private $max_number_of_interactions = 5;
17 13
18 - private const VALID_EFFECTS = [ 'fade', 'slide', 'scale', 'custom' ];
19 - private const VALID_TYPES = [ 'in', 'out' ];
20 - private const VALID_DIRECTIONS = [ '', 'left', 'right', 'top', 'bottom', 'top-left', 'top-right', 'bottom-left', 'bottom-right' ];
21 - private const VALID_REPEAT_MODES = [ '', 'loop', 'times' ];
14 + public function __construct( Presets $presets ) {
15 + $this->valid_ids = array_column( $presets->list(), 'value' );
16 + }
22 17
23 18 public function sanitize( $document ) {
24 19 return $this->sanitize_document_data( $document );
25 20 }
@@ -28,9 +23,9 @@
28 23 foreach ( $this->elements_to_interactions_counter as $element_id => $number_of_interactions ) {
29 24 if ( $number_of_interactions > $this->max_number_of_interactions ) {
30 25 throw new \Exception(
31 26 sprintf(
32 - // translators: %1$s: element ID, %2$d: maximum number of interactions allowed.
27 + // translators: %1 is the element ID and %2 is the maximum number of interactions
33 28 esc_html__( 'Element %1$s has more than %2$d interactions', 'elementor' ),
34 29 esc_html( $element_id ),
35 30 esc_html( $this->max_number_of_interactions )
36 31 )
@@ -68,11 +63,8 @@
68 63 }
69 64
70 65 private function decode_interactions( $interactions ) {
71 66 if ( is_array( $interactions ) ) {
72 - if ( isset( $interactions['items']['$$type'] ) && 'array' === $interactions['items']['$$type'] ) {
73 - return isset( $interactions['items']['value'] ) ? $interactions['items']['value'] : [];
74 - }
75 67 return isset( $interactions['items'] ) ? $interactions['items'] : [];
76 68 }
77 69
78 70 if ( is_string( $interactions ) ) {
@@ -77,11 +69,8 @@
77 69
78 70 if ( is_string( $interactions ) ) {
79 71 $decoded = json_decode( $interactions, true );
80 72 if ( json_last_error() === JSON_ERROR_NONE && is_array( $decoded ) ) {
81 - if ( isset( $decoded['items']['$$type'] ) && 'array' === $decoded['items']['$$type'] ) {
82 - return isset( $decoded['items']['value'] ) ? $decoded['items']['value'] : [];
83 - }
84 73 return isset( $decoded['items'] ) ? $decoded['items'] : [];
85 74 }
86 75 }
87 76
@@ -106,9 +95,17 @@
106 95
107 96 $list_of_interactions = $this->decode_interactions( $interactions );
108 97
109 98 foreach ( $list_of_interactions as $interaction ) {
110 - if ( $this->is_valid_interaction_item( $interaction ) ) {
99 + $animation_id = null;
100 +
101 + if ( is_string( $interaction ) ) {
102 + $animation_id = $interaction;
103 + } elseif ( is_array( $interaction ) && isset( $interaction['animation']['animation_id'] ) ) {
104 + $animation_id = $interaction['animation']['animation_id'];
105 + }
106 +
107 + if ( $animation_id && $this->is_valid_animation_id( $animation_id ) ) {
111 108 $sanitized['items'][] = $interaction;
112 109 $this->increment_interactions_counter_for( $element_id );
113 110 }
114 111 }
@@ -119,298 +116,18 @@
119 116
120 117 return wp_json_encode( $sanitized );
121 118 }
122 119
123 - private function is_valid_interaction_item( $item ) {
124 - if ( ! is_array( $item ) ) {
120 + private function is_valid_animation_id( $animation_id ) {
121 + if ( ! is_string( $animation_id ) || empty( $animation_id ) ) {
125 122 return false;
126 123 }
127 124
128 - // Validate PropType format: { $$type: 'interaction-item', value: { ... } }
129 - if ( ! isset( $item['$$type'] ) || 'interaction-item' !== $item['$$type'] ) {
130 - return false;
131 - }
125 + $sanitized_id = sanitize_text_field( $animation_id );
132 126
133 - if ( ! isset( $item['value'] ) || ! is_array( $item['value'] ) ) {
127 + if ( $sanitized_id !== $animation_id ) {
134 128 return false;
135 129 }
136 130
137 - $value = $item['value'];
138 -
139 - // Validate required fields exist
140 - if ( isset( $value['interaction_id'] ) && ! $this->is_valid_string_prop( $value, 'interaction_id' ) ) {
141 - return false;
142 - }
143 -
144 - if ( ! $this->is_valid_trigger_prop( $value ) ) {
145 - return false;
146 - }
147 -
148 - if ( ! $this->is_valid_animation_prop( $value ) ) {
149 - return false;
150 - }
151 -
152 - if ( ! $this->is_valid_breakpoints_prop( $value ) ) {
153 - return false;
154 - }
155 -
156 - return true;
157 - }
158 -
159 - private function is_valid_trigger_prop( $data ) {
160 - if ( ! array_key_exists( 'trigger', $data ) ) {
161 - return false;
162 - }
163 -
164 - return TriggerValueValidator::is_valid( $data['trigger'] );
165 - }
166 -
167 - private function is_valid_breakpoints_prop( $data ) {
168 - if ( array_key_exists( 'breakpoints', $data ) ) {
169 - return BreakpointsValueValidator::is_valid( $data['breakpoints'] );
170 - }
171 -
172 - return true;
173 - }
174 -
175 - private function is_valid_string_prop( $data, $key, $allowed_values = null ) {
176 - if ( ! isset( $data[ $key ] ) ) {
177 - return false;
178 - }
179 -
180 - return StringValueValidator::is_valid( $data[ $key ], $allowed_values );
181 - }
182 -
183 - private function is_valid_boolean_prop( $data, $key ) {
184 - if ( ! isset( $data[ $key ] ) || ! is_array( $data[ $key ] ) ) {
185 - return false;
186 - }
187 -
188 - $prop = $data[ $key ];
189 -
190 - if ( ! isset( $prop['$$type'] ) || 'boolean' !== $prop['$$type'] ) {
191 - return false;
192 - }
193 -
194 - if ( ! isset( $prop['value'] ) || ! is_bool( $prop['value'] ) ) {
195 - return false;
196 - }
197 -
198 - return true;
199 - }
200 -
201 - private function is_valid_number_prop( $data, $key ) {
202 - if ( ! isset( $data[ $key ] ) || ! is_array( $data[ $key ] ) ) {
203 - return false;
204 - }
205 -
206 - $prop = $data[ $key ];
207 -
208 - if ( ! isset( $prop['$$type'] ) || 'number' !== $prop['$$type'] ) {
209 - return false;
210 - }
211 -
212 - if ( ! isset( $prop['value'] ) || ! is_numeric( $prop['value'] ) ) {
213 - return false;
214 - }
215 -
216 - return true;
217 - }
218 -
219 - private function is_valid_number_prop_in_range( $data, $key, $min = null, $max = null ) {
220 - if ( ! $this->is_valid_number_prop( $data, $key ) ) {
221 - return false;
222 - }
223 -
224 - $value = (float) $data[ $key ]['value'];
225 -
226 - if ( null !== $min && $value < $min ) {
227 - return false;
228 - }
229 -
230 - if ( null !== $max && $value > $max ) {
231 - return false;
232 - }
233 -
234 - return true;
235 - }
236 -
237 - private function is_valid_config_prop( $data ) {
238 - if ( ! isset( $data['config'] ) || ! is_array( $data['config'] ) ) {
239 - return false;
240 - }
241 -
242 - $config_value = $data['config']['value'];
243 -
244 - if ( isset( $config_value['replay'] ) && ! $this->is_valid_boolean_prop( $config_value, 'replay' ) ) {
245 - return false;
246 - }
247 -
248 - if ( isset( $config_value['easing'] ) && ! $this->is_valid_string_prop( $config_value, 'easing' ) ) {
249 - return false;
250 - }
251 -
252 - if ( isset( $config_value['relativeTo'] ) && ! $this->is_valid_string_prop( $config_value, 'relativeTo' ) ) {
253 - return false;
254 - }
255 -
256 - if ( isset( $config_value['repeat'] ) && ! $this->is_valid_string_prop( $config_value, 'repeat', self::VALID_REPEAT_MODES ) ) {
257 - return false;
258 - }
259 -
260 - if ( isset( $config_value['times'] ) && ! $this->is_valid_number_prop_in_range( $config_value, 'times', 1 ) ) {
261 - return false;
262 - }
263 -
264 - if ( isset( $config_value['start'] ) && ! $this->is_valid_size_prop_in_range( $config_value, 'start', 0, 100 ) ) {
265 - return false;
266 - }
267 -
268 - if ( isset( $config_value['end'] ) && ! $this->is_valid_size_prop_in_range( $config_value, 'end', 0, 100 ) ) {
269 - return false;
270 - }
271 -
272 - return true;
273 - }
274 -
275 - private function is_valid_animation_prop( $data ) {
276 - if ( ! isset( $data['animation'] ) || ! is_array( $data['animation'] ) ) {
277 - return false;
278 - }
279 -
280 - $animation = $data['animation'];
281 -
282 - if ( ! isset( $animation['$$type'] ) || 'animation-preset-props' !== $animation['$$type'] ) {
283 - return false;
284 - }
285 -
286 - if ( ! isset( $animation['value'] ) || ! is_array( $animation['value'] ) ) {
287 - return false;
288 - }
289 -
290 - $animation_value = $animation['value'];
291 -
292 - // Validate effect
293 - if ( ! $this->is_valid_string_prop( $animation_value, 'effect', self::VALID_EFFECTS ) ) {
294 - return false;
295 - }
296 -
297 - // Validate type
298 - if ( ! $this->is_valid_string_prop( $animation_value, 'type', self::VALID_TYPES ) ) {
299 - return false;
300 - }
301 -
302 - // Validate direction (can be empty string)
303 - if ( ! $this->is_valid_string_prop( $animation_value, 'direction', self::VALID_DIRECTIONS ) ) {
304 - return false;
305 - }
306 -
307 - // Validate timing_config
308 - if ( ! $this->is_valid_timing_config( $animation_value ) ) {
309 - return false;
310 - }
311 -
312 - if ( isset( $animation_value['config'] ) && ! $this->is_valid_config_prop( $animation_value ) ) {
313 - return false;
314 - }
315 -
316 - if ( ! Custom_Effect_Value::is_valid( $animation_value ) ) {
317 - return false;
318 - }
319 -
320 - return true;
321 - }
322 -
323 - private function is_valid_timing_config( $data ) {
324 - if ( ! isset( $data['timing_config'] ) || ! is_array( $data['timing_config'] ) ) {
325 - return false;
326 - }
327 -
328 - $timing = $data['timing_config'];
329 -
330 - if ( ! isset( $timing['$$type'] ) || 'timing-config' !== $timing['$$type'] ) {
331 - return false;
332 - }
333 -
334 - if ( ! isset( $timing['value'] ) || ! is_array( $timing['value'] ) ) {
335 - return false;
336 - }
337 -
338 - $timing_value = $timing['value'];
339 -
340 - // Validate duration (accepts both 'number' and 'size' formats)
341 - if ( ! $this->is_valid_timing_value( $timing_value, 'duration', 0 ) ) {
342 - return false;
343 - }
344 -
345 - // Validate delay (accepts both 'number' and 'size' formats)
346 - if ( ! $this->is_valid_timing_value( $timing_value, 'delay', 0 ) ) {
347 - return false;
348 - }
349 -
350 - return true;
351 - }
352 -
353 - /**
354 - * Validate timing value that can be either 'number' or 'size' type.
355 - * - number format: {$$type: 'number', value: 123}
356 - * - size format: {$$type: 'size', value: {size: 123, unit: 'ms'}}
357 - */
358 - private function is_valid_timing_value( $data, $key, $min = null, $max = null ) {
359 - if ( ! isset( $data[ $key ] ) || ! is_array( $data[ $key ] ) ) {
360 - return false;
361 - }
362 -
363 - $prop = $data[ $key ];
364 -
365 - if ( ! isset( $prop['$$type'] ) ) {
366 - return false;
367 - }
368 -
369 - // Accept 'number' format
370 - if ( 'number' === $prop['$$type'] ) {
371 - return $this->is_valid_number_prop_in_range( $data, $key, $min, $max );
372 - }
373 -
374 - // Accept 'size' format
375 - if ( 'size' === $prop['$$type'] ) {
376 - return $this->is_valid_size_prop_in_range( $data, $key, $min, $max );
377 - }
378 -
379 - return false;
380 - }
381 -
382 - /**
383 - * Validate size prop: {$$type: 'size', value: {size: X, unit: 'ms'}}
384 - */
385 - private function is_valid_size_prop_in_range( $data, $key, $min = null, $max = null ) {
386 - if ( ! isset( $data[ $key ] ) || ! is_array( $data[ $key ] ) ) {
387 - return false;
388 - }
389 -
390 - $prop = $data[ $key ];
391 -
392 - if ( ! isset( $prop['$$type'] ) || 'size' !== $prop['$$type'] ) {
393 - return false;
394 - }
395 -
396 - if ( ! isset( $prop['value'] ) || ! is_array( $prop['value'] ) ) {
397 - return false;
398 - }
399 -
400 - if ( ! isset( $prop['value']['size'] ) || ! is_numeric( $prop['value']['size'] ) ) {
401 - return false;
402 - }
403 -
404 - $value = (float) $prop['value']['size'];
405 -
406 - if ( null !== $min && $value < $min ) {
407 - return false;
408 - }
409 -
410 - if ( null !== $max && $value > $max ) {
411 - return false;
412 - }
413 -
414 - return true;
131 + return in_array( $animation_id, $this->valid_ids, true );
415 132 }
416 133 }