PluginProbe
Elementor Website Builder – more than just a page builder / 3.30.3
Elementor Website Builder – more than just a page builder v3.30.3
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
elementor / modules / atomic-widgets / parsers / style-parser.php

style-parser.php in Elementor Website Builder – more than just a page builder 3.30.3, at modules/atomic-widgets/parsers/style-parser.php

234 lines 5.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Elementor\Modules\AtomicWidgets\Parsers;
4
5 if ( ! defined( 'ABSPATH' ) ) {
6 exit; // Exit if accessed directly.
7 }
8
9 use Elementor\Modules\AtomicWidgets\Module;
10 use Elementor\Plugin;
11
12 class Style_Parser {
13 const VALID_TYPES = [
14 'class',
15 ];
16
17 const VALID_STATES = [
18 'hover',
19 'active',
20 'focus',
21 null,
22 ];
23
24 private array $schema;
25
26 public function __construct( array $schema ) {
27 $this->schema = $schema;
28 }
29
30 public static function make( array $schema ): self {
31 return new static( $schema );
32 }
33
34 /**
35 * @param array $style
36 * the style object to validate
37 */
38 private function validate( array $style ): Parse_Result {
39 $validated_style = $style;
40 $result = Parse_Result::make();
41
42 if ( ! isset( $style['id'] ) || ! is_string( $style['id'] ) ) {
43 $result->errors()->add( 'id', 'missing_or_invalid' );
44 }
45
46 if ( ! isset( $style['type'] ) || ! in_array( $style['type'], self::VALID_TYPES, true ) ) {
47 $result->errors()->add( 'type', 'missing_or_invalid' );
48 }
49
50 if ( ! isset( $style['label'] ) || ! is_string( $style['label'] ) ) {
51 $result->errors()->add( 'label', 'missing_or_invalid' );
52 } elseif ( Plugin::$instance->experiments->is_feature_active( Module::EXPERIMENT_VERSION_3_30 ) ) {
53 $label_validation = $this->validate_style_label( $style['label'] );
54
55 if ( ! $label_validation['is_valid'] ) {
56 $result->errors()->add( 'label', $label_validation['error_message'] );
57 }
58 }
59
60 if ( ! isset( $style['variants'] ) || ! is_array( $style['variants'] ) ) {
61 $result->errors()->add( 'variants', 'missing_or_invalid' );
62
63 unset( $validated_style['variants'] );
64
65 return $result->wrap( $validated_style );
66 }
67
68 $props_parser = Props_Parser::make( $this->schema );
69
70 foreach ( $style['variants'] as $variant_index => $variant ) {
71 if ( ! isset( $variant['meta'] ) ) {
72 $result->errors()->add( 'meta', 'missing' );
73
74 continue;
75 }
76
77 $meta_result = $this->validate_meta( $variant['meta'] );
78
79 $result->errors()->merge( $meta_result->errors(), 'meta' );
80
81 if ( $meta_result->is_valid() ) {
82 $variant_result = $props_parser->validate( $variant['props'] );
83
84 $result->errors()->merge( $variant_result->errors(), "variants[$variant_index]" );
85
86 $validated_style['variants'][ $variant_index ]['props'] = $variant_result->unwrap();
87 } else {
88 unset( $validated_style['variants'][ $variant_index ] );
89 }
90 }
91
92 return $result->wrap( $validated_style );
93 }
94
95 private function validate_style_label( string $label ): array {
96 $label = strtolower( $label );
97
98 $reserved_class_names = [ 'container' ];
99
100 if ( strlen( $label ) > 50 ) {
101 return [
102 'is_valid' => false,
103 'error_message' => 'class_name_too_long',
104 ];
105 }
106
107 if ( strlen( $label ) < 2 ) {
108 return [
109 'is_valid' => false,
110 'error_message' => 'class_name_too_short',
111 ];
112 }
113
114 if ( in_array( $label, $reserved_class_names, true ) ) {
115 return [
116 'is_valid' => false,
117 'error_message' => 'reserved_class_name',
118 ];
119 }
120
121 $regexes = [
122 [
123 'pattern' => '/^(|[^0-9].*)$/',
124 'message' => 'class_name_starts_with_digit',
125 ],
126 [
127 'pattern' => '/^\S*$/',
128 'message' => 'class_name_contains_spaces',
129 ],
130 [
131 'pattern' => '/^(|[a-zA-Z0-9_-]+)$/',
132 'message' => 'class_name_invalid_chars',
133 ],
134 [
135 'pattern' => '/^(?!--).*/',
136 'message' => 'class_name_double_hyphen',
137 ],
138 [
139 'pattern' => '/^(?!-[0-9])/',
140 'message' => 'class_name_starts_with_hyphen_digit',
141 ],
142 ];
143
144 foreach ( $regexes as $rule ) {
145 if ( ! preg_match( $rule['pattern'], $label ) ) {
146 return [
147 'is_valid' => false,
148 'error_message' => $rule['message'],
149 ];
150 }
151 }
152 return [
153 'is_valid' => true,
154 'error_message' => null,
155 ];
156 }
157
158 private function validate_meta( $meta ): Parse_Result {
159 $result = Parse_Result::make();
160
161 if ( ! is_array( $meta ) ) {
162 $result->errors()->add( 'meta', 'invalid_type' );
163
164 return $result;
165 }
166
167 if ( ! array_key_exists( 'state', $meta ) || ! in_array( $meta['state'], self::VALID_STATES, true ) ) {
168 $result->errors()->add( 'state', 'missing_or_invalid_value' );
169
170 return $result;
171 }
172
173 // TODO: Validate breakpoint based on the existing breakpoints in the system [EDS-528]
174 if ( ! isset( $meta['breakpoint'] ) || ! is_string( $meta['breakpoint'] ) ) {
175 $result->errors()->add( 'breakpoint', 'missing_or_invalid_value' );
176
177 return $result;
178 }
179
180 return $result;
181 }
182
183 private function sanitize_meta( $meta ) {
184 if ( ! is_array( $meta ) ) {
185 return [];
186 }
187
188 if ( isset( $meta['breakpoint'] ) ) {
189 $meta['breakpoint'] = sanitize_key( $meta['breakpoint'] );
190 }
191
192 return $meta;
193 }
194
195 /**
196 * @param array $style
197 * the style object to sanitize
198 */
199 private function sanitize( array $style ): Parse_Result {
200 $props_parser = Props_Parser::make( $this->schema );
201
202 if ( isset( $style['label'] ) ) {
203 $style['label'] = sanitize_text_field( $style['label'] );
204 }
205
206 if ( isset( $style['id'] ) ) {
207 $style['id'] = sanitize_key( $style['id'] );
208 }
209
210 if ( ! empty( $style['variants'] ) ) {
211 foreach ( $style['variants'] as $variant_index => $variant ) {
212 $style['variants'][ $variant_index ]['props'] = $props_parser->sanitize( $variant['props'] )->unwrap();
213 $style['variants'][ $variant_index ]['meta'] = $this->sanitize_meta( $variant['meta'] );
214 }
215 }
216
217 return Parse_Result::make()->wrap( $style );
218 }
219
220 /**
221 * @param array $style
222 * the style object to parse
223 */
224 public function parse( array $style ): Parse_Result {
225 $validate_result = $this->validate( $style );
226
227 $sanitize_result = $this->sanitize( $validate_result->unwrap() );
228
229 $sanitize_result->errors()->merge( $validate_result->errors() );
230
231 return $sanitize_result;
232 }
233 }
234