PluginProbe ʕ •ᴥ•ʔ
JetFormBuilder — Dynamic Blocks Form Builder / 3.6.5.2
JetFormBuilder — Dynamic Blocks Form Builder v3.6.5.2
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 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.3.0 1.3.1 1.3.2 1.3.3 1.4.0 1.4.1 1.4.2 1.4.3 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.1.0 2.1.1 2.1.10 2.1.11 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 3.0.0 3.0.0.1 3.0.0.2 3.0.0.3 3.0.1 3.0.1.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.0.1 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9 3.2.0 3.2.1 3.2.2 3.2.3 3.3.0 3.3.1 3.3.2 3.3.3 3.3.3.1 3.3.4 3.3.4.1 3.3.4.2 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.4.5 3.4.5.1 3.4.5.2 3.4.6 3.4.7 3.4.7.1 3.5.0 3.5.1 3.5.1.1 3.5.1.2 3.5.2 3.5.2.1 3.5.3 3.5.4 3.5.5 3.5.6 3.5.6.1 3.5.6.2 3.5.6.3 3.6.0
jetformbuilder / modules / validation / module.php
jetformbuilder / modules / validation Last commit date
advanced-rules 4 days ago handlers 2 months ago messages 2 years ago post-type 2 years ago rest-api 6 months ago ssr 1 month ago class-validation-handlers.php 1 year ago module.php 4 days ago rules-controller.php 4 days ago
module.php
441 lines
1 <?php
2
3
4 namespace JFB_Modules\Validation;
5
6 use Jet_Form_Builder\Admin\Editor;
7 use Jet_Form_Builder\Blocks\Types\Base;
8 use Jet_Form_Builder\Classes\Arrayable\Array_Tools;
9 use Jet_Form_Builder\Classes\Tools;
10 use Jet_Form_Builder\Exceptions\Repository_Exception;
11 use Jet_Form_Builder\Plugin;
12 use JFB_Components\Module\Base_Module_After_Install_It;
13 use JFB_Components\Module\Base_Module_Dir_It;
14 use JFB_Components\Module\Base_Module_Dir_Trait;
15 use JFB_Components\Module\Base_Module_Handle_It;
16 use JFB_Components\Module\Base_Module_Handle_Trait;
17 use JFB_Components\Module\Base_Module_It;
18 use JFB_Components\Module\Base_Module_Url_It;
19 use JFB_Components\Module\Base_Module_Url_Trait;
20 use JFB_Modules\Block_Parsers\Field_Data_Parser;
21 use JFB_Modules\Validation\Advanced_Rules\Ssr_Callback_Allowlist;
22 use JFB_Modules\Validation\Class_Validation_Handlers;
23 use JFB_Modules\Validation\Handlers\Validation_Handler;
24 use JFB_Modules\Validation\Rest_Api\Rest_Validation_Endpoint;
25
26 // If this file is called directly, abort.
27 if ( ! defined( 'WPINC' ) ) {
28 die;
29 }
30
31 final class Module implements
32 Base_Module_It,
33 Base_Module_Handle_It,
34 Base_Module_Url_It,
35 Base_Module_Dir_It,
36 Base_Module_After_Install_It {
37
38 use Base_Module_Url_Trait;
39 use Base_Module_Handle_Trait;
40 use Base_Module_Dir_Trait;
41
42 const FORMAT_ADVANCED = 'advanced';
43 const FORMAT_BROWSER = 'browser';
44 const HANDLE = 'jet-fb-advanced-reporting';
45
46 private $messages = array();
47 /**
48 * @var Rules_Controller
49 */
50 private $rules;
51 private $settings;
52 private $inline_messages = array();
53 /**
54 * @var Ssr_Callback_Allowlist
55 */
56 private $ssr_allowlist;
57
58 public function rep_item_id() {
59 return 'validation';
60 }
61
62 public function condition(): bool {
63 return true;
64 }
65
66 /**
67 * @throws Repository_Exception
68 */
69 public function on_install() {
70 $handlers = new Class_Validation_Handlers();
71
72 foreach ( $handlers->get_handlers() as $handler ) {
73 if ( method_exists( $handler, 'init' ) ) {
74 $handler->init();
75 }
76 }
77
78 /** @var \JFB_Modules\Post_Type\Module $post_type */
79 $post_type = jet_form_builder()->module( 'post-type' );
80 $post_type->get_meta()->install( new Post_Type\Validation_Meta() );
81
82 /** @var \JFB_Modules\Rest_Api\Module $rest_api */
83 $rest_api = jet_form_builder()->module( 'rest-api' );
84 $rest_api->get_controller()->install( new Rest_Api\Rest_Validation_Endpoint() );
85 }
86
87 /**
88 * @throws Repository_Exception
89 */
90 public function on_uninstall() {
91 $this->rules = null;
92 $this->messages = array();
93
94 /** @var \JFB_Modules\Post_Type\Module $post_type */
95 $post_type = jet_form_builder()->module( 'post-type' );
96 $post_type->get_meta()->uninstall( Post_Type\Validation_Meta::class );
97
98 /** @var \JFB_Modules\Rest_Api\Module $rest_api */
99 $rest_api = jet_form_builder()->module( 'rest-api' );
100 $rest_api->get_controller()->uninstall( new Rest_Api\Rest_Validation_Endpoint() );
101 }
102
103 public function init_hooks() {
104 add_filter(
105 'jet-form-builder/before-start-form',
106 array( $this, 'add_validation_messages_global' )
107 );
108
109 add_action(
110 'jet-form-builder/before-start-form-row',
111 array( $this, 'add_validation_block' )
112 );
113 add_action(
114 'wp_enqueue_scripts',
115 array( $this, 'register_scripts' )
116 );
117
118 /**
119 * @link https://github.com/Crocoblock/issues-tracker/issues/1542
120 */
121 add_action(
122 'jet_plugins/frontend/register_scripts',
123 array( $this, 'register_scripts' )
124 );
125 add_action(
126 'jet-form-builder/validate-field',
127 array( $this, 'validate_block' ),
128 0
129 );
130 add_action(
131 'jet-form-builder/editor-assets/before',
132 array( $this, 'localize_editor_config' )
133 );
134
135 $this->ssr_allowlist = new Ssr_Callback_Allowlist();
136 }
137
138 public function remove_hooks() {
139 remove_filter(
140 'jet-form-builder/before-start-form',
141 array( $this, 'add_validation_messages_global' )
142 );
143 remove_action(
144 'jet-form-builder/before-start-form-row',
145 array( $this, 'add_validation_block' )
146 );
147 remove_action(
148 'wp_enqueue_scripts',
149 array( $this, 'register_scripts' )
150 );
151
152 /**
153 * @link https://github.com/Crocoblock/issues-tracker/issues/1542
154 */
155 remove_action(
156 'jet_plugins/frontend/register_scripts',
157 array( $this, 'register_scripts' )
158 );
159 remove_action(
160 'jet-form-builder/validate-field',
161 array( $this, 'validate_block' ),
162 0
163 );
164 remove_action(
165 'jet-form-builder/editor-assets/before',
166 array( $this, 'localize_editor_config' )
167 );
168
169 if ( $this->ssr_allowlist ) {
170 $this->ssr_allowlist->remove_hooks();
171 }
172 }
173
174 public function register_scripts() {
175 $script_asset = require_once jet_form_builder()->plugin_dir(
176 'assets/build/frontend/advanced.reporting.asset.php'
177 );
178
179 if ( true === $script_asset ) {
180 return;
181 }
182
183 array_push(
184 $script_asset['dependencies'],
185 \Jet_Form_Builder\Blocks\Module::MAIN_SCRIPT_HANDLE
186 );
187
188 wp_register_script(
189 self::HANDLE,
190 jet_form_builder()->plugin_url( 'assets/build/frontend/advanced.reporting.js' ),
191 $script_asset['dependencies'],
192 $script_asset['version'],
193 true
194 );
195 }
196
197 public function add_validation_messages_global( string $markup, bool $force = false ): string {
198 $this->settings = $this->get_settings();
199 $form_id = jet_fb_live()->form_id;
200
201 if (
202 ( ! $this->is_advanced_form() && ! $force ) ||
203 in_array( $form_id, $this->inline_messages, true )
204 ) {
205 return $markup;
206 }
207
208 $data = Tools::encode_json( $this->settings );
209
210 wp_enqueue_script( self::HANDLE );
211
212 wp_add_inline_script(
213 \Jet_Form_Builder\Blocks\Module::MAIN_SCRIPT_HANDLE,
214 "
215 window.JetFormsValidation = window.JetFormsValidation ?? {};
216 window.JetFormsValidation[ {$form_id} ] = $data;
217 "
218 );
219
220 add_action(
221 'wp_enqueue_scripts',
222 function () use ( $form_id, $data ) {
223 wp_add_inline_script(
224 \Jet_Form_Builder\Blocks\Module::MAIN_SCRIPT_HANDLE,
225 "
226 window.JetFormsValidation = window.JetFormsValidation ?? {};
227 window.JetFormsValidation[ {$form_id} ] = $data;
228 "
229 );
230 },
231 20
232 );
233
234 $this->inline_messages[] = $form_id;
235
236 return $markup;
237 }
238
239 public function add_validation_block( Base $block ) {
240 /**
241 * If in post meta enable Advanced validation
242 * or right in block settings
243 */
244 if ( ! $this->is_advanced( $block->block_attrs ) ) {
245 return;
246 }
247
248 $this->add_validation_messages_global( '', true );
249
250 $type = $block->block_attrs['validation']['type'] ?? '';
251 $rules = $block->block_attrs['validation']['rules'] ?? array();
252
253 $block->add_attribute( 'data-validation-type', $type ?: 'inherit' );
254
255 if ( ! empty( $rules ) ) {
256 $this->get_rules()->prepare_rules( $rules );
257
258 // Security: Add signatures for SSR validation rules
259 // For repeater fields, we include the repeater name in the signature
260 // but NOT the row index (which is dynamic). The signature binds the
261 // field to its structural path: [repeater_name, field_name] or just field_name
262 $form_id = jet_fb_live()->form_id;
263 $field_name = $block->block_attrs['name'] ?? '';
264 $repeater_name = $block->get_repeater_name();
265
266 // Build canonical path for signature (without row index)
267 $signature_path = $repeater_name
268 ? array( $repeater_name, $field_name )
269 : $field_name;
270
271 foreach ( $rules as $index => &$rule ) {
272 if ( 'ssr' === ( $rule['type'] ?? '' ) ) {
273 $signature = Rest_Validation_Endpoint::generate_signature(
274 (int) $form_id,
275 $signature_path,
276 (int) $index
277 );
278 $signature_key = Validation_Handler::get_signature_key( $signature_path, (int) $index );
279
280 printf(
281 '<input type="hidden" name="%1$s[%2$s]" value="%3$s" />',
282 esc_attr( Validation_Handler::MAIN_SIGNATURES_KEY ),
283 esc_attr( $signature_key ),
284 esc_attr( $signature )
285 );
286
287 // Security: only the lookup key is exposed here, never the signature
288 // itself — the signature already lives in the hidden input above,
289 // and JS reads it from there instead of duplicating it in this
290 // public JSON blob (https://github.com/Crocoblock/issues-tracker/issues/20361).
291 $rule['_sig_key'] = $signature_key;
292 }
293 }
294 unset( $rule );
295
296 $block->add_attribute(
297 'data-validation-rules',
298 Tools::encode_json( $rules )
299 );
300 }
301
302 /**
303 * If advanced validation not enabled right in block settings
304 */
305 if ( self::FORMAT_ADVANCED !== $type ) {
306 return;
307 }
308
309 $messages = $block->block_attrs['validation']['messages'] ?? array();
310
311 if ( ! empty( $messages ) ) {
312 $block->add_attribute( 'data-validation-messages', Tools::encode_json( $messages ) );
313 }
314 }
315
316 public function get_settings(): array {
317 /** @var \JFB_Modules\Post_Type\Module $module */
318 /** @noinspection PhpUnhandledExceptionInspection */
319 $module = jet_form_builder()->module( 'post-type' );
320 $validation = $module->query_meta( Post_Type\Validation_Meta::class );
321
322 $response = array(
323 'type' => $validation['type'] ?? self::FORMAT_BROWSER,
324 'messages' => array(),
325 );
326
327 $messages = $this->get_messages();
328
329 foreach ( $messages as $message ) {
330 $response['messages'][ $message->get_id() ] = (
331 $validation['messages'][ $message->get_id() ] ?? $message->get_initial()
332 );
333 }
334
335 return $response;
336 }
337
338 public function is_advanced( array $block_attrs ): bool {
339 $type = $block_attrs['validation']['type'] ?? '';
340
341 if ( self::FORMAT_ADVANCED === $type ) {
342 return true;
343 }
344
345 if ( '' === $type || 'inherit' === $type ) {
346 return $this->is_advanced_form();
347 }
348
349 return false;
350 }
351
352 public function is_advanced_form(): bool {
353 if ( is_null( $this->settings ) ) {
354 $this->settings = $this->get_settings();
355 }
356
357 return self::FORMAT_ADVANCED === ( $this->settings['type'] ?? '' );
358 }
359
360 public function formats(): array {
361 return array(
362 array(
363 'value' => self::FORMAT_BROWSER,
364 'label' => __( 'Default', 'jet-form-builder' ),
365 'title' => __( 'Browser native validation', 'jet-form-builder' ),
366 ),
367 array(
368 'value' => self::FORMAT_ADVANCED,
369 'label' => __( 'Advanced', 'jet-form-builder' ),
370 'title' => __( 'More flexible JetFormBuilder\'s validation', 'jet-form-builder' ),
371 ),
372 );
373 }
374
375 public function validate_block( Field_Data_Parser $parser ) {
376 if (
377 ! $this->is_advanced( $parser->get_settings() ) ||
378 Tools::is_empty( $parser->get_value() ) ||
379 $parser->is_inside_conditional()
380 ) {
381 return;
382 }
383
384 $this->get_rules()->validate_block( $parser );
385 }
386
387 public function localize_editor_config() {
388 wp_localize_script(
389 Editor::EDITOR_PACKAGE_HANDLE,
390 'jetFormValidation',
391 array(
392 'messages' => Array_Tools::to_array( $this->get_messages() ),
393 'ssr_callbacks' => Array_Tools::to_array( $this->get_rules()->get_ssr()->get_callbacks() ),
394 'formats' => $this->formats(),
395 'rule_types' => Array_Tools::to_array( $this->get_rules()->rep_get_values() ),
396 )
397 );
398 }
399
400 /**
401 * @return Rules_Controller
402 */
403 public function get_rules(): Rules_Controller {
404 if ( ! is_null( $this->rules ) ) {
405 return $this->rules;
406 }
407
408 $this->rules = new Rules_Controller();
409
410 return $this->rules;
411 }
412
413 public function get_messages(): array {
414 if ( ! empty( $this->messages ) ) {
415 return $this->messages;
416 }
417
418 $this->messages = apply_filters(
419 'jet-form-builder/validation-messages',
420 array(
421 new Messages\Is_Empty_Value(),
422 new Messages\Is_Number_Min(),
423 new Messages\Is_Number_Max(),
424 new Messages\Is_Char_Min(),
425 new Messages\Is_Char_Max(),
426 new Messages\Is_Not_Valid_Email(),
427 new Messages\Is_Not_Valid_Url(),
428 new Messages\Is_Not_Complete_Mask(),
429 new Messages\Is_Files_Max(),
430 new Messages\Is_File_Size(),
431 new Messages\Is_File_Ext(),
432 new Messages\Is_Date_Min(),
433 new Messages\Is_Date_Max(),
434 )
435 );
436
437 return $this->messages;
438 }
439
440 }
441