PluginProbe
WP Data Access – App Builder for Tables, Forms, Dashboards, Charts & Maps / 5.5.87
WP Data Access – App Builder for Tables, Forms, Dashboards, Charts & Maps v5.5.87
5.5.87 5.5.85 5.5.84 5.5.83 5.5.82 5.5.81 5.5.80 5.5.79 5.5.77 5.5.76 5.5.75 5.5.73 5.5.72 5.5.22 5.5.23 5.5.29 5.5.3 5.5.31 5.5.32 5.5.34 5.5.35 5.5.36 5.5.37 5.5.4 5.5.40 All 162 releases
← All changes | WPDataAccess/API/WPDA_Apps.php +733 -186 5.5.22 → 5.5.87 View file →
@@ -8,8 +8,9 @@
8 8 use WPDataAccess\Plugin_Table_Models\WPDA_App_Container_Model;
9 9 use WPDataAccess\Plugin_Table_Models\WPDA_App_Apps_Model;
10 10 use WPDataAccess\Plugin_Table_Models\WPDA_App_Model;
11 11 use WPDataAccess\Plugin_Table_Models\WPDA_Table_Settings_Model;
12 +use WPDataAccess\Utilities\WPDA_App_Localization;
12 13 use WPDataAccess\WPDA;
13 14 class WPDA_Apps extends WPDA_API_Core {
14 15 const METHODS = array('httpGet', 'httpPost', 'httpRequest');
15 16
@@ -26,8 +27,9 @@
26 27 $value->{$property_name} = $this->sanitize_settings( $property_value );
27 28 }
28 29 } else {
29 30 // Allow HTML and onclick for computed fields
31 + // phpcs:disable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
30 32 $value = apply_filters(
31 33 'wp_kses_post',
32 34 $value,
33 35 "",
@@ -32,8 +34,9 @@
32 34 $value,
33 35 "",
34 36 ["onclick"]
35 37 );
38 + // phpcs:enable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
36 39 }
37 40 return $value;
38 41 }
39 42
@@ -267,14 +270,32 @@
267 270 'required' => false,
268 271 'type' => 'string',
269 272 'description' => __( 'App settings - JSON string', 'wp-data-access' ),
270 273 'sanitize_callback' => function ( $param ) {
271 - $satitized_settings = $this->sanitize_settings( json_decode( (string) $param, true ) );
274 + $sanitized_settings = $this->sanitize_settings( json_decode( (string) $param, true ) );
272 275 // Save sanitized JSON as string
273 - return json_encode( $satitized_settings );
276 + return json_encode( $sanitized_settings );
274 277 },
275 278 'validate_callback' => 'rest_validate_request_arg',
276 279 ),
280 + 'map' => array(
281 + 'required' => false,
282 + 'type' => 'string',
283 + 'description' => __( 'Map settings - JSON string', 'wp-data-access' ),
284 + 'sanitize_callback' => function ( $param ) {
285 + $sanitized_settings = $this->sanitize_settings( json_decode( (string) $param, true ) );
286 + // Save sanitized JSON as string
287 + return json_encode( $sanitized_settings );
288 + },
289 + 'validate_callback' => 'rest_validate_request_arg',
290 + ),
291 + 'chart' => array(
292 + 'required' => false,
293 + 'type' => 'string',
294 + 'description' => __( 'Chart settings - JSON string', 'wp-data-access' ),
295 + 'sanitize_callback' => 'sanitize_text_field',
296 + 'validate_callback' => 'rest_validate_request_arg',
297 + ),
277 298 'theme' => array(
278 299 'required' => false,
279 300 'type' => 'string',
280 301 'description' => __( 'Theme settings - JSON string', 'wp-data-access' ),
@@ -307,8 +328,33 @@
307 328 'row_count' => $this->get_param( 'row_count' ),
308 329 'row_count_estimate' => $this->get_param( 'row_count_estimate' ),
309 330 'media' => $this->get_param( 'media' ),
310 331 'rel_tab' => $this->get_param( 'rel_tab' ),
332 + 'client_side' => $this->get_param( 'client_side' ),
333 + 'geo_radius' => array(
334 + 'required' => false,
335 + 'type' => 'mixed',
336 + 'description' => __( 'Geo radius segment', 'wp-data-access' ),
337 + 'sanitize_callback' => function ( $param ) {
338 + $geo_radius = array();
339 + if ( isset(
340 + $param['col']['lat'],
341 + $param['col']['lng'],
342 + $param['loc']['lat'],
343 + $param['loc']['lng'],
344 + $param['radius'],
345 + $param['unit']
346 + ) && is_numeric( $param['loc']['lat'] ) && is_numeric( $param['loc']['lng'] ) && is_numeric( $param['radius'] ) && ('km' === $param['unit'] || 'miles' === $param['unit']) ) {
347 + $geo_radius['col']['lat'] = WPDA::remove_backticks( sanitize_text_field( $param['col']['lat'] ) );
348 + $geo_radius['col']['lng'] = WPDA::remove_backticks( sanitize_text_field( $param['col']['lng'] ) );
349 + $geo_radius['loc']['lat'] = (float) sanitize_text_field( $param['loc']['lat'] );
350 + $geo_radius['loc']['lng'] = (float) sanitize_text_field( $param['loc']['lng'] );
351 + $geo_radius['radius'] = (float) sanitize_text_field( $param['radius'] );
352 + $geo_radius['unit'] = sanitize_text_field( $param['unit'] );
353 + }
354 + return $geo_radius;
355 + },
356 + ),
311 357 ),
312 358 ) );
313 359 register_rest_route( WPDA_API::WPDA_NAMESPACE, 'app/get', array(
314 360 'methods' => array('POST'),
@@ -417,8 +463,9 @@
417 463 'search_params' => $this->get_param( 'search_params' ),
418 464 'shortcode_params' => $this->get_param( 'search_params' ),
419 465 'md' => $this->get_param( 'md' ),
420 466 'cascade' => $this->get_param( 'cascade' ),
467 + 'values' => $this->get_param( 'md' ),
421 468 ),
422 469 ) );
423 470 register_rest_route( WPDA_API::WPDA_NAMESPACE, 'app/lookup/dbs', array(
424 471 'methods' => array('POST'),
@@ -459,9 +506,11 @@
459 506 'methods' => array('POST'),
460 507 'callback' => array($this, 'app_chart_data'),
461 508 'permission_callback' => '__return_true',
462 509 'args' => array(
463 - 'app_id' => $this->get_param( 'app_id' ),
510 + 'app_id' => $this->get_param( 'app_id' ),
511 + 'search_custom' => $this->get_param( 'search_custom' ),
512 + 'shortcode_params' => $this->get_param( 'search_params' ),
464 513 ),
465 514 ) );
466 515 register_rest_route( WPDA_API::WPDA_NAMESPACE, 'app/dbs/rename', array(
467 516 'methods' => array('POST'),
@@ -469,12 +518,227 @@
469 518 'permission_callback' => '__return_true',
470 519 'args' => array(
471 520 'dbs_source' => $this->get_param( 'dbs' ),
472 521 'dbs_destination' => $this->get_param( 'dbs' ),
522 + 'appDbId' => array(
523 + 'required' => false,
524 + 'type' => 'integer',
525 + 'description' => __( 'App Id', 'wp-data-access' ),
526 + 'sanitize_callback' => 'absint',
527 + 'validate_callback' => 'rest_validate_request_arg',
528 + ),
473 529 ),
474 530 ) );
531 + register_rest_route( WPDA_API::WPDA_NAMESPACE, 'app/lang/get', array(
532 + 'methods' => array('POST'),
533 + 'callback' => array($this, 'app_lang_get'),
534 + 'permission_callback' => '__return_true',
535 + ) );
536 + register_rest_route( WPDA_API::WPDA_NAMESPACE, 'app/lang/set', array(
537 + 'methods' => array('POST'),
538 + 'callback' => array($this, 'app_lang_set'),
539 + 'permission_callback' => '__return_true',
540 + 'args' => array(
541 + 'localizations' => array(
542 + 'required' => true,
543 + 'type' => 'mixed',
544 + 'description' => __( 'Custom side translations (JSON as string)', 'wp-data-access' ),
545 + 'sanitize_callback' => 'sanitize_text_field',
546 + 'validate_callback' => 'rest_validate_request_arg',
547 + ),
548 + ),
549 + ) );
550 + register_rest_route( WPDA_API::WPDA_NAMESPACE, 'app/call', array(
551 + 'methods' => array('POST'),
552 + 'callback' => array($this, 'app_call'),
553 + 'permission_callback' => '__return_true',
554 + 'args' => array(
555 + 'app_id' => $this->get_param( 'app_id' ),
556 + 'cls' => array(
557 + 'required' => true,
558 + 'type' => 'string',
559 + 'description' => __( 'Class name', 'wp-data-access' ),
560 + 'sanitize_callback' => 'sanitize_text_field',
561 + 'validate_callback' => 'rest_validate_request_arg',
562 + ),
563 + 'fnc' => array(
564 + 'required' => true,
565 + 'type' => 'string',
566 + 'description' => __( 'Function name', 'wp-data-access' ),
567 + 'sanitize_callback' => 'sanitize_text_field',
568 + 'validate_callback' => 'rest_validate_request_arg',
569 + ),
570 + 'arg' => array(
571 + 'required' => true,
572 + 'type' => 'mixed',
573 + 'description' => __( 'Arguments', 'wp-data-access' ),
574 + 'sanitize_callback' => 'sanitize_text_field',
575 + 'validate_callback' => 'rest_validate_request_arg',
576 + ),
577 + ),
578 + ) );
579 + register_rest_route( WPDA_API::WPDA_NAMESPACE, 'app/proxy', array(
580 + 'methods' => array('GET'),
581 + 'callback' => array($this, 'app_proxy'),
582 + 'permission_callback' => '__return_true',
583 + 'args' => array(
584 + 'app_id' => $this->get_param( 'app_id' ),
585 + 'cnt_id' => $this->get_param( 'cnt_id' ),
586 + 'url' => array(
587 + 'required' => true,
588 + 'type' => 'string',
589 + 'description' => __( 'URL', 'wp-data-access' ),
590 + 'sanitize_callback' => 'sanitize_text_field',
591 + 'validate_callback' => 'rest_validate_request_arg',
592 + ),
593 + ),
594 + ) );
595 + // PWA
596 + register_rest_route( WPDA_API::WPDA_NAMESPACE, 'app/pwa/get', array(
597 + 'methods' => array('POST'),
598 + 'callback' => array($this, 'app_wpa_get'),
599 + 'permission_callback' => '__return_true',
600 + 'args' => array(
601 + 'app_id' => $this->get_param( 'app_id' ),
602 + ),
603 + ) );
604 + register_rest_route( WPDA_API::WPDA_NAMESPACE, 'app/pwa/activate', array(
605 + 'methods' => array('POST'),
606 + 'callback' => array($this, 'app_wpa_activate'),
607 + 'permission_callback' => '__return_true',
608 + 'args' => array(
609 + 'app_id' => $this->get_param( 'app_id' ),
610 + ),
611 + ) );
612 + register_rest_route( WPDA_API::WPDA_NAMESPACE, 'app/pwa/deactivate', array(
613 + 'methods' => array('POST'),
614 + 'callback' => array($this, 'app_wpa_deactivate'),
615 + 'permission_callback' => '__return_true',
616 + 'args' => array(
617 + 'app_id' => $this->get_param( 'app_id' ),
618 + ),
619 + ) );
620 + register_rest_route( WPDA_API::WPDA_NAMESPACE, 'app/upload', array(
621 + 'methods' => array('POST'),
622 + 'callback' => array($this, 'app_upload'),
623 + 'permission_callback' => '__return_true',
624 + 'args' => array(
625 + 'app_id' => $this->get_param( 'app_id' ),
626 + 'cnt_id' => $this->get_param( 'cnt_id' ),
627 + 'pk' => array(
628 + 'required' => true,
629 + 'type' => 'string',
630 + 'description' => __( 'Primary key in JSON format', 'wp-data-access' ),
631 + 'sanitize_callback' => 'sanitize_text_field',
632 + 'validate_callback' => 'rest_validate_request_arg',
633 + ),
634 + 'col' => $this->get_param( 'col' ),
635 + ),
636 + ) );
637 + register_rest_route( WPDA_API::WPDA_NAMESPACE, 'app/download', array(
638 + 'methods' => array('POST'),
639 + 'callback' => array($this, 'app_download'),
640 + 'permission_callback' => '__return_true',
641 + 'args' => array(
642 + 'app_id' => $this->get_param( 'app_id' ),
643 + 'cnt_id' => $this->get_param( 'cnt_id' ),
644 + 'pk' => array(
645 + 'required' => true,
646 + 'type' => 'string',
647 + 'description' => __( 'Primary key in JSON format', 'wp-data-access' ),
648 + 'sanitize_callback' => 'sanitize_text_field',
649 + 'validate_callback' => 'rest_validate_request_arg',
650 + ),
651 + 'col' => $this->get_param( 'col' ),
652 + ),
653 + ) );
475 654 }
476 655
656 + public function app_proxy( $request ) {
657 + wp_die();
658 + }
659 +
660 + private function detect_image_mime( $body ) {
661 + return null;
662 + }
663 +
664 + private function is_safe_remote_url( $url, $allowed_hosts ) {
665 + return false;
666 + }
667 +
668 + private function is_public_ip( $ip ) {
669 + return false;
670 + }
671 +
672 + public function app_download( $request ) {
673 + return $this->WPDA_Rest_Response( 'OK' );
674 + }
675 +
676 + public function app_upload( $request ) {
677 + return $this->WPDA_Rest_Response( 'OK' );
678 + }
679 +
680 + public function app_wpa_deactivate( $request ) {
681 + if ( !$this->current_user_can_access() ) {
682 + // Only admins
683 + return $this->unauthorized();
684 + }
685 + if ( !$this->current_user_token_valid( $request ) ) {
686 + return $this->invalid_nonce();
687 + }
688 + return $this->WPDA_Rest_Response( 'OK' );
689 + }
690 +
691 + public function app_wpa_activate( $request ) {
692 + if ( !$this->current_user_can_access() ) {
693 + // Only admins
694 + return $this->unauthorized();
695 + }
696 + if ( !$this->current_user_token_valid( $request ) ) {
697 + return $this->invalid_nonce();
698 + }
699 + return $this->WPDA_Rest_Response( 'OK' );
700 + }
701 +
702 + public function app_wpa_get( $request ) {
703 + if ( !$this->current_user_can_access() ) {
704 + // Only admins
705 + return $this->unauthorized();
706 + }
707 + if ( !$this->current_user_token_valid( $request ) ) {
708 + return $this->invalid_nonce();
709 + }
710 + return $this->WPDA_Rest_Response( 'NOT FOUND' );
711 + }
712 +
713 + public function app_call( $request ) {
714 + return $this->bad_request();
715 + }
716 +
717 + public function app_lang_get( $request ) {
718 + if ( !$this->current_user_can_access() ) {
719 + // Only admins
720 + return $this->unauthorized();
721 + }
722 + if ( !$this->current_user_token_valid( $request ) ) {
723 + return $this->invalid_nonce();
724 + }
725 + return $this->WPDA_Rest_Response( '', WPDA_App_Localization::get() );
726 + }
727 +
728 + public function app_lang_set( $request ) {
729 + if ( !$this->current_user_can_access() ) {
730 + // Only admins
731 + return $this->unauthorized();
732 + }
733 + if ( !$this->current_user_token_valid( $request ) ) {
734 + return $this->invalid_nonce();
735 + }
736 + $localizations = $request->get_param( 'localizations' );
737 + WPDA_App_Localization::set( $localizations );
738 + return $this->WPDA_Rest_Response( 'Translation successfully saved' );
739 + }
740 +
477 741 private function get_app_columns( $columns ) {
478 742 if ( !is_array( $columns ) ) {
479 743 return false;
480 744 }
@@ -589,9 +853,9 @@
589 853 $tbl,
590 854 $msg,
591 855 $settings
592 856 ) ) {
593 - $container = WPDA_App_Container_Model::get_container( $cnt_id );
857 + $container = WPDA_App_Container_Model::get_container( $app_id, $cnt_id );
594 858 if ( !isset( $container[0] ) ) {
595 859 return $this->bad_request();
596 860 }
597 861 return $this->get_app_container_meta( $app_id, $container, $rel_tab );
@@ -618,17 +882,17 @@
618 882 $tbl,
619 883 $msg,
620 884 $settings
621 885 ) ) {
622 - $container = WPDA_App_Container_Model::get_container( $cnt_id_from );
886 + $container = WPDA_App_Container_Model::get_container( $app_id, $cnt_id_from );
623 887 if ( !isset( $container[0] ) ) {
624 888 return $this->bad_request();
625 889 }
626 - $container = WPDA_App_Container_Model::get_container( $cnt_id_to );
890 + $container = WPDA_App_Container_Model::get_container( $app_id, $cnt_id_to );
627 891 if ( !isset( $container[0] ) ) {
628 892 return $this->bad_request();
629 893 }
630 - return $this->reorder_details( $cnt_id_from, $cnt_id_to );
894 + return $this->reorder_details( $app_id, $cnt_id_from, $cnt_id_to );
631 895 } else {
632 896 if ( 'rest_cookie_invalid_nonce' === $msg ) {
633 897 return $this->invalid_nonce();
634 898 } else {
@@ -708,8 +972,9 @@
708 972 $search_column_fns,
709 973 &$default_where,
710 974 &$lookups
711 975 ) {
976 + return $this->bad_request();
712 977 }
713 978
714 979 private function build_relationships(
715 980 $container,
@@ -716,8 +981,9 @@
716 981 &$m2m_relationship,
717 982 $tbl,
718 983 &$default_where
719 984 ) {
985 + return $this->bad_request();
720 986 }
721 987
722 988 public function app_select( $request ) {
723 989 $app_id = $request->get_param( 'app_id' );
@@ -738,12 +1004,20 @@
738 1004 $row_count = $request->get_param( 'row_count' );
739 1005 $row_count_estimate = $request->get_param( 'row_count_estimate' );
740 1006 $media = $request->get_param( 'media' );
741 1007 $rel_tab = $request->get_param( 'rel_tab' );
1008 + $client_side = '1' === $request->get_param( 'client_side' );
1009 + $geo_radius = $request->get_param( 'geo_radius' );
1010 + $docs = array();
742 1011 $default_where = '';
743 1012 $default_orderby = '';
744 1013 $lookups = array();
745 1014 $m2m_relationship = array();
1015 + if ( $client_side ) {
1016 + // Delete search values on refresh
1017 + $search = '';
1018 + $search_columns = array();
1019 + }
746 1020 if ( $this->check_app_access(
747 1021 $app_id,
748 1022 $cnt_id,
749 1023 'select',
@@ -751,27 +1025,57 @@
751 1025 $tbl,
752 1026 $msg,
753 1027 $settings
754 1028 ) ) {
755 - $container = WPDA_App_Container_Model::get_container( $cnt_id );
756 - if ( '1' === $rel_tab ) {
1029 + $container = WPDA_App_Container_Model::get_container( $app_id, $cnt_id );
1030 + $app = WPDA_App_Model::get_by_id( $app_id );
1031 + if ( isset( $app[0]['app_type'], $container[0]['cnt_map'] ) && '2' == $app[0]['app_type'] && null !== $container[0]['cnt_map'] ) {
1032 + // App = Map
1033 + // Get default where map
1034 + $map_json = json_decode( (string) $container[0]['cnt_map'], true );
1035 + if ( isset( $map_json['setup']['defaultWhere'] ) && null !== $map_json['setup']['defaultWhere'] && '' !== trim( $map_json['setup']['defaultWhere'] ) ) {
1036 + $default_where = $map_json['setup']['defaultWhere'];
1037 + }
757 1038 } else {
758 - $table_settings = $settings['table'] ?? array();
759 - // Get default where clause
760 - if ( isset( $table_settings['table']['defaultWhere'] ) ) {
761 - $default_where = $table_settings['table']['defaultWhere'];
1039 + // All other apps (not being a map)
1040 + if ( '1' === $rel_tab ) {
1041 + } else {
1042 + $table_settings = $settings['table'] ?? array();
1043 + // Get default where clause
1044 + if ( isset( $table_settings['table']['defaultWhere'] ) ) {
1045 + $default_where = $table_settings['table']['defaultWhere'];
1046 + }
1047 + // Get default order by
1048 + if ( isset( $table_settings['table']['defaultOrderBy'] ) ) {
1049 + $default_orderby_db = $table_settings['table']['defaultOrderBy'];
1050 + if ( is_array( $default_orderby_db ) ) {
1051 + foreach ( $default_orderby_db as $orderby ) {
1052 + if ( isset( $orderby['columnName'], $orderby['order'] ) && '' !== trim( $orderby['columnName'] ) ) {
1053 + $default_orderby .= (( '' === $default_orderby ? 'order by ' : ',' )) . '`' . WPDA::remove_backticks( $orderby['columnName'] ) . '` ' . (( 'desc' === $orderby['order'] ? 'desc' : 'asc' ));
1054 + }
1055 + }
1056 + }
1057 + }
762 1058 }
763 - // Get default order by
764 - if ( isset( $table_settings['table']['defaultOrderBy'] ) ) {
765 - $default_orderby_db = $table_settings['table']['defaultOrderBy'];
766 - if ( is_array( $default_orderby_db ) ) {
767 - foreach ( $default_orderby_db as $orderby ) {
768 - if ( isset( $orderby['columnName'], $orderby['order'] ) && '' !== trim( $orderby['columnName'] ) ) {
769 - $default_orderby .= (( '' === $default_orderby ? 'order by ' : ',' )) . '`' . WPDA::remove_backticks( $orderby['columnName'] ) . '` ' . (( 'desc' === $orderby['order'] ? 'desc' : 'asc' ));
770 - }
1059 + }
1060 + if ( isset( $settings['columns'] ) && is_array( $settings['columns'] ) && '1' !== $rel_tab ) {
1061 + $queryable_columns = array();
1062 + if ( isset( $settings['table']['columns'] ) && is_array( $settings['table']['columns'] ) ) {
1063 + foreach ( $settings['table']['columns'] as $column ) {
1064 + if ( isset( $column['queryable'] ) && $column['queryable'] ) {
1065 + $queryable_columns[] = $column['columnName'];
771 1066 }
772 1067 }
773 1068 }
1069 + $app_columns = array();
1070 + foreach ( $settings['columns'] as $column ) {
1071 + if ( isset( $column['columnName'], $column['isSelected'] ) && $column['isSelected'] ) {
1072 + $app_columns[$column['columnName']] = in_array( $column['columnName'], $queryable_columns );
1073 + }
1074 + }
1075 + if ( 0 < count( $app_columns ) ) {
1076 + $col = $app_columns;
1077 + }
774 1078 }
775 1079 $table_api = new WPDA_Table();
776 1080 return $table_api->select(
777 1081 $dbs,
@@ -795,9 +1099,12 @@
795 1099 $default_orderby,
796 1100 $lookups,
797 1101 $md,
798 1102 $m2m_relationship,
799 - $search_data_types
1103 + $search_data_types,
1104 + $client_side,
1105 + $geo_radius,
1106 + $docs
800 1107 );
801 1108 } else {
802 1109 if ( 'rest_cookie_invalid_nonce' === $msg ) {
803 1110 return $this->invalid_nonce();
@@ -821,8 +1128,9 @@
821 1128 return array_map( function ( $value ) {
822 1129 if ( true === $value['isSelected'] ) {
823 1130 return $value['columnName'];
824 1131 }
1132 + return null;
825 1133 }, $columns );
826 1134 }
827 1135
828 1136 public function app_get( $request ) {
@@ -830,8 +1138,9 @@
830 1138 $cnt_id = $request->get_param( 'cnt_id' );
831 1139 $key = $request->get_param( 'key' );
832 1140 $media = $request->get_param( 'media' );
833 1141 $rel_tab = $request->get_param( 'rel_tab' );
1142 + $docs = array();
834 1143 if ( $this->check_app_access(
835 1144 $app_id,
836 1145 $cnt_id,
837 1146 'select',
@@ -851,9 +1160,10 @@
851 1160 $tbl,
852 1161 $key,
853 1162 $media,
854 1163 $column_names,
855 - $default_where
1164 + $default_where,
1165 + $docs
856 1166 );
857 1167 } else {
858 1168 if ( 'rest_cookie_invalid_nonce' === $msg ) {
859 1169 return $this->invalid_nonce();
@@ -911,8 +1221,17 @@
911 1221 $column_names = $this->get_app_form_columns( $settings );
912 1222 if ( false === $column_names ) {
913 1223 $column_names = array();
914 1224 }
1225 + $code_columns = array();
1226 + $html_columns = array();
1227 + if ( isset( $settings['form']['columns'] ) ) {
1228 + foreach ( $settings['form']['columns'] as $column ) {
1229 + if ( isset( $column['allowHtml'] ) && false === $column['allowHtml'] ) {
1230 + $html_columns[] = $column['columnName'];
1231 + }
1232 + }
1233 + }
915 1234 $table_api = new WPDA_Table();
916 1235 return $table_api->update(
917 1236 $dbs,
918 1237 $tbl,
@@ -917,9 +1236,11 @@
917 1236 $dbs,
918 1237 $tbl,
919 1238 $key,
920 1239 $val,
921 - $column_names
1240 + $column_names,
1241 + $code_columns,
1242 + $html_columns
922 1243 );
923 1244 } else {
924 1245 if ( 'rest_cookie_invalid_nonce' === $msg ) {
925 1246 return $this->invalid_nonce();
@@ -931,51 +1252,9 @@
931 1252 }
932 1253 }
933 1254
934 1255 public function app_update_inline( $request ) {
935 - $app_id = $request->get_param( 'app_id' );
936 - $cnt_id = $request->get_param( 'cnt_id' );
937 - $key = $request->get_param( 'key' );
938 - $val = $request->get_param( 'val' );
939 - if ( $this->check_app_access(
940 - $app_id,
941 - $cnt_id,
942 - 'select',
943 - $dbs,
944 - $tbl,
945 - $msg,
946 - $settings
947 - ) ) {
948 - foreach ( $val as $column_name => $column ) {
949 - $found = false;
950 - foreach ( $settings['table']['columns'] as $settings_column ) {
951 - if ( isset( $settings_column['columnName'] ) && $column_name === $settings_column['columnName'] ) {
952 - $found = true;
953 - }
954 - }
955 - if ( !$found ) {
956 - return $this->unauthorized();
957 - }
958 - }
959 - $column_names = $this->get_app_form_columns( $settings );
960 - if ( false === $column_names ) {
961 - $column_names = array();
962 - }
963 - $table_api = new WPDA_Table();
964 - return $table_api->update(
965 - $dbs,
966 - $tbl,
967 - $key,
968 - $val,
969 - $column_names
970 - );
971 - } else {
972 - if ( 'rest_cookie_invalid_nonce' === $msg ) {
973 - return $this->invalid_nonce();
974 - } else {
975 - return $this->unauthorized();
976 - }
977 - }
1256 + return $this->bad_request();
978 1257 }
979 1258
980 1259 public function app_delete( $request ) {
981 1260 $app_id = $request->get_param( 'app_id' );
@@ -1024,8 +1303,9 @@
1024 1303 $search_params = $request->get_param( 'search_params' );
1025 1304 $shortcode_params = $request->get_param( 'shortcode_params' );
1026 1305 $md = $request->get_param( 'md' );
1027 1306 $cascade = $request->get_param( 'cascade' );
1307 + $values = $request->get_param( 'values' );
1028 1308 $default_where = '';
1029 1309 $default_where_lookup = '';
1030 1310 $lookups = array();
1031 1311 $m2m_relationship = array();
@@ -1037,9 +1317,9 @@
1037 1317 $tbl,
1038 1318 $msg,
1039 1319 $settings
1040 1320 ) ) {
1041 - $container = WPDA_App_Container_Model::get_container( $cnt_id );
1321 + $container = WPDA_App_Container_Model::get_container( $app_id, $cnt_id );
1042 1322 if ( !isset( $container[0] ) ) {
1043 1323 return $this->bad_request();
1044 1324 }
1045 1325 $lookup = array();
@@ -1090,9 +1370,10 @@
1090 1370 $this->process_params(
1091 1371 $default_where_lookup,
1092 1372 $search_custom,
1093 1373 $search_params,
1094 - $shortcode_params
1374 + $shortcode_params,
1375 + $values
1095 1376 ),
1096 1377 '1' === $cascade,
1097 1378 $tbl,
1098 1379 $col,
@@ -1099,9 +1380,10 @@
1099 1380 $this->process_params(
1100 1381 $default_where,
1101 1382 $search_custom,
1102 1383 $search_params,
1103 - $shortcode_params
1384 + $shortcode_params,
1385 + $values
1104 1386 ),
1105 1387 $search,
1106 1388 $cols,
1107 1389 $search_columns,
@@ -1122,8 +1404,12 @@
1122 1404 }
1123 1405 }
1124 1406
1125 1407 public function app_lookup_dbs( $request ) {
1408 + // Only admins are allowed to configre lookups
1409 + if ( !$this->current_user_can_access() ) {
1410 + return $this->unauthorized();
1411 + }
1126 1412 $app_id = $request->get_param( 'app_id' );
1127 1413 $cnt_id = $request->get_param( 'cnt_id' );
1128 1414 if ( $this->check_app_access(
1129 1415 $app_id,
@@ -1146,8 +1432,12 @@
1146 1432 }
1147 1433 }
1148 1434
1149 1435 public function app_lookup_tbl( $request ) {
1436 + // Only admins are allowed to configre lookups
1437 + if ( !$this->current_user_can_access() ) {
1438 + return $this->unauthorized();
1439 + }
1150 1440 $app_id = $request->get_param( 'app_id' );
1151 1441 $cnt_id = $request->get_param( 'cnt_id' );
1152 1442 $dbs = $request->get_param( 'dbs' );
1153 1443 if ( $this->check_app_access(
@@ -1171,8 +1461,12 @@
1171 1461 }
1172 1462 }
1173 1463
1174 1464 public function app_lookup_cls( $request ) {
1465 + // Only admins are allowed to configre lookups
1466 + if ( !$this->current_user_can_access() ) {
1467 + return $this->unauthorized();
1468 + }
1175 1469 $app_id = $request->get_param( 'app_id' );
1176 1470 $cnt_id = $request->get_param( 'cnt_id' );
1177 1471 $dbs = $request->get_param( 'dbs' );
1178 1472 $tbl = $request->get_param( 'tbl' );
@@ -1197,23 +1491,14 @@
1197 1491 }
1198 1492 }
1199 1493
1200 1494 public function app_qb_list( $request ) {
1201 - if ( !$this->current_user_can_access() ) {
1202 - return $this->unauthorized();
1203 - }
1204 - if ( !$this->current_user_token_valid( $request ) ) {
1205 - return $this->invalid_nonce();
1206 - }
1207 - $queries = get_user_meta( WPDA::get_current_user_id(), 'wpda_query_builder' );
1208 - if ( false === $queries ) {
1209 - $queries = array();
1210 - }
1211 - return $this->WPDA_Rest_Response( '', $queries );
1495 + $qb = new WPDA_QB();
1496 + return $qb->open( $request );
1212 1497 }
1213 1498
1214 1499 public function app_dbs_rename( $request ) {
1215 - if ( !$this->current_user_can_access( true ) ) {
1500 + if ( !$this->current_user_can_access() ) {
1216 1501 // Only admins
1217 1502 return $this->unauthorized();
1218 1503 }
1219 1504 if ( !$this->current_user_token_valid( $request ) ) {
@@ -1227,8 +1512,71 @@
1227 1512 ));
1228 1513 }
1229 1514 global $wpdb;
1230 1515 $renamed = 0;
1516 + $debug_mode = 'on' === WPDA::get_option( WPDA::OPTION_PLUGIN_DEBUG );
1517 + $debug = array();
1518 + $errors = array();
1519 + if ( $request->get_param( 'appDbId' ) ) {
1520 + // Update app only
1521 + $appDbId = $request->get_param( 'appDbId' );
1522 + // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared -- plugin table
1523 + $sql = $wpdb->prepare( "update `{$wpdb->prefix}wpda_app_container` set `cnt_dbs` = %s where `cnt_dbs` = %s and app_id = %d", array($dbs_destination, $dbs_source, $appDbId) );
1524 + $result = $wpdb->query( $sql );
1525 + // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
1526 + $renamed += $result;
1527 + if ( $debug_mode ) {
1528 + $debug[] = array(
1529 + 'sql' => $sql,
1530 + 'result' => $result,
1531 + );
1532 + }
1533 + if ( '' !== $wpdb->last_error ) {
1534 + $errors[] = array(
1535 + 'sql' => $sql,
1536 + 'error' => $wpdb->last_error,
1537 + );
1538 + }
1539 + $sql_content = array("update `{$wpdb->prefix}wpda_app_container` set `cnt_table` = replace(`cnt_table`, '\"dbs\":\"%1s\"', '\"dbs\":\"%1s\"') where `cnt_table` like '%\"dbs\":\"%1s\"%' and app_id = %d", "update `{$wpdb->prefix}wpda_app_container` set `cnt_form` = replace(`cnt_form`, '\"dbs\":\"%1s\"', '\"dbs\":\"%1s\"') where `cnt_form` like '%\"dbs\":\"%1s\"%' and app_id = %d");
1540 + foreach ( $sql_content as $sql ) {
1541 + // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared -- plugin table
1542 + $result = $wpdb->query( $wpdb->prepare( $sql, array(
1543 + $dbs_source,
1544 + $dbs_destination,
1545 + $dbs_source,
1546 + $appDbId
1547 + ) ) );
1548 + $renamed += $result;
1549 + if ( $debug_mode ) {
1550 + $debug[] = array(
1551 + 'sql' => $sql,
1552 + 'result' => $result,
1553 + );
1554 + }
1555 + if ( '' !== $wpdb->last_error ) {
1556 + $errors[] = array(
1557 + 'sql' => $sql,
1558 + 'error' => $wpdb->last_error,
1559 + );
1560 + }
1561 + }
1562 + $context = array();
1563 + if ( $debug_mode ) {
1564 + $context['debug'] = $debug;
1565 + }
1566 + if ( 0 < count( $errors ) ) {
1567 + $context['errors'] = $errors;
1568 + return new \WP_Error('error', 'Failed renaming database', array(
1569 + 'status' => 401,
1570 + 'context' => $context,
1571 + ));
1572 + }
1573 + return $this->WPDA_Rest_Response( sprintf(
1574 + /* translators: %s = number of database substitutions */
1575 + __( 'Successfully renamed %s database occurrences', 'wp-data-access' ),
1576 + $renamed
1577 + ), null, $context );
1578 + }
1231 1579 // Rename all occurrences in repository tables and apps
1232 1580 $sqls = array(
1233 1581 "update `{$wpdb->prefix}wpda_publisher` set `pub_schema_name` = %s where `pub_schema_name` = %s",
1234 1582 "update `{$wpdb->prefix}wpda_project_page` set `page_schema_name` = %s where `page_schema_name` = %s",
@@ -1236,22 +1584,68 @@
1236 1584 "update `{$wpdb->prefix}wpda_media` set `media_schema_name` = %s where `media_schema_name` = %s",
1237 1585 "update `{$wpdb->prefix}wpda_menus` set `menu_schema_name` = %s where `menu_schema_name` = %s",
1238 1586 "update `{$wpdb->prefix}wpda_table_design` set `wpda_schema_name` = %s where `wpda_schema_name` = %s",
1239 1587 "update `{$wpdb->prefix}wpda_table_settings` set `wpda_schema_name` = %s where `wpda_schema_name` = %s",
1240 - "update `{$wpdb->prefix}wpda_container` set `cnt_dbs` = %s where `cnt_dbs` = %s"
1588 + "update `{$wpdb->prefix}wpda_app_container` set `cnt_dbs` = %s where `cnt_dbs` = %s"
1241 1589 );
1242 1590 foreach ( $sqls as $sql ) {
1243 - $renamed += $wpdb->query( $wpdb->prepare( $sql, array($dbs_destination, $dbs_source) ) );
1591 + // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared -- plugin table
1592 + $result = $wpdb->query( $wpdb->prepare( $sql, array($dbs_destination, $dbs_source) ) );
1593 + // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
1594 + $renamed += $result;
1595 + if ( $debug_mode ) {
1596 + $debug[] = array(
1597 + 'sql' => $sql,
1598 + 'result' => $result,
1599 + );
1600 + }
1601 + if ( '' !== $wpdb->last_error ) {
1602 + $errors[] = array(
1603 + 'sql' => $sql,
1604 + 'error' => $wpdb->last_error,
1605 + );
1606 + }
1244 1607 }
1245 - $sql_content = array("update `{$wpdb->prefix}wpda_container` set `cnt_table` = replace(`cnt_table`, '\"dbs\":\"%1s\"', '\"dbs\":\"%1s\"') where `cnt_table` like '%\"dbs\":\"%1s\"%'", "update `{$wpdb->prefix}wpda_container` set `cnt_form` = replace(`cnt_form`, '\"dbs\":\"%1s\"', '\"dbs\":\"%1s\"') where `cnt_form` like '%\"dbs\":\"%1s\"%'");
1608 + $sql_content = array("update `{$wpdb->prefix}wpda_app_container` set `cnt_table` = replace(`cnt_table`, '\"dbs\":\"%1s\"', '\"dbs\":\"%1s\"') where `cnt_table` like '%\"dbs\":\"%1s\"%'", "update `{$wpdb->prefix}wpda_app_container` set `cnt_form` = replace(`cnt_form`, '\"dbs\":\"%1s\"', '\"dbs\":\"%1s\"') where `cnt_form` like '%\"dbs\":\"%1s\"%'");
1246 1609 foreach ( $sql_content as $sql ) {
1247 - $renamed += $wpdb->query( $wpdb->prepare( $sql, array($dbs_source, $dbs_destination, $dbs_source) ) );
1610 + // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared -- plugin table
1611 + $result = $wpdb->query( $wpdb->prepare( $sql, array($dbs_source, $dbs_destination, $dbs_source) ) );
1612 + $renamed += $result;
1613 + if ( $debug_mode ) {
1614 + $debug[] = array(
1615 + 'sql' => $sql,
1616 + 'result' => $result,
1617 + );
1618 + }
1619 + if ( '' !== $wpdb->last_error ) {
1620 + $errors[] = array(
1621 + 'sql' => $sql,
1622 + 'error' => $wpdb->last_error,
1623 + );
1624 + }
1248 1625 }
1249 - return $this->WPDA_Rest_Response( sprintf( __( 'Successfully renamed %s database occurrences', 'wp-data-access' ), $renamed ) );
1626 + $context = array();
1627 + if ( $debug_mode ) {
1628 + $context['debug'] = $debug;
1629 + }
1630 + if ( 0 < count( $errors ) ) {
1631 + $context['errors'] = $errors;
1632 + return new \WP_Error('error', 'Failed renaming database', array(
1633 + 'status' => 401,
1634 + 'context' => $context,
1635 + ));
1636 + }
1637 + return $this->WPDA_Rest_Response( sprintf(
1638 + /* translators: %s = number of database substitutions */
1639 + __( 'Successfully renamed %s database occurrences', 'wp-data-access' ),
1640 + $renamed
1641 + ), null, $context );
1250 1642 }
1251 1643
1252 1644 public function app_chart_data( $request ) {
1253 1645 $app_id = $request->get_param( 'app_id' );
1646 + $search_custom = $request->get_param( 'search_custom' );
1647 + $shortcode_params = $request->get_param( 'shortcode_params' );
1254 1648 if ( !$this->main_app_access( $app_id, $msg ) ) {
1255 1649 if ( 'rest_cookie_invalid_nonce' === $msg ) {
1256 1650 return $this->invalid_nonce();
1257 1651 }
@@ -1263,8 +1657,15 @@
1263 1657 $app_container = WPDA_App_Container_Model::select( $app_id, 0 );
1264 1658 if ( 1 === count( $app_container ) && null !== $app_container[0]['cnt_query'] && '' !== trim( (string) $app_container[0]['cnt_query'] ) ) {
1265 1659 $dbs = $app_container[0]['cnt_dbs'];
1266 1660 $query = $app_container[0]['cnt_query'];
1661 + // Process shortcode and url parameters
1662 + $query = $this->process_params(
1663 + $query,
1664 + $search_custom,
1665 + null,
1666 + $shortcode_params
1667 + );
1267 1668 $wpdadb = WPDADB::get_db_connection( $dbs );
1268 1669 if ( null === $wpdadb ) {
1269 1670 // Error connecting.
1270 1671 return new \WP_Error('error', "Error connecting to database {$dbs}", array(
@@ -1272,14 +1673,23 @@
1272 1673 ));
1273 1674 }
1274 1675 $suppress = $wpdadb->suppress_errors( true );
1275 1676 $chart_data = $wpdadb->get_results( $query, 'ARRAY_A' );
1677 + // Add an * before each label to support numeric labels
1678 + $chart_data_converted = array();
1679 + foreach ( $chart_data as $data ) {
1680 + $row = array();
1681 + foreach ( $data as $key => $value ) {
1682 + $row['*' . $key] = $value;
1683 + }
1684 + $chart_data_converted[] = $row;
1685 + }
1276 1686 $wpdadb->get_results( "create temporary table `wpda_chart_data_types` as {$query}", 'ARRAY_A' );
1277 1687 $explain = $wpdadb->get_results( "desc `wpda_chart_data_types`", 'ARRAY_A' );
1278 1688 $wpdadb->get_results( "drop temporary table `wpda_chart_data_types`", 'ARRAY_A' );
1279 1689 $wpdadb->suppress_errors( $suppress );
1280 1690 return array(
1281 - 'data' => $chart_data,
1691 + 'data' => $chart_data_converted,
1282 1692 'explain' => $explain,
1283 1693 );
1284 1694 } else {
1285 1695 return new \WP_Error('error', $msg, array(
@@ -1324,8 +1734,10 @@
1324 1734 $app_id = $request->get_param( 'app_id' );
1325 1735 $cnt_id = $request->get_param( 'cnt_id' );
1326 1736 $target = $request->get_param( 'target' );
1327 1737 $settings = $request->get_param( 'settings' );
1738 + $chart = $request->get_param( 'chart' );
1739 + $map = $request->get_param( 'map' );
1328 1740 $theme = $request->get_param( 'theme' );
1329 1741 return $this->do_app_settings(
1330 1742 $app_id,
1331 1743 $cnt_id,
@@ -1330,8 +1742,10 @@
1330 1742 $app_id,
1331 1743 $cnt_id,
1332 1744 $target,
1333 1745 $settings,
1746 + $chart,
1747 + $map,
1334 1748 $theme
1335 1749 );
1336 1750 }
1337 1751
@@ -1494,11 +1908,13 @@
1494 1908 $app_id,
1495 1909 $cnt_id,
1496 1910 $target,
1497 1911 $settings,
1912 + $chart,
1913 + $map,
1498 1914 $theme
1499 1915 ) {
1500 - if ( 1 > $app_id || 1 > $cnt_id || 'table' !== $target && 'form' !== $target && 'rform' !== $target && 'theme' !== $target && 'chart' !== $target ) {
1916 + if ( 1 > $app_id || 1 > $cnt_id || 'table' !== $target && 'form' !== $target && 'rform' !== $target && 'theme' !== $target && 'chart' !== $target && 'map' !== $target && 'dashboard' !== $target ) {
1501 1917 return $this->bad_request();
1502 1918 }
1503 1919 if ( null === $settings || '' === $settings ) {
1504 1920 // Perform reset
@@ -1518,8 +1934,24 @@
1518 1934 'status' => 403,
1519 1935 ));
1520 1936 }
1521 1937 break;
1938 + case 'chart':
1939 + $error_msg = WPDA_App_Container_Model::update_chart_settings( $cnt_id, null );
1940 + if ( '' !== $error_msg ) {
1941 + return new \WP_Error('error', $error_msg, array(
1942 + 'status' => 403,
1943 + ));
1944 + }
1945 + break;
1946 + case 'map':
1947 + $error_msg = WPDA_App_Container_Model::update_map_settings( $cnt_id, null );
1948 + if ( '' !== $error_msg ) {
1949 + return new \WP_Error('error', $error_msg, array(
1950 + 'status' => 403,
1951 + ));
1952 + }
1953 + break;
1522 1954 case 'theme':
1523 1955 $error_msg = WPDA_App_Model::update_theme( $app_id, null );
1524 1956 if ( '' !== $error_msg ) {
1525 1957 return new \WP_Error('error', $error_msg, array(
@@ -1526,8 +1958,16 @@
1526 1958 'status' => 403,
1527 1959 ));
1528 1960 }
1529 1961 break;
1962 + case 'dashboard':
1963 + $error_msg = WPDA_App_Container_Model::update_dashboard_settings( $app_id, null );
1964 + if ( '' !== $error_msg ) {
1965 + return new \WP_Error('error', $error_msg, array(
1966 + 'status' => 403,
1967 + ));
1968 + }
1969 + break;
1530 1970 default:
1531 1971 return $this->bad_request();
1532 1972 }
1533 1973 return $this->WPDA_Rest_Response( __( 'Reset was successful', 'wp-data-access' ) );
@@ -1539,8 +1979,22 @@
1539 1979 return new \WP_Error('error', $error_msg, array(
1540 1980 'status' => 403,
1541 1981 ));
1542 1982 }
1983 + // Update chart settings
1984 + $error_msg = WPDA_App_Container_Model::update_chart_settings( $cnt_id, $chart );
1985 + if ( '' !== $error_msg ) {
1986 + return new \WP_Error('error', $error_msg, array(
1987 + 'status' => 403,
1988 + ));
1989 + }
1990 + // Update map settings
1991 + $error_msg = WPDA_App_Container_Model::update_map_settings( $cnt_id, $map );
1992 + if ( '' !== $error_msg ) {
1993 + return new \WP_Error('error', $error_msg, array(
1994 + 'status' => 403,
1995 + ));
1996 + }
1543 1997 } else {
1544 1998 if ( 'rform' === $target ) {
1545 1999 // Update rform settings
1546 2000 $error_msg = WPDA_App_Container_Model::update_rform_settings( $cnt_id, $settings );
@@ -1558,14 +2012,35 @@
1558 2012 'status' => 403,
1559 2013 ));
1560 2014 }
1561 2015 } else {
1562 - // Update form settings
1563 - $error_msg = WPDA_App_Container_Model::update_form_settings( $cnt_id, $settings );
1564 - if ( '' !== $error_msg ) {
1565 - return new \WP_Error('error', $error_msg, array(
1566 - 'status' => 403,
1567 - ));
2016 + if ( 'map' === $target ) {
2017 + // Update chart settings
2018 + $error_msg = WPDA_App_Container_Model::update_map_settings( $cnt_id, $settings );
2019 + if ( '' !== $error_msg ) {
2020 + return new \WP_Error('error', $error_msg, array(
2021 + 'status' => 403,
2022 + ));
2023 + }
2024 + } else {
2025 + if ( 'form' === $target ) {
2026 + // Update form settings
2027 + $error_msg = WPDA_App_Container_Model::update_form_settings( $cnt_id, $settings );
2028 + if ( '' !== $error_msg ) {
2029 + return new \WP_Error('error', $error_msg, array(
2030 + 'status' => 403,
2031 + ));
2032 + }
2033 + } else {
2034 + if ( 'dashboard' === $target ) {
2035 + $error_msg = WPDA_App_Container_Model::update_dashboard_settings( $cnt_id, $settings );
2036 + if ( '' !== $error_msg ) {
2037 + return new \WP_Error('error', $error_msg, array(
2038 + 'status' => 403,
2039 + ));
2040 + }
2041 + }
2042 + }
1568 2043 }
1569 2044 }
1570 2045 }
1571 2046 }
@@ -1686,9 +2161,9 @@
1686 2161 private function get_relation_columns( $container ) {
1687 2162 return null;
1688 2163 }
1689 2164
1690 - private function reorder_details( $cnt_id_from, $cnt_id_to ) {
2165 + private function reorder_details( $app_id, $cnt_id_from, $cnt_id_to ) {
1691 2166 return $this->bad_request();
1692 2167 }
1693 2168
1694 2169 private function get_app_apps_meta( $app, $apps ) {
@@ -1695,8 +2170,9 @@
1695 2170 $app_id_details = array_map( function ( $e ) {
1696 2171 if ( isset( $e['app_id_detail'] ) ) {
1697 2172 return $e['app_id_detail'];
1698 2173 }
2174 + return null;
1699 2175 }, $apps );
1700 2176 $app_titles = array();
1701 2177 foreach ( $app_id_details as $app_id_detail ) {
1702 2178 $app_detail = WPDA_App_Model::get_by_id( $app_id_detail );
@@ -1731,18 +2207,46 @@
1731 2207 }
1732 2208 $settings->env = $this->get_env();
1733 2209 global $wpdb;
1734 2210 $settings->wp = [
1735 - 'roles' => $this->get_wp_roles(),
1736 - 'users' => $this->get_wp_users(),
1737 - 'home' => admin_url( 'admin.php' ),
1738 - 'tables' => array_values( $wpdb->tables() ),
1739 - 'date_format' => get_option( 'date_format' ),
1740 - 'time_format' => get_option( 'time_format' ),
2211 + 'roles' => $this->get_wp_roles(),
2212 + 'users' => $this->get_wp_users(),
2213 + 'home' => admin_url( 'admin.php' ),
2214 + 'siteurl' => site_url(),
2215 + 'tables' => array_values( $wpdb->tables() ),
2216 + 'date_format' => get_option( 'date_format' ),
2217 + 'time_format' => get_option( 'time_format' ),
2218 + 'scroll_offset' => WPDA::get_option( WPDA::OPTION_APPS_SCROLL_OFFSET ),
2219 + 'upload' => @ini_get( 'upload_max_filesize' ),
2220 + 'uploadBytes' => $this->uploadToBytes( @ini_get( 'upload_max_filesize' ) ),
2221 + 'locale' => get_locale(),
1741 2222 ];
1742 2223 return $settings;
1743 2224 }
1744 2225
2226 + /**
2227 + * Convert a PHP shorthand notation (e.g., "2M", "512K", "1G") to bytes.
2228 + *
2229 + * @param string $val The shorthand size string.
2230 + *
2231 + * @return int The size in bytes.
2232 + */
2233 + private function uploadToBytes( $val ) : int {
2234 + $val = trim( $val );
2235 + $lastChar = strtolower( $val[strlen( $val ) - 1] );
2236 + $num = (float) $val;
2237 + switch ( $lastChar ) {
2238 + case 'g':
2239 + $num *= 1024;
2240 + case 'm':
2241 + $num *= 1024;
2242 + case 'k':
2243 + $num *= 1024;
2244 + break;
2245 + }
2246 + return (int) round( $num );
2247 + }
2248 +
1745 2249 private function get_app_container_meta( $app_id, $container, $rel_tab = false ) {
1746 2250 $app = WPDA_App_Model::get_by_id( $app_id );
1747 2251 if ( false === $app ) {
1748 2252 return $this->bad_request();
@@ -1755,9 +2259,9 @@
1755 2259 $response = array(
1756 2260 'app' => array(
1757 2261 'app' => $app,
1758 2262 'container' => array_map( function ( $value ) {
1759 - $show = current_user_can( 'manage_options' );
2263 + $show = WPDA::current_user_is_admin();
1760 2264 if ( !$show ) {
1761 2265 // Hide database and table name in responses for non admin users.
1762 2266 unset($value['cnt_dbs']);
1763 2267 unset($value['cnt_tbl']);
@@ -1773,10 +2277,18 @@
1773 2277 'update' => array(),
1774 2278 'delete' => array(),
1775 2279 );
1776 2280 $cls = WPDA_List_Columns_Cache::get_list_columns( $dbs, $tbl );
2281 + $columns = $cls->get_table_columns();
2282 + $columns_sorted = array();
2283 + foreach ( $columns as $column ) {
2284 + if ( isset( $column['column_name'] ) ) {
2285 + $columns_sorted[$column['column_name']] = $column;
2286 + }
2287 + }
1777 2288 $media = $this->get_media( $dbs, $tbl, $cls->get_table_columns() );
1778 - $response['columns'] = $cls->get_table_columns();
2289 + $response['columns'] = $columns;
2290 + $response['columns_sorted'] = $columns_sorted;
1779 2291 $response['table_labels'] = $cls->get_table_header_labels();
1780 2292 $response['form_labels'] = $cls->get_table_column_headers();
1781 2293 $response['primary_key'] = $cls->get_table_primary_key();
1782 2294 $response['access'] = $access;
@@ -1792,9 +2304,9 @@
1792 2304 $response['table_info'] = $this->get_table_info( $dbs, $tbl, $default_where );
1793 2305 return $this->WPDA_Rest_Response( '', $response );
1794 2306 }
1795 2307
1796 - private function get_app_meta( $app_id ) {
2308 + public function get_app_meta( $app_id ) {
1797 2309 $app = WPDA_App_Model::get_by_id( $app_id );
1798 2310 if ( !isset( $app[0]['app_type'] ) ) {
1799 2311 return $this->bad_request();
1800 2312 }
@@ -1811,30 +2323,46 @@
1811 2323 return $this->get_app_container_meta( $app_id, $container );
1812 2324 }
1813 2325 }
1814 2326
1815 - private function do_app_export_app( $app_id ) {
2327 + private function escapeUnicodeForExport( $str ) {
2328 + if ( empty( $str ) ) {
2329 + return $str;
2330 + }
2331 + return preg_replace_callback( '/\\\\(u[0-9a-fA-F]{4})/', function ( $matches ) {
2332 + return '\\\\' . $matches[1];
2333 + }, $str );
2334 + }
2335 +
2336 + private function do_app_export_app( $app_id, $main_app_id ) {
1816 2337 global $wpdb;
1817 2338 $quotes = function ( $value ) {
1818 2339 return str_replace( array(
1819 2340 "'",
1820 2341 '\\"',
2342 + "\\\\t",
1821 2343 "\\t",
2344 + "\\\\n",
1822 2345 "\\n",
1823 2346 "\\r\\n",
1824 - "\\r"
2347 + "\\r",
2348 + "\\d"
1825 2349 ), array(
1826 2350 "''",
1827 2351 '\\\\"',
2352 + "\\\\\\t",
1828 2353 "\\\\t",
2354 + "\\\\\\n",
1829 2355 "\\\\n",
1830 2356 "\\\\r\\\\n",
1831 - "\\\\r"
2357 + "\\\\r",
2358 + "\\\\\\\\\\d"
1832 2359 ), $value );
1833 2360 };
1834 2361 $app = WPDA_App_Model::get_by_id( $app_id );
1835 2362 $app_settings = ( null === $app[0]['app_settings'] ? 'null' : "{$quotes( $app[0]['app_settings'] )}" );
1836 2363 $app_theme = ( null === $app[0]['app_theme'] ? 'null' : "{$quotes( $app[0]['app_theme'] )}" );
2364 + // phpcs:ignore PluginCheck.CodeAnalysis.Heredoc.NotAllowed
1837 2365 $app_sql = <<<SQL
1838 2366 # Import app
1839 2367 insert into `{wp_prefix}wpda_app`
1840 2368 \t(`app_name`
@@ -1853,9 +2381,9 @@
1853 2381 \t,{$app[0]['app_add_to_menu']}
1854 2382 \t);
1855 2383
1856 2384 SET @APP_ID = LAST_INSERT_ID();
1857 -insert into `wpda_transfer_apps`
2385 +insert into `wpda_transfer_apps_{$main_app_id}`
1858 2386 values
1859 2387 ({$app[0]['app_id']}
1860 2388 ,(select LAST_INSERT_ID(`app_id`) from `{wp_prefix}wpda_app` order by 1 desc limit 1)
1861 2389 );
@@ -1864,15 +2392,23 @@
1864 2392 SQL;
1865 2393 $containers = WPDA_App_Container_Model::select_all( $app_id );
1866 2394 $containers_sql = '';
1867 2395 foreach ( $containers as $container ) {
1868 - $cnt_table = ( null === $container['cnt_table'] ? 'null' : "{$quotes( $container['cnt_table'] )}" );
1869 - $cnt_form = ( null === $container['cnt_form'] ? 'null' : "{$quotes( $container['cnt_form'] )}" );
1870 - $cnt_relation = ( null === $container['cnt_relation'] ? 'null' : "{$quotes( $container['cnt_relation'] )}" );
2396 + $cnt_table = ( null === $container['cnt_table'] ? 'null' : "'{$quotes( $container['cnt_table'] )}'" );
2397 + $cnt_form = ( null === $container['cnt_form'] ? 'null' : "'{$quotes( $container['cnt_form'] )}'" );
2398 + $cnt_relation = ( null === $container['cnt_relation'] ? 'null' : "'{$quotes( $container['cnt_relation'] )}'" );
2399 + $cnt_rform = ( null === $container['cnt_rform'] ? 'null' : "'{$quotes( $container['cnt_rform'] )}'" );
2400 + $cnt_chart = ( null === $container['cnt_chart'] ? 'null' : "'{$quotes( $container['cnt_chart'] )}'" );
2401 + $cnt_map = ( null === $container['cnt_map'] ? 'null' : "'{$quotes( $container['cnt_map'] )}'" );
2402 + $cnt_query = ( null === $container['cnt_query'] ? 'null' : "'{$quotes( $container['cnt_query'] )}'" );
1871 2403 // Replace default WordPress database with conversion string
1872 2404 $cnt_dbs = ( $wpdb->dbname === $container['cnt_dbs'] ? '{wp_schema}' : "{$quotes( $container['cnt_dbs'] )}" );
1873 2405 $cnt_table = str_replace( "\"dbs\":\"{$wpdb->dbname}\"", "\"dbs\":\"{wp_schema}\"", $cnt_table );
1874 2406 $cnt_form = str_replace( "\"dbs\":\"{$wpdb->dbname}\"", "\"dbs\":\"{wp_schema}\"", $cnt_form );
2407 + // Replace Unicode characters
2408 + $cnt_table = $this->escapeUnicodeForExport( $cnt_table );
2409 + $cnt_form = $this->escapeUnicodeForExport( $cnt_form );
2410 + // phpcs:ignore PluginCheck.CodeAnalysis.Heredoc.NotAllowed
1875 2411 $containers_sql .= <<<SQL
1876 2412 # Import app container
1877 2413 insert into `{wp_prefix}wpda_app_container`
1878 2414 \t(`cnt_dbs`
@@ -1883,8 +2419,12 @@
1883 2419 \t,`cnt_seq_nr`
1884 2420 \t,`cnt_table`
1885 2421 \t,`cnt_form`
1886 2422 \t,`cnt_relation`
2423 + ,`cnt_rform`
2424 + ,`cnt_chart`
2425 + ,`cnt_map`
2426 + ,`cnt_query`
1887 2427 \t)
1888 2428 values
1889 2429 \t('{$cnt_dbs}'
1890 2430 \t,'{$quotes( $container['cnt_tbl'] )}'
@@ -1891,14 +2431,18 @@
1891 2431 \t,'{$quotes( $container['cnt_cls'] )}'
1892 2432 \t,'{$quotes( $container['cnt_title'] )}'
1893 2433 \t,@APP_ID
1894 2434 \t,{$container['cnt_seq_nr']}
1895 -\t,'{$cnt_table}'
1896 -\t,'{$cnt_form}'
1897 -\t,'{$cnt_relation}'
2435 +\t,{$cnt_table}
2436 +\t,{$cnt_form}
2437 +\t,{$cnt_relation}
2438 +\t,{$cnt_rform}
2439 +\t,{$cnt_chart}
2440 +\t,{$cnt_map}
2441 +\t,{$cnt_query}
1898 2442 \t);
1899 2443
1900 -insert into `wpda_transfer_containers`
2444 +insert into `wpda_transfer_containers_{$main_app_id}`
1901 2445 values
1902 2446 ({$container['cnt_id']}
1903 2447 ,(select LAST_INSERT_ID(`cnt_id`) from `{wp_prefix}wpda_app_container` order by 1 desc limit 1)
1904 2448 );
@@ -1906,24 +2450,24 @@
1906 2450
1907 2451 SQL;
1908 2452 }
1909 2453 // Post update: update master container ids
2454 + // phpcs:ignore PluginCheck.CodeAnalysis.Heredoc.NotAllowed
1910 2455 $containers_sql .= <<<SQL
1911 2456 # Update app master container IDs
1912 -update `{wp_prefix}wpda_app_container` c
1913 -set c.`cnt_relation` =
2457 +update `{wp_prefix}wpda_app_container` as a
2458 +set a.`cnt_relation` =
1914 2459 (
1915 2460 select replace(
1916 -\t\t\ta.`cnt_relation`,
1917 -\t\t\tconcat('"cnt_id_master":"', b.cnt_id_old, '"'),
1918 -\t\t\tconcat('"cnt_id_master":"', b.cnt_id_new, '"')
1919 -\t\t)
1920 -\t\tfrom `{wp_prefix}wpda_app_container` a, `wpda_transfer_containers` b
1921 -\t\twhere a.cnt_id = c.cnt_id
1922 -\t\t and a.`cnt_relation` like concat('%"cnt_id_master":"', b.cnt_id_old, '"%')
2461 + a.`cnt_relation`,
2462 + concat('"cnt_id_master":"', b.cnt_id_old, '"'),
2463 + concat('"cnt_id_master":"', b.cnt_id_new, '"')
2464 + )
2465 + from `wpda_transfer_containers_{$main_app_id}` as b
2466 + where a.`cnt_relation` like concat('%"cnt_id_master":"', b.cnt_id_old, '"%')
1923 2467 )
1924 -where c.`app_id` = @APP_ID
1925 - and c.`cnt_relation` is not null;
2468 +where a.`app_id` = @APP_ID
2469 + and a.`cnt_relation` is not null;
1926 2470
1927 2471
1928 2472 SQL;
1929 2473 $apps = WPDA_App_Apps_Model::select_all( $app_id );
@@ -1928,11 +2472,12 @@
1928 2472 SQL;
1929 2473 $apps = WPDA_App_Apps_Model::select_all( $app_id );
1930 2474 $apps_sql = '';
1931 2475 foreach ( $apps as $app ) {
1932 - $apps_sql .= $this->do_app_export_app( $app['app_id_detail'] );
2476 + $apps_sql .= $this->do_app_export_app( $app['app_id_detail'], $main_app_id );
1933 2477 }
1934 2478 foreach ( $apps as $app ) {
2479 + // phpcs:ignore PluginCheck.CodeAnalysis.Heredoc.NotAllowed
1935 2480 $apps_sql .= <<<SQL
1936 2481 # Import app relationships
1937 2482 insert into `{wp_prefix}wpda_app_apps`
1938 2483 \t(`app_id`
@@ -1939,10 +2484,10 @@
1939 2484 \t,`app_id_detail`
1940 2485 \t,`seq_nr`\t\t\t\t\t
1941 2486 \t)
1942 2487 values
1943 -\t((select `app_id_new` from `wpda_transfer_apps` where `app_id_old` = {$app['app_id']})
1944 -\t,(select `app_id_new` from `wpda_transfer_apps` where `app_id_old` = {$app['app_id_detail']})
2488 +\t((select `app_id_new` from `wpda_transfer_apps_{$main_app_id}` where `app_id_old` = {$app['app_id']})
2489 +\t,(select `app_id_new` from `wpda_transfer_apps_{$main_app_id}` where `app_id_old` = {$app['app_id_detail']})
1945 2490 \t,{$app['seq_nr']}\t\t\t\t\t
1946 2491 \t);
1947 2492
1948 2493
@@ -1953,8 +2498,9 @@
1953 2498
1954 2499 private function do_app_export( $app_id ) {
1955 2500 global $wpdb;
1956 2501 $sql = '';
2502 + // phpcs:ignore PluginCheck.CodeAnalysis.Heredoc.NotAllowed
1957 2503 $begin_sql = <<<SQL
1958 2504 /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
1959 2505 /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
1960 2506 /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
@@ -1960,14 +2506,14 @@
1960 2506 /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
1961 2507 /*!40101 SET NAMES {$wpdb->charset} */;
1962 2508
1963 2509 # Create temporary table
1964 -CREATE TEMPORARY TABLE `wpda_transfer_containers`
2510 +CREATE TABLE `wpda_transfer_containers_{$app_id}`
1965 2511 (cnt_id_old bigint(20) unsigned
1966 2512 ,cnt_id_new bigint(20) unsigned
1967 2513 );
1968 2514
1969 -CREATE TEMPORARY TABLE `wpda_transfer_apps`
2515 +CREATE TABLE `wpda_transfer_apps_{$app_id}`
1970 2516 (app_id_old bigint(20) unsigned
1971 2517 ,app_id_new bigint(20) unsigned
1972 2518 );
1973 2519
@@ -1974,13 +2520,14 @@
1974 2520 SET @APP_ID = NULL;
1975 2521
1976 2522
1977 2523 SQL;
1978 - $sql .= $this->do_app_export_app( $app_id );
2524 + $sql .= $this->do_app_export_app( $app_id, $app_id );
2525 + // phpcs:ignore PluginCheck.CodeAnalysis.Heredoc.NotAllowed
1979 2526 $end_sql = <<<SQL
1980 2527 # Drop temporary table
1981 -DROP TEMPORARY TABLE `wpda_transfer_containers`;
1982 -DROP TEMPORARY TABLE `wpda_transfer_apps`;
2528 +DROP TABLE `wpda_transfer_containers_{$app_id}`;
2529 +DROP TABLE `wpda_transfer_apps_{$app_id}`;
1983 2530
1984 2531 /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
1985 2532 /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
1986 2533 /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
@@ -2069,9 +2616,9 @@
2069 2616 }
2070 2617 return $this->WPDA_Rest_Response( __( 'Changes successfully saved', 'wp-data-access' ) );
2071 2618 }
2072 2619
2073 - private function main_app_access( $app_id, &$msg = '' ) {
2620 + private function main_app_access( $app_id, &$msg = '', &$app = null ) {
2074 2621 // Get app info
2075 2622 $app = WPDA_App_Model::get_by_id( $app_id );
2076 2623 if ( false === $app ) {
2077 2624 // App not found
@@ -2114,9 +2661,9 @@
2114 2661 if ( !$this->main_app_access( $app_id, $msg ) ) {
2115 2662 return false;
2116 2663 }
2117 2664 // Get container
2118 - $container = WPDA_App_Container_Model::get_container( $cnt_id );
2665 + $container = WPDA_App_Container_Model::get_container( $app_id, $cnt_id );
2119 2666 if ( !is_array( $container ) || 0 === count( $container ) ) {
2120 2667 // Container not found
2121 2668 $msg = __( 'Bad request', 'wp-data-access' );
2122 2669 return false;
@@ -2123,12 +2670,14 @@
2123 2670 }
2124 2671 if ( 'select' !== $action ) {
2125 2672 $cnt_table = json_decode( (string) $container[0]['cnt_table'], true );
2126 2673 if ( !isset( $cnt_table['table']['transactions'][$action] ) || false === $cnt_table['table']['transactions'][$action] ) {
2127 - $cnt_relation = json_decode( (string) $container[0]['cnt_relation'], true );
2128 - if ( !(isset( $cnt_relation['cnt_id_master'] ) && $this->check_master_container_access( $cnt_relation['cnt_id_master'], $action )) ) {
2129 - $msg = __( 'Unauthorized', 'wp-data-access' );
2130 - return false;
2674 + if ( !isset( $cnt_table['table']['bulkActions'][$action] ) || false === $cnt_table['table']['bulkActions'][$action] ) {
2675 + $cnt_relation = json_decode( (string) $container[0]['cnt_relation'], true );
2676 + if ( !(isset( $cnt_relation['cnt_id_master'] ) && $this->check_master_container_access( $app_id, $cnt_relation['cnt_id_master'], $action )) ) {
2677 + $msg = __( 'Unauthorized', 'wp-data-access' );
2678 + return false;
2679 + }
2131 2680 }
2132 2681 }
2133 2682 }
2134 2683 // Return database name, table name and columns
@@ -2141,10 +2690,10 @@
2141 2690 );
2142 2691 return true;
2143 2692 }
2144 2693
2145 - private function check_master_container_access( $cnt_id, $action ) {
2146 - $container = WPDA_App_Container_Model::get_container( $cnt_id );
2694 + private function check_master_container_access( $app_id, $cnt_id, $action ) {
2695 + $container = WPDA_App_Container_Model::get_container( $app_id, $cnt_id );
2147 2696 if ( !is_array( $container ) || 0 === count( $container ) ) {
2148 2697 // Container not found
2149 2698 return false;
2150 2699 }
@@ -2150,9 +2699,9 @@
2150 2699 }
2151 2700 $cnt_table = json_decode( (string) $container[0]['cnt_table'], true );
2152 2701 if ( !isset( $cnt_table['table']['transactions'][$action] ) || false === $cnt_table['table']['transactions'][$action] ) {
2153 2702 $cnt_relation = json_decode( (string) $container[0]['cnt_relation'], true );
2154 - if ( !(isset( $cnt_relation['cnt_id_master'] ) && $this->check_master_container_access( $cnt_relation['cnt_id_master'], $action )) ) {
2703 + if ( !(isset( $cnt_relation['cnt_id_master'] ) && $this->check_master_container_access( $app_id, $cnt_relation['cnt_id_master'], $action )) ) {
2155 2704 return false;
2156 2705 }
2157 2706 }
2158 2707 return true;
@@ -2161,18 +2710,21 @@
2161 2710 private function process_params(
2162 2711 $where,
2163 2712 $search_custom,
2164 2713 $search_params,
2165 - $shortcode_params
2714 + $shortcode_params,
2715 + $dynamic_params = array()
2166 2716 ) {
2167 2717 // Process $search_custom > URL parameters
2168 2718 global $wpdb;
2719 + $replacements = array();
2720 + $nonce = bin2hex( random_bytes( 8 ) );
2169 2721 foreach ( self::METHODS as $method ) {
2170 2722 $offset = 0;
2171 2723 $search = $method . '[';
2724 + // Get filter
2172 2725 while ( ($pos_start = stripos( $where, $search, $offset )) !== false ) {
2173 2726 if ( ($pos_end = stripos( $where, ']', $pos_start )) !== false ) {
2174 - // Get filter
2175 2727 $filter = substr( $where, $pos_start, $pos_end - $pos_start + 1 );
2176 2728 // Get name
2177 2729 $arg_name = substr( $where, $pos_start + strlen( $search ), $pos_end - $pos_start - strlen( $search ) );
2178 2730 // Remove quotes from name
@@ -2178,44 +2730,36 @@
2178 2730 // Remove quotes from name
2179 2731 if ( substr( $arg_name, 0, 1 ) === "'" && substr( $arg_name, -1 ) === "'" ) {
2180 2732 $arg_name = substr( $arg_name, 1, -1 );
2181 2733 }
2182 - // Remove double quotes from name
2734 + // Remove quotes from name
2183 2735 if ( substr( $arg_name, 0, 1 ) === '"' && substr( $arg_name, -1 ) === '"' ) {
2184 2736 $arg_name = substr( $arg_name, 1, -1 );
2185 2737 }
2738 + $arg_value = null;
2186 2739 // Handle GET args
2187 - if ( $method === self::METHODS[0] || $method === self::METHODS[2] ) {
2740 + if ( $method === self::METHODS[0] && isset( $search_custom['get'][$arg_name] ) ) {
2741 + $arg_value = sanitize_text_field( wp_unslash( $search_custom['get'][$arg_name] ) );
2742 + } elseif ( $method === self::METHODS[1] && isset( $search_custom['post'][$arg_name] ) ) {
2743 + $arg_value = sanitize_text_field( wp_unslash( $search_custom['post'][$arg_name] ) );
2744 + } elseif ( $method === self::METHODS[2] ) {
2188 2745 if ( isset( $search_custom['get'][$arg_name] ) ) {
2189 2746 $arg_value = sanitize_text_field( wp_unslash( $search_custom['get'][$arg_name] ) );
2190 - $where = $wpdb->prepare( substr_replace(
2191 - $where,
2192 - '%s',
2193 - $pos_start,
2194 - $pos_end - $pos_start + 1
2195 - ), $arg_value );
2196 - } else {
2197 - if ( $method === self::METHODS[0] ) {
2198 - $where = str_replace( $filter, 'null', $where );
2199 - }
2747 + } elseif ( isset( $search_custom['post'][$arg_name] ) ) {
2748 + $arg_value = sanitize_text_field( wp_unslash( $search_custom['post'][$arg_name] ) );
2200 2749 }
2201 2750 }
2202 2751 // Handle POST args
2203 - if ( $method === self::METHODS[1] || $method === self::METHODS[2] ) {
2204 - if ( isset( $search_custom['post'][$arg_name] ) ) {
2205 - $arg_value = sanitize_text_field( wp_unslash( $search_custom['post'][$arg_name] ) );
2206 - $where = $wpdb->prepare( substr_replace(
2207 - $where,
2208 - '%s',
2209 - $pos_start,
2210 - $pos_end - $pos_start + 1
2211 - ), $arg_value );
2212 - } else {
2213 - $where = str_replace( $filter, 'null', $where );
2214 - }
2752 + $placeholder = '###URL_PLACEHOLDER_' . $nonce . '_' . md5( $filter ) . '###';
2753 + $where = str_replace( $filter, $placeholder, $where );
2754 + // Handle REQUEST args
2755 + if ( $arg_value !== null ) {
2756 + $replacements[$placeholder] = $wpdb->prepare( '%s', $arg_value );
2757 + } else {
2758 + $replacements[$placeholder] = 'null';
2215 2759 }
2216 2760 }
2217 - $offset = $pos_start + 6;
2761 + $offset = $pos_start + 1;
2218 2762 if ( $offset > strlen( $where ) ) {
2219 2763 $offset = strlen( $where ) - 1;
2220 2764 }
2221 2765 }
@@ -2224,21 +2768,16 @@
2224 2768 if ( is_array( $search_params ) && 1 === count( $search_params ) ) {
2225 2769 $filter_field_name = $this->sanitize_db_identifier( array_keys( $search_params )[0] );
2226 2770 $filter_field_value = sanitize_text_field( $search_params[$filter_field_name] );
2227 2771 $filter_field_name_array = array_map( 'trim', explode( ',', $filter_field_name ) );
2228 - //phpcs:ignore - 8.1 proof
2229 2772 $filter_field_value_array = array_map( 'trim', explode( ',', $filter_field_value ) );
2230 - //phpcs:ignore - 8.1 proof
2231 2773 if ( count( $filter_field_name_array ) === count( $filter_field_value_array ) ) {
2232 - //phpcs:ignore - 8.1 proof
2233 2774 // Add filter to where clause.
2234 2775 for ($i = 0; $i < count( $filter_field_name_array ); $i++) {
2235 - // phpcs:ignore Generic.CodeAnalysis.ForLoopWithTestFunctionCall, Squiz.PHP.DisallowSizeFunctionsInLoops
2236 - $where .= (( '' === $where ? '' : ' and ' )) . $wpdb->prepare(
2237 - ' `%1s` like %s ',
2238 - // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders
2239 - array($filter_field_name_array[$i], $filter_field_value_array[$i])
2240 - );
2776 + $where .= ( '' === $where ? '' : ' and ' );
2777 + $placeholder = '###SHORTCODE_PARAM_' . $nonce . '_' . md5( $filter_field_name_array[$i] ) . '###';
2778 + $where .= ' `' . $filter_field_name_array[$i] . '` like ' . $placeholder;
2779 + $replacements[$placeholder] = $wpdb->prepare( '%s', $filter_field_value_array[$i] );
2241 2780 }
2242 2781 }
2243 2782 }
2244 2783 // Substitute all shortcode parameters
@@ -2245,13 +2784,11 @@
2245 2784 if ( is_array( $shortcode_params ) ) {
2246 2785 foreach ( $shortcode_params as $column_name => $column_value ) {
2247 2786 $occurences = substr_count( strtolower( $where ), strtolower( "shortcodeParam['{$column_name}']" ) );
2248 2787 if ( 0 < $occurences ) {
2249 - $column_values = array();
2250 - for ($i = 0; $i < $occurences; $i++) {
2251 - $column_values[] = sanitize_text_field( $column_value );
2252 - }
2253 - $where = $wpdb->prepare( str_ireplace( "shortcodeParam['{$column_name}']", '%s', $where ), $column_values );
2788 + $placeholder = '###SHORTCODE_PARAM_' . $nonce . '_' . md5( $column_name ) . '###';
2789 + $where = str_ireplace( "shortcodeParam['{$column_name}']", $placeholder, $where );
2790 + $replacements[$placeholder] = $wpdb->prepare( '%s', sanitize_text_field( $column_value ) );
2254 2791 }
2255 2792 }
2256 2793 }
2257 2794 // Substitute all unused shortcode parameter calls with null
@@ -2266,8 +2803,18 @@
2266 2803 if ( $offset > strlen( $where ) ) {
2267 2804 $offset = strlen( $where ) - 1;
2268 2805 }
2269 2806 }
2270 - return $where;
2807 + // Substitute all dynamic parameters
2808 + if ( is_array( $dynamic_params ) && 0 < count( $dynamic_params ) ) {
2809 + foreach ( $dynamic_params as $column_name => $column_value ) {
2810 + if ( stripos( $where, "{:{$column_name}}" ) !== false ) {
2811 + $placeholder = '###DYNAMIC_PARAM_' . $nonce . '_' . md5( $column_name ) . '###';
2812 + $where = str_ireplace( "{:{$column_name}}", $placeholder, $where );
2813 + $replacements[$placeholder] = $wpdb->prepare( '%s', $column_value );
2814 + }
2815 + }
2816 + }
2817 + return strtr( $where, $replacements );
2271 2818 }
2272 2819
2273 2820 }