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

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