PluginProbe
JetFormBuilder — Dynamic Blocks Form Builder / 3.6.6
JetFormBuilder — Dynamic Blocks Form Builder v3.6.6
3.6.6.1 3.6.6 3.6.5.4 3.6.5.3 3.6.5.2 3.6.5.1 3.6.5 3.6.4.2 3.6.4.1 3.6.4 3.6.3.1 3.6.3 3.6.2.2 3.6.2.1 3.6.2 3.6.1.1 3.6.1 3.6.0.1 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 All 134 releases
← All changes | includes/presets/sources/base-source.php +395 -120 1.1.0 → 3.6.6 View file →
@@ -1,120 +1,395 @@
1 -<?php
2 -
3 -
4 -namespace Jet_Form_Builder\Presets\Sources;
5 -
6 -
7 -use Jet_Form_Builder\Classes\Tools;
8 -use Jet_Form_Builder\Dev_Mode\Manager;
9 -use Jet_Form_Builder\Exceptions\Preset_Exception;
10 -use Jet_Form_Builder\Presets\Types\Base_Preset;
11 -
12 -abstract class Base_Source {
13 -
14 - protected $fields_map;
15 - protected $field_data = array();
16 - protected $field_args;
17 - protected $preset_data;
18 - protected $field = '__condition__';
19 - protected $prop;
20 - private $src;
21 -
22 - const FUNC_PREFIX = '_source__';
23 - const EMPTY = '';
24 -
25 - abstract public function query_source();
26 -
27 - public function __construct( $fields_map, $field_args, $preset_data ) {
28 - $this->fields_map = $fields_map;
29 - $this->field_args = $field_args;
30 - $this->preset_data = $preset_data;
31 - $this->field = $this->field_args['name'];
32 - $this->field_data = $this->get_field_data();
33 - $this->prop = $this->get_prop();
34 - $this->src = $this->query_source();
35 - }
36 -
37 -
38 - protected function get_field_data() {
39 - if ( $this->has_field_in_map() ) {
40 - return $this->fields_map[ $this->field ];
41 - }
42 - }
43 -
44 - public function has_field_in_map() {
45 - return ( isset( $this->fields_map[ $this->field ] ) && ( isset( $this->fields_map[ $this->field ]['prop'] ) || isset( $this->fields_map[ $this->field ]['key'] ) ) );
46 - }
47 -
48 - public function src() {
49 - return $this->src;
50 - }
51 -
52 - protected function can_get_preset() {
53 - return ( ! empty( $this->src() ) && ! is_wp_error( $this->src() ) );
54 - }
55 -
56 - public function default_prop( string $prop ) {
57 - $source = $this->src;
58 -
59 - if ( isset( $source->$prop ) ) {
60 - return $source->$prop;
61 - } elseif ( isset( $source->data ) && isset( $source->data->$prop ) ) {
62 - return $source->data->$prop;
63 - }
64 -
65 - return self::EMPTY;
66 - }
67 -
68 - protected function get_prop() {
69 - return ( ! empty( $this->field_data['prop'] ) ? $this->field_data['prop'] : 'post_title' );
70 - }
71 -
72 - public function get_result_on_prop() {
73 - $func_name = self::FUNC_PREFIX . $this->prop;
74 -
75 - if ( is_callable( array( $this, $func_name ) ) ) {
76 - return call_user_func( array( $this, $func_name ) );
77 - }
78 -
79 - return $this->default_prop( $this->prop );
80 - }
81 -
82 - final public function result() {
83 - if ( ! $this->can_get_preset() ) {
84 - return self::EMPTY;
85 - }
86 -
87 - return $this->parse_result_value( $this->get_result_on_prop() );
88 - }
89 -
90 -
91 - /**
92 - * Try to get values from request if passed
93 - *
94 - * @param [type] $args [description]
95 - *
96 - * @return [type] [description]
97 - */
98 - public function maybe_adjust_value() {
99 -
100 - $value = isset( $this->field_args['default'] ) ? $this->field_args['default'] : '';
101 - $request_val = ! empty( $_REQUEST['values'] ) ? $_REQUEST['values'] : array();
102 -
103 - if ( isset( $request_val[ $this->field ] ) ) {
104 - $value = $request_val[ $this->field ];
105 - }
106 -
107 - return $value;
108 -
109 - }
110 -
111 - public function parse_result_value( $value ) {
112 - // Prepare value for date field
113 - if ( 'date-field' === $this->field_args['type'] && Tools::is_valid_timestamp( $value ) ) {
114 - $value = date_i18n( 'Y-m-d', $value );
115 - }
116 -
117 - return $value;
118 - }
119 -
120 -}
1 +<?php
2 +
3 +
4 +namespace Jet_Form_Builder\Presets\Sources;
5 +
6 +use Jet_Form_Builder\Blocks\Types\Base;
7 +use Jet_Form_Builder\Exceptions\Preset_Exception;
8 +use Jet_Form_Builder\Presets\Preset_Manager;
9 +use JFB_Modules\Rich_Content\Macros_Parser;
10 +
11 +// If this file is called directly, abort.
12 +if ( ! defined( 'WPINC' ) ) {
13 + die;
14 +}
15 +
16 +abstract class Base_Source {
17 +
18 + protected $fields_map;
19 + protected $field_data = array();
20 + protected $field_args;
21 + protected $preset_data;
22 + protected $field = '__condition__';
23 + protected $prop;
24 + private $src;
25 +
26 + protected $permission;
27 +
28 + const FUNC_PREFIX = 'source__';
29 +
30 + abstract public function query_source();
31 +
32 + abstract public function get_id();
33 +
34 + public function condition(): bool {
35 + return true;
36 + }
37 +
38 + /**
39 + * Field args getter
40 + *
41 + * @return array
42 + */
43 + public function get_field_args() {
44 + return $this->field_args;
45 + }
46 +
47 + /**
48 + * Fields map getter
49 + *
50 + * @return array
51 + */
52 + public function get_fields_map() {
53 + return $this->fields_map;
54 + }
55 +
56 + /**
57 + * @param $fields_map
58 + * @param $preset_data
59 + * @param $args
60 + *
61 + * @return $this
62 + * @throws Preset_Exception
63 + */
64 + public function init_source( $fields_map, $preset_data, $args ): Base_Source {
65 + $this->field_args = $args;
66 + $this->field = $args['name'] ?? '';
67 + $this->fields_map = $fields_map;
68 + $this->preset_data = $preset_data;
69 + $this->field_data = $this->get_field_data();
70 + $this->prop = $this->get_prop();
71 +
72 + return $this;
73 + }
74 +
75 + public function after_init(): Base_Source {
76 + return $this;
77 + }
78 +
79 + public function after_register() {
80 + }
81 +
82 + public function on_sanitize(): bool {
83 + return true;
84 + }
85 +
86 + public function is_need_prop() {
87 + return true;
88 + }
89 +
90 + /**
91 + * @return mixed
92 + * @throws Preset_Exception
93 + */
94 + public function maybe_query_source() {
95 + if ( $this->prop ) {
96 + $this->src = $this->query_source();
97 +
98 + return $this;
99 + }
100 +
101 + // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
102 + throw new Preset_Exception( 'Empty `prop` in ' . get_class( $this ), $this->field_data );
103 + }
104 +
105 + /**
106 + * @return mixed
107 + * @throws Preset_Exception
108 + */
109 + public function get_field_data() {
110 + if ( $this->has_field_in_map() ) {
111 + return $this->fields_map[ $this->field ];
112 + }
113 + // phpcs:disable WordPress.Security.EscapeOutput.ExceptionNotEscaped
114 + throw new Preset_Exception(
115 + "Empty `fields_map['{$this->field}']` in " . get_class( $this ),
116 + $this->fields_map
117 + );
118 + // phpcs:enable WordPress.Security.EscapeOutput.ExceptionNotEscaped
119 + }
120 +
121 + public function has_field_in_map() {
122 + return ( isset( $this->fields_map[ $this->field ]['prop'] ) || isset( $this->fields_map[ $this->field ]['key'] ) );
123 + }
124 +
125 + /**
126 + * @return mixed
127 + */
128 + public function src() {
129 + return $this->src;
130 + }
131 +
132 + /**
133 + * @return mixed
134 + * @throws Preset_Exception
135 + */
136 + public function safe_src() {
137 + $this->throw_if_preset_not_available();
138 +
139 + return $this->src();
140 + }
141 +
142 + /**
143 + * @return bool
144 + * @throws Preset_Exception
145 + */
146 + protected function can_get_preset() {
147 + return ( ! empty( $this->src() ) && ! is_wp_error( $this->src() ) );
148 + }
149 +
150 + /**
151 + * Whether this source allows the `restricted: false` opt-out (see
152 + * has_permission()) to skip can_get_preset() at all. A source can override
153 + * this to return false when its capability must never be made public by a
154 + * form's stored, trusted preset configuration.
155 + *
156 + * Preset_Source_Options_Page intentionally uses the default: a form author
157 + * can explicitly publish the selected Options Page field by switching
158 + * "Restrict access" off. Request-provided preset data cannot do so because
159 + * it never receives the trusted-origin marker. See issues-tracker #20359.
160 + *
161 + * @return bool
162 + */
163 + protected function allows_restriction_bypass(): bool {
164 + return true;
165 + }
166 +
167 + /**
168 + * The `restricted: false` opt-out below only takes effect when both:
169 + * - `_trusts_restriction_flag` is true - a flag set programmatically by
170 + * Base_Preset::get_source() (never taken from preset_data itself, so
171 + * an attacker can't forge it) from whatever the CALL SITE declared via
172 + * Base_Preset::trust_restriction_flag(). Only origins that are
173 + * admin-authored declare trust: the form's own preset meta
174 + * (General_Preset), a field block's attributes
175 + * (Preset_Manager::get_field_value()) and the settings parsed via
176 + * jet_fb_parse_dynamic_trusted() (validation rules, date min/max,
177 + * conditional blocks, action conditions, dynamic value). Parsing a
178 + * submitted field value - jet_fb_parse_dynamic() via Rich_Content -
179 + * never does, so a `restricted` flag smuggled through a submitted
180 + * value is ignored there.
181 + * - allows_restriction_bypass() is true for this source (see above).
182 + *
183 + * This closes the access-control bypass reported in issues-tracker
184 + * #20359 while preserving the documented "Restrict access" editor
185 + * toggle for admin-configured presets.
186 + *
187 + * `jet-form-builder/preset-sanitize` runs on BOTH paths - the opt-out
188 + * result as well as can_get_preset() - so an integrator can always
189 + * further restrict access, including on presets whose author switched
190 + * the toggle off. Returning true from it does not grant access that
191 + * can_get_preset() denied on the checked path, because the filter only
192 + * ever sees an already-computed decision.
193 + *
194 + * @return bool
195 + * @throws Preset_Exception
196 + */
197 + protected function has_permission(): bool {
198 + if ( is_null( $this->permission ) ) {
199 + $this->permission = apply_filters(
200 + 'jet-form-builder/preset-sanitize',
201 + $this->allows_restriction_bypass_for_data() ? true : $this->can_get_preset(),
202 + $this
203 + );
204 + }
205 +
206 + return $this->permission;
207 + }
208 +
209 + /**
210 + * Whether the `restricted: false` opt-out applies to this preset data.
211 + *
212 + * All four conditions must hold, see has_permission() above.
213 + *
214 + * @return bool
215 + */
216 + private function allows_restriction_bypass_for_data(): bool {
217 + return (
218 + ! empty( $this->preset_data['_trusts_restriction_flag'] ) &&
219 + $this->allows_restriction_bypass() &&
220 + array_key_exists( 'restricted', $this->preset_data ) &&
221 + ! $this->preset_data['restricted']
222 + );
223 + }
224 +
225 + /**
226 + * @throws Preset_Exception
227 + */
228 + final protected function throw_if_preset_not_available() {
229 + if ( ! $this->has_permission() ) {
230 + throw new Preset_Exception( static::class . '::can_get_preset return FALSE' );
231 + }
232 + }
233 +
234 +
235 + protected function get_prop() {
236 + if ( ! $this->is_need_prop() ) {
237 + return true;
238 + }
239 +
240 + return ( ! empty( $this->field_data['prop'] ) ? $this->field_data['prop'] : false );
241 + }
242 +
243 + /**
244 + * Public prop getter
245 + *
246 + * @return string
247 + */
248 + public function get_prop_name() {
249 + return $this->prop;
250 + }
251 +
252 + /**
253 + * @return false|mixed
254 + * @throws Preset_Exception
255 + */
256 + public function get_result_on_prop() {
257 + if ( ! $this->is_need_prop() ) {
258 + return $this->src();
259 + }
260 +
261 + $extra = $this->get_extra_fields();
262 +
263 + if ( empty( $extra ) ) {
264 + return $this->get_current_value();
265 + }
266 +
267 + $value = array();
268 +
269 + foreach ( $extra as $name => $field ) {
270 + $this->before_query_extra_field( $field );
271 +
272 + $value[ $name ] = $this->get_current_value();
273 + }
274 +
275 + return $value;
276 + }
277 +
278 + protected function before_query_extra_field( $field ) {
279 + $this->field_data['key'] = $field;
280 + }
281 +
282 + /**
283 + * @return false|mixed
284 + * @throws Preset_Exception
285 + */
286 + private function get_current_value() {
287 + $func_name = self::FUNC_PREFIX . $this->prop;
288 +
289 + if ( is_callable( array( $this, $func_name ) ) ) {
290 + $result = call_user_func( array( $this, $func_name ) );
291 + } else {
292 + $result = $this->default_prop( $this->prop );
293 + }
294 +
295 + return apply_filters(
296 + 'jet-form-builder/preset/source/value',
297 + $result,
298 + $this
299 + );
300 + }
301 +
302 + private function get_extra_fields(): array {
303 + try {
304 + $extra = $this->get_field_object()->get_extra_fields( $this );
305 + } catch ( Preset_Exception $exception ) {
306 + return array();
307 + }
308 +
309 + $extra = apply_filters( 'jet-form-builder/preset/extra-fields', $extra, $this );
310 +
311 + $parser = ( new Macros_Parser() )->set_replacements(
312 + array(
313 + 'key' => $this->field_data['key'] ?? '',
314 + 'prop' => $this->prop,
315 + )
316 + );
317 +
318 + foreach ( $extra as $index => $field ) {
319 + $extra[ $index ] = $parser->parse_macros( $field );
320 + }
321 +
322 + return $extra;
323 + }
324 +
325 + /**
326 + * @param string $prop
327 + *
328 + * @return mixed
329 + * @throws Preset_Exception
330 + */
331 + public function default_prop( string $prop ) {
332 + $source = $this->src;
333 +
334 + if ( isset( $source->$prop ) ) {
335 + return $source->$prop;
336 + } elseif ( isset( $source->data ) && isset( $source->data->$prop ) ) {
337 + return $source->data->$prop;
338 + }
339 +
340 + if ( ! is_object( $source ) ) {
341 + throw new Preset_Exception( "Source isn't object" );
342 + }
343 +
344 + throw new Preset_Exception(
345 + esc_html( "Can't get value from " . get_class( $source ) )
346 + );
347 + }
348 +
349 +
350 + /**
351 + * @return mixed
352 + * @throws Preset_Exception
353 + */
354 + final public function result() {
355 + $this->throw_if_preset_not_available();
356 +
357 + return $this->parse_result_value( $this->get_result_on_prop() );
358 + }
359 +
360 + public function parse_result_value( $value ) {
361 + if ( ! isset( $this->field_args['type'] ) ) {
362 + return $value;
363 + }
364 +
365 + return Preset_Manager::instance()->prepare_result( $this->field_args['type'], $value );
366 + }
367 +
368 +
369 + /**
370 + * @return Base
371 + * @throws Preset_Exception
372 + */
373 + public function get_field_object(): Base {
374 + $type = $this->field_args['type'] ?? false;
375 + $block = jet_form_builder()->blocks->get_field_by_name( $type );
376 +
377 + if ( ! $block ) {
378 + // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
379 + throw new Preset_Exception( 'Undefined block_type: ' . $type, $this->field_args );
380 + }
381 +
382 + $block->block_attrs = $this->field_args;
383 +
384 + return $block;
385 + }
386 +
387 + /**
388 + * @return mixed
389 + * @throws Preset_Exception
390 + */
391 + public function get_expected_format() {
392 + return $this->get_field_object()->expected_preset_type()[0] ?? 'raw';
393 + }
394 +
395 +}