PluginProbe
WP Data Access – App Builder for Tables, Forms, Charts, Maps & Dashboards / 5.5.84
WP Data Access – App Builder for Tables, Forms, Charts, Maps & Dashboards v5.5.84
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 5.5.41 5.5.42 All 160 releases
← All changes | WPDataAccess/API/WPDA_Apps.php +631 -158 5.5.345.5.84 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
@@ -273,8 +276,19 @@
273 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 + ),
277 291 'chart' => array(
278 292 'required' => false,
279 293 'type' => 'string',
280 294 'description' => __( 'Chart settings - JSON string', 'wp-data-access' ),
@@ -315,8 +329,32 @@
315 329 'row_count_estimate' => $this->get_param( 'row_count_estimate' ),
316 330 'media' => $this->get_param( 'media' ),
317 331 'rel_tab' => $this->get_param( 'rel_tab' ),
318 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 + ),
319 357 ),
320 358 ) );
321 359 register_rest_route( WPDA_API::WPDA_NAMESPACE, 'app/get', array(
322 360 'methods' => array('POST'),
@@ -425,8 +463,9 @@
425 463 'search_params' => $this->get_param( 'search_params' ),
426 464 'shortcode_params' => $this->get_param( 'search_params' ),
427 465 'md' => $this->get_param( 'md' ),
428 466 'cascade' => $this->get_param( 'cascade' ),
467 + 'values' => $this->get_param( 'md' ),
429 468 ),
430 469 ) );
431 470 register_rest_route( WPDA_API::WPDA_NAMESPACE, 'app/lookup/dbs', array(
432 471 'methods' => array('POST'),
@@ -467,9 +506,11 @@
467 506 'methods' => array('POST'),
468 507 'callback' => array($this, 'app_chart_data'),
469 508 'permission_callback' => '__return_true',
470 509 'args' => array(
471 - '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' ),
472 513 ),
473 514 ) );
474 515 register_rest_route( WPDA_API::WPDA_NAMESPACE, 'app/dbs/rename', array(
475 516 'methods' => array('POST'),
@@ -477,12 +518,195 @@
477 518 'permission_callback' => '__return_true',
478 519 'args' => array(
479 520 'dbs_source' => $this->get_param( 'dbs' ),
480 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 + ),
481 529 ),
482 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 + // PWA
580 + register_rest_route( WPDA_API::WPDA_NAMESPACE, 'app/pwa/get', array(
581 + 'methods' => array('POST'),
582 + 'callback' => array($this, 'app_wpa_get'),
583 + 'permission_callback' => '__return_true',
584 + 'args' => array(
585 + 'app_id' => $this->get_param( 'app_id' ),
586 + ),
587 + ) );
588 + register_rest_route( WPDA_API::WPDA_NAMESPACE, 'app/pwa/activate', array(
589 + 'methods' => array('POST'),
590 + 'callback' => array($this, 'app_wpa_activate'),
591 + 'permission_callback' => '__return_true',
592 + 'args' => array(
593 + 'app_id' => $this->get_param( 'app_id' ),
594 + ),
595 + ) );
596 + register_rest_route( WPDA_API::WPDA_NAMESPACE, 'app/pwa/deactivate', array(
597 + 'methods' => array('POST'),
598 + 'callback' => array($this, 'app_wpa_deactivate'),
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/upload', array(
605 + 'methods' => array('POST'),
606 + 'callback' => array($this, 'app_upload'),
607 + 'permission_callback' => '__return_true',
608 + 'args' => array(
609 + 'app_id' => $this->get_param( 'app_id' ),
610 + 'cnt_id' => $this->get_param( 'cnt_id' ),
611 + 'pk' => array(
612 + 'required' => true,
613 + 'type' => 'string',
614 + 'description' => __( 'Primary key in JSON format', 'wp-data-access' ),
615 + 'sanitize_callback' => 'sanitize_text_field',
616 + 'validate_callback' => 'rest_validate_request_arg',
617 + ),
618 + 'col' => $this->get_param( 'col' ),
619 + ),
620 + ) );
621 + register_rest_route( WPDA_API::WPDA_NAMESPACE, 'app/download', array(
622 + 'methods' => array('POST'),
623 + 'callback' => array($this, 'app_download'),
624 + 'permission_callback' => '__return_true',
625 + 'args' => array(
626 + 'app_id' => $this->get_param( 'app_id' ),
627 + 'cnt_id' => $this->get_param( 'cnt_id' ),
628 + 'pk' => array(
629 + 'required' => true,
630 + 'type' => 'string',
631 + 'description' => __( 'Primary key in JSON format', 'wp-data-access' ),
632 + 'sanitize_callback' => 'sanitize_text_field',
633 + 'validate_callback' => 'rest_validate_request_arg',
634 + ),
635 + 'col' => $this->get_param( 'col' ),
636 + ),
637 + ) );
483 638 }
484 639
640 + public function app_download( $request ) {
641 + return $this->WPDA_Rest_Response( 'OK' );
642 + }
643 +
644 + public function app_upload( $request ) {
645 + return $this->WPDA_Rest_Response( 'OK' );
646 + }
647 +
648 + public function app_wpa_deactivate( $request ) {
649 + if ( !$this->current_user_can_access() ) {
650 + // Only admins
651 + return $this->unauthorized();
652 + }
653 + if ( !$this->current_user_token_valid( $request ) ) {
654 + return $this->invalid_nonce();
655 + }
656 + return $this->WPDA_Rest_Response( 'OK' );
657 + }
658 +
659 + public function app_wpa_activate( $request ) {
660 + if ( !$this->current_user_can_access() ) {
661 + // Only admins
662 + return $this->unauthorized();
663 + }
664 + if ( !$this->current_user_token_valid( $request ) ) {
665 + return $this->invalid_nonce();
666 + }
667 + return $this->WPDA_Rest_Response( 'OK' );
668 + }
669 +
670 + public function app_wpa_get( $request ) {
671 + if ( !$this->current_user_can_access() ) {
672 + // Only admins
673 + return $this->unauthorized();
674 + }
675 + if ( !$this->current_user_token_valid( $request ) ) {
676 + return $this->invalid_nonce();
677 + }
678 + return $this->WPDA_Rest_Response( 'NOT FOUND' );
679 + }
680 +
681 + public function app_call( $request ) {
682 + return $this->bad_request();
683 + }
684 +
685 + public function app_lang_get( $request ) {
686 + if ( !$this->current_user_can_access() ) {
687 + // Only admins
688 + return $this->unauthorized();
689 + }
690 + if ( !$this->current_user_token_valid( $request ) ) {
691 + return $this->invalid_nonce();
692 + }
693 + return $this->WPDA_Rest_Response( '', WPDA_App_Localization::get() );
694 + }
695 +
696 + public function app_lang_set( $request ) {
697 + if ( !$this->current_user_can_access() ) {
698 + // Only admins
699 + return $this->unauthorized();
700 + }
701 + if ( !$this->current_user_token_valid( $request ) ) {
702 + return $this->invalid_nonce();
703 + }
704 + $localizations = $request->get_param( 'localizations' );
705 + WPDA_App_Localization::set( $localizations );
706 + return $this->WPDA_Rest_Response( 'Translation successfully saved' );
707 + }
708 +
485 709 private function get_app_columns( $columns ) {
486 710 if ( !is_array( $columns ) ) {
487 711 return false;
488 712 }
@@ -597,9 +821,9 @@
597 821 $tbl,
598 822 $msg,
599 823 $settings
600 824 ) ) {
601 - $container = WPDA_App_Container_Model::get_container( $cnt_id );
825 + $container = WPDA_App_Container_Model::get_container( $app_id, $cnt_id );
602 826 if ( !isset( $container[0] ) ) {
603 827 return $this->bad_request();
604 828 }
605 829 return $this->get_app_container_meta( $app_id, $container, $rel_tab );
@@ -626,17 +850,17 @@
626 850 $tbl,
627 851 $msg,
628 852 $settings
629 853 ) ) {
630 - $container = WPDA_App_Container_Model::get_container( $cnt_id_from );
854 + $container = WPDA_App_Container_Model::get_container( $app_id, $cnt_id_from );
631 855 if ( !isset( $container[0] ) ) {
632 856 return $this->bad_request();
633 857 }
634 - $container = WPDA_App_Container_Model::get_container( $cnt_id_to );
858 + $container = WPDA_App_Container_Model::get_container( $app_id, $cnt_id_to );
635 859 if ( !isset( $container[0] ) ) {
636 860 return $this->bad_request();
637 861 }
638 - return $this->reorder_details( $cnt_id_from, $cnt_id_to );
862 + return $this->reorder_details( $app_id, $cnt_id_from, $cnt_id_to );
639 863 } else {
640 864 if ( 'rest_cookie_invalid_nonce' === $msg ) {
641 865 return $this->invalid_nonce();
642 866 } else {
@@ -716,8 +940,9 @@
716 940 $search_column_fns,
717 941 &$default_where,
718 942 &$lookups
719 943 ) {
944 + return $this->bad_request();
720 945 }
721 946
722 947 private function build_relationships(
723 948 $container,
@@ -724,8 +949,9 @@
724 949 &$m2m_relationship,
725 950 $tbl,
726 951 &$default_where
727 952 ) {
953 + return $this->bad_request();
728 954 }
729 955
730 956 public function app_select( $request ) {
731 957 $app_id = $request->get_param( 'app_id' );
@@ -747,12 +973,19 @@
747 973 $row_count_estimate = $request->get_param( 'row_count_estimate' );
748 974 $media = $request->get_param( 'media' );
749 975 $rel_tab = $request->get_param( 'rel_tab' );
750 976 $client_side = '1' === $request->get_param( 'client_side' );
977 + $geo_radius = $request->get_param( 'geo_radius' );
978 + $docs = array();
751 979 $default_where = '';
752 980 $default_orderby = '';
753 981 $lookups = array();
754 982 $m2m_relationship = array();
983 + if ( $client_side ) {
984 + // Delete search values on refresh
985 + $search = '';
986 + $search_columns = array();
987 + }
755 988 if ( $this->check_app_access(
756 989 $app_id,
757 990 $cnt_id,
758 991 'select',
@@ -760,27 +993,57 @@
760 993 $tbl,
761 994 $msg,
762 995 $settings
763 996 ) ) {
764 - $container = WPDA_App_Container_Model::get_container( $cnt_id );
765 - if ( '1' === $rel_tab ) {
997 + $container = WPDA_App_Container_Model::get_container( $app_id, $cnt_id );
998 + $app = WPDA_App_Model::get_by_id( $app_id );
999 + if ( isset( $app[0]['app_type'], $container[0]['cnt_map'] ) && '2' == $app[0]['app_type'] && null !== $container[0]['cnt_map'] ) {
1000 + // App = Map
1001 + // Get default where map
1002 + $map_json = json_decode( (string) $container[0]['cnt_map'], true );
1003 + if ( isset( $map_json['setup']['defaultWhere'] ) && null !== $map_json['setup']['defaultWhere'] && '' !== trim( $map_json['setup']['defaultWhere'] ) ) {
1004 + $default_where = $map_json['setup']['defaultWhere'];
1005 + }
766 1006 } else {
767 - $table_settings = $settings['table'] ?? array();
768 - // Get default where clause
769 - if ( isset( $table_settings['table']['defaultWhere'] ) ) {
770 - $default_where = $table_settings['table']['defaultWhere'];
1007 + // All other apps (not being a map)
1008 + if ( '1' === $rel_tab ) {
1009 + } else {
1010 + $table_settings = $settings['table'] ?? array();
1011 + // Get default where clause
1012 + if ( isset( $table_settings['table']['defaultWhere'] ) ) {
1013 + $default_where = $table_settings['table']['defaultWhere'];
1014 + }
1015 + // Get default order by
1016 + if ( isset( $table_settings['table']['defaultOrderBy'] ) ) {
1017 + $default_orderby_db = $table_settings['table']['defaultOrderBy'];
1018 + if ( is_array( $default_orderby_db ) ) {
1019 + foreach ( $default_orderby_db as $orderby ) {
1020 + if ( isset( $orderby['columnName'], $orderby['order'] ) && '' !== trim( $orderby['columnName'] ) ) {
1021 + $default_orderby .= (( '' === $default_orderby ? 'order by ' : ',' )) . '`' . WPDA::remove_backticks( $orderby['columnName'] ) . '` ' . (( 'desc' === $orderby['order'] ? 'desc' : 'asc' ));
1022 + }
1023 + }
1024 + }
1025 + }
771 1026 }
772 - // Get default order by
773 - if ( isset( $table_settings['table']['defaultOrderBy'] ) ) {
774 - $default_orderby_db = $table_settings['table']['defaultOrderBy'];
775 - if ( is_array( $default_orderby_db ) ) {
776 - foreach ( $default_orderby_db as $orderby ) {
777 - if ( isset( $orderby['columnName'], $orderby['order'] ) && '' !== trim( $orderby['columnName'] ) ) {
778 - $default_orderby .= (( '' === $default_orderby ? 'order by ' : ',' )) . '`' . WPDA::remove_backticks( $orderby['columnName'] ) . '` ' . (( 'desc' === $orderby['order'] ? 'desc' : 'asc' ));
779 - }
1027 + }
1028 + if ( isset( $settings['columns'] ) && is_array( $settings['columns'] ) && '1' !== $rel_tab ) {
1029 + $queryable_columns = array();
1030 + if ( isset( $settings['table']['columns'] ) && is_array( $settings['table']['columns'] ) ) {
1031 + foreach ( $settings['table']['columns'] as $column ) {
1032 + if ( isset( $column['queryable'] ) && $column['queryable'] ) {
1033 + $queryable_columns[] = $column['columnName'];
780 1034 }
781 1035 }
782 1036 }
1037 + $app_columns = array();
1038 + foreach ( $settings['columns'] as $column ) {
1039 + if ( isset( $column['columnName'], $column['isSelected'] ) && $column['isSelected'] ) {
1040 + $app_columns[$column['columnName']] = in_array( $column['columnName'], $queryable_columns );
1041 + }
1042 + }
1043 + if ( 0 < count( $app_columns ) ) {
1044 + $col = $app_columns;
1045 + }
783 1046 }
784 1047 $table_api = new WPDA_Table();
785 1048 return $table_api->select(
786 1049 $dbs,
@@ -805,9 +1068,11 @@
805 1068 $lookups,
806 1069 $md,
807 1070 $m2m_relationship,
808 1071 $search_data_types,
809 - $client_side
1072 + $client_side,
1073 + $geo_radius,
1074 + $docs
810 1075 );
811 1076 } else {
812 1077 if ( 'rest_cookie_invalid_nonce' === $msg ) {
813 1078 return $this->invalid_nonce();
@@ -831,8 +1096,9 @@
831 1096 return array_map( function ( $value ) {
832 1097 if ( true === $value['isSelected'] ) {
833 1098 return $value['columnName'];
834 1099 }
1100 + return null;
835 1101 }, $columns );
836 1102 }
837 1103
838 1104 public function app_get( $request ) {
@@ -840,8 +1106,9 @@
840 1106 $cnt_id = $request->get_param( 'cnt_id' );
841 1107 $key = $request->get_param( 'key' );
842 1108 $media = $request->get_param( 'media' );
843 1109 $rel_tab = $request->get_param( 'rel_tab' );
1110 + $docs = array();
844 1111 if ( $this->check_app_access(
845 1112 $app_id,
846 1113 $cnt_id,
847 1114 'select',
@@ -861,9 +1128,10 @@
861 1128 $tbl,
862 1129 $key,
863 1130 $media,
864 1131 $column_names,
865 - $default_where
1132 + $default_where,
1133 + $docs
866 1134 );
867 1135 } else {
868 1136 if ( 'rest_cookie_invalid_nonce' === $msg ) {
869 1137 return $this->invalid_nonce();
@@ -952,53 +1220,9 @@
952 1220 }
953 1221 }
954 1222
955 1223 public function app_update_inline( $request ) {
956 - $app_id = $request->get_param( 'app_id' );
957 - $cnt_id = $request->get_param( 'cnt_id' );
958 - $key = $request->get_param( 'key' );
959 - $val = $request->get_param( 'val' );
960 - if ( $this->check_app_access(
961 - $app_id,
962 - $cnt_id,
963 - 'select',
964 - $dbs,
965 - $tbl,
966 - $msg,
967 - $settings
968 - ) ) {
969 - foreach ( $val as $column_name => $column ) {
970 - $found = false;
971 - if ( isset( $settings['table']['columns'] ) ) {
972 - foreach ( $settings['table']['columns'] as $settings_column ) {
973 - if ( isset( $settings_column['columnName'] ) && $column_name === $settings_column['columnName'] ) {
974 - $found = true;
975 - }
976 - }
977 - if ( !$found ) {
978 - return $this->unauthorized();
979 - }
980 - }
981 - }
982 - $column_names = $this->get_app_form_columns( $settings );
983 - if ( false === $column_names ) {
984 - $column_names = array();
985 - }
986 - $table_api = new WPDA_Table();
987 - return $table_api->update(
988 - $dbs,
989 - $tbl,
990 - $key,
991 - $val,
992 - $column_names
993 - );
994 - } else {
995 - if ( 'rest_cookie_invalid_nonce' === $msg ) {
996 - return $this->invalid_nonce();
997 - } else {
998 - return $this->unauthorized();
999 - }
1000 - }
1224 + return $this->bad_request();
1001 1225 }
1002 1226
1003 1227 public function app_delete( $request ) {
1004 1228 $app_id = $request->get_param( 'app_id' );
@@ -1047,8 +1271,9 @@
1047 1271 $search_params = $request->get_param( 'search_params' );
1048 1272 $shortcode_params = $request->get_param( 'shortcode_params' );
1049 1273 $md = $request->get_param( 'md' );
1050 1274 $cascade = $request->get_param( 'cascade' );
1275 + $values = $request->get_param( 'values' );
1051 1276 $default_where = '';
1052 1277 $default_where_lookup = '';
1053 1278 $lookups = array();
1054 1279 $m2m_relationship = array();
@@ -1060,9 +1285,9 @@
1060 1285 $tbl,
1061 1286 $msg,
1062 1287 $settings
1063 1288 ) ) {
1064 - $container = WPDA_App_Container_Model::get_container( $cnt_id );
1289 + $container = WPDA_App_Container_Model::get_container( $app_id, $cnt_id );
1065 1290 if ( !isset( $container[0] ) ) {
1066 1291 return $this->bad_request();
1067 1292 }
1068 1293 $lookup = array();
@@ -1113,9 +1338,10 @@
1113 1338 $this->process_params(
1114 1339 $default_where_lookup,
1115 1340 $search_custom,
1116 1341 $search_params,
1117 - $shortcode_params
1342 + $shortcode_params,
1343 + $values
1118 1344 ),
1119 1345 '1' === $cascade,
1120 1346 $tbl,
1121 1347 $col,
@@ -1122,9 +1348,10 @@
1122 1348 $this->process_params(
1123 1349 $default_where,
1124 1350 $search_custom,
1125 1351 $search_params,
1126 - $shortcode_params
1352 + $shortcode_params,
1353 + $values
1127 1354 ),
1128 1355 $search,
1129 1356 $cols,
1130 1357 $search_columns,
@@ -1145,8 +1372,12 @@
1145 1372 }
1146 1373 }
1147 1374
1148 1375 public function app_lookup_dbs( $request ) {
1376 + // Only admins are allowed to configre lookups
1377 + if ( !$this->current_user_can_access() ) {
1378 + return $this->unauthorized();
1379 + }
1149 1380 $app_id = $request->get_param( 'app_id' );
1150 1381 $cnt_id = $request->get_param( 'cnt_id' );
1151 1382 if ( $this->check_app_access(
1152 1383 $app_id,
@@ -1169,8 +1400,12 @@
1169 1400 }
1170 1401 }
1171 1402
1172 1403 public function app_lookup_tbl( $request ) {
1404 + // Only admins are allowed to configre lookups
1405 + if ( !$this->current_user_can_access() ) {
1406 + return $this->unauthorized();
1407 + }
1173 1408 $app_id = $request->get_param( 'app_id' );
1174 1409 $cnt_id = $request->get_param( 'cnt_id' );
1175 1410 $dbs = $request->get_param( 'dbs' );
1176 1411 if ( $this->check_app_access(
@@ -1194,8 +1429,12 @@
1194 1429 }
1195 1430 }
1196 1431
1197 1432 public function app_lookup_cls( $request ) {
1433 + // Only admins are allowed to configre lookups
1434 + if ( !$this->current_user_can_access() ) {
1435 + return $this->unauthorized();
1436 + }
1198 1437 $app_id = $request->get_param( 'app_id' );
1199 1438 $cnt_id = $request->get_param( 'cnt_id' );
1200 1439 $dbs = $request->get_param( 'dbs' );
1201 1440 $tbl = $request->get_param( 'tbl' );
@@ -1220,23 +1459,14 @@
1220 1459 }
1221 1460 }
1222 1461
1223 1462 public function app_qb_list( $request ) {
1224 - if ( !$this->current_user_can_access() ) {
1225 - return $this->unauthorized();
1226 - }
1227 - if ( !$this->current_user_token_valid( $request ) ) {
1228 - return $this->invalid_nonce();
1229 - }
1230 - $queries = get_user_meta( WPDA::get_current_user_id(), 'wpda_query_builder' );
1231 - if ( false === $queries ) {
1232 - $queries = array();
1233 - }
1234 - return $this->WPDA_Rest_Response( '', $queries );
1463 + $qb = new WPDA_QB();
1464 + return $qb->open( $request );
1235 1465 }
1236 1466
1237 1467 public function app_dbs_rename( $request ) {
1238 - if ( !$this->current_user_can_access( true ) ) {
1468 + if ( !$this->current_user_can_access() ) {
1239 1469 // Only admins
1240 1470 return $this->unauthorized();
1241 1471 }
1242 1472 if ( !$this->current_user_token_valid( $request ) ) {
@@ -1250,8 +1480,71 @@
1250 1480 ));
1251 1481 }
1252 1482 global $wpdb;
1253 1483 $renamed = 0;
1484 + $debug_mode = 'on' === WPDA::get_option( WPDA::OPTION_PLUGIN_DEBUG );
1485 + $debug = array();
1486 + $errors = array();
1487 + if ( $request->get_param( 'appDbId' ) ) {
1488 + // Update app only
1489 + $appDbId = $request->get_param( 'appDbId' );
1490 + // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared -- plugin table
1491 + $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) );
1492 + $result = $wpdb->query( $sql );
1493 + // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
1494 + $renamed += $result;
1495 + if ( $debug_mode ) {
1496 + $debug[] = array(
1497 + 'sql' => $sql,
1498 + 'result' => $result,
1499 + );
1500 + }
1501 + if ( '' !== $wpdb->last_error ) {
1502 + $errors[] = array(
1503 + 'sql' => $sql,
1504 + 'error' => $wpdb->last_error,
1505 + );
1506 + }
1507 + $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");
1508 + foreach ( $sql_content as $sql ) {
1509 + // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared -- plugin table
1510 + $result = $wpdb->query( $wpdb->prepare( $sql, array(
1511 + $dbs_source,
1512 + $dbs_destination,
1513 + $dbs_source,
1514 + $appDbId
1515 + ) ) );
1516 + $renamed += $result;
1517 + if ( $debug_mode ) {
1518 + $debug[] = array(
1519 + 'sql' => $sql,
1520 + 'result' => $result,
1521 + );
1522 + }
1523 + if ( '' !== $wpdb->last_error ) {
1524 + $errors[] = array(
1525 + 'sql' => $sql,
1526 + 'error' => $wpdb->last_error,
1527 + );
1528 + }
1529 + }
1530 + $context = array();
1531 + if ( $debug_mode ) {
1532 + $context['debug'] = $debug;
1533 + }
1534 + if ( 0 < count( $errors ) ) {
1535 + $context['errors'] = $errors;
1536 + return new \WP_Error('error', 'Failed renaming database', array(
1537 + 'status' => 401,
1538 + 'context' => $context,
1539 + ));
1540 + }
1541 + return $this->WPDA_Rest_Response( sprintf(
1542 + /* translators: %s = number of database substitutions */
1543 + __( 'Successfully renamed %s database occurrences', 'wp-data-access' ),
1544 + $renamed
1545 + ), null, $context );
1546 + }
1254 1547 // Rename all occurrences in repository tables and apps
1255 1548 $sqls = array(
1256 1549 "update `{$wpdb->prefix}wpda_publisher` set `pub_schema_name` = %s where `pub_schema_name` = %s",
1257 1550 "update `{$wpdb->prefix}wpda_project_page` set `page_schema_name` = %s where `page_schema_name` = %s",
@@ -1259,22 +1552,68 @@
1259 1552 "update `{$wpdb->prefix}wpda_media` set `media_schema_name` = %s where `media_schema_name` = %s",
1260 1553 "update `{$wpdb->prefix}wpda_menus` set `menu_schema_name` = %s where `menu_schema_name` = %s",
1261 1554 "update `{$wpdb->prefix}wpda_table_design` set `wpda_schema_name` = %s where `wpda_schema_name` = %s",
1262 1555 "update `{$wpdb->prefix}wpda_table_settings` set `wpda_schema_name` = %s where `wpda_schema_name` = %s",
1263 - "update `{$wpdb->prefix}wpda_container` set `cnt_dbs` = %s where `cnt_dbs` = %s"
1556 + "update `{$wpdb->prefix}wpda_app_container` set `cnt_dbs` = %s where `cnt_dbs` = %s"
1264 1557 );
1265 1558 foreach ( $sqls as $sql ) {
1266 - $renamed += $wpdb->query( $wpdb->prepare( $sql, array($dbs_destination, $dbs_source) ) );
1559 + // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared -- plugin table
1560 + $result = $wpdb->query( $wpdb->prepare( $sql, array($dbs_destination, $dbs_source) ) );
1561 + // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
1562 + $renamed += $result;
1563 + if ( $debug_mode ) {
1564 + $debug[] = array(
1565 + 'sql' => $sql,
1566 + 'result' => $result,
1567 + );
1568 + }
1569 + if ( '' !== $wpdb->last_error ) {
1570 + $errors[] = array(
1571 + 'sql' => $sql,
1572 + 'error' => $wpdb->last_error,
1573 + );
1574 + }
1267 1575 }
1268 - $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\"%'");
1576 + $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\"%'");
1269 1577 foreach ( $sql_content as $sql ) {
1270 - $renamed += $wpdb->query( $wpdb->prepare( $sql, array($dbs_source, $dbs_destination, $dbs_source) ) );
1578 + // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared -- plugin table
1579 + $result = $wpdb->query( $wpdb->prepare( $sql, array($dbs_source, $dbs_destination, $dbs_source) ) );
1580 + $renamed += $result;
1581 + if ( $debug_mode ) {
1582 + $debug[] = array(
1583 + 'sql' => $sql,
1584 + 'result' => $result,
1585 + );
1586 + }
1587 + if ( '' !== $wpdb->last_error ) {
1588 + $errors[] = array(
1589 + 'sql' => $sql,
1590 + 'error' => $wpdb->last_error,
1591 + );
1592 + }
1271 1593 }
1272 - return $this->WPDA_Rest_Response( sprintf( __( 'Successfully renamed %s database occurrences', 'wp-data-access' ), $renamed ) );
1594 + $context = array();
1595 + if ( $debug_mode ) {
1596 + $context['debug'] = $debug;
1597 + }
1598 + if ( 0 < count( $errors ) ) {
1599 + $context['errors'] = $errors;
1600 + return new \WP_Error('error', 'Failed renaming database', array(
1601 + 'status' => 401,
1602 + 'context' => $context,
1603 + ));
1604 + }
1605 + return $this->WPDA_Rest_Response( sprintf(
1606 + /* translators: %s = number of database substitutions */
1607 + __( 'Successfully renamed %s database occurrences', 'wp-data-access' ),
1608 + $renamed
1609 + ), null, $context );
1273 1610 }
1274 1611
1275 1612 public function app_chart_data( $request ) {
1276 1613 $app_id = $request->get_param( 'app_id' );
1614 + $search_custom = $request->get_param( 'search_custom' );
1615 + $shortcode_params = $request->get_param( 'shortcode_params' );
1277 1616 if ( !$this->main_app_access( $app_id, $msg ) ) {
1278 1617 if ( 'rest_cookie_invalid_nonce' === $msg ) {
1279 1618 return $this->invalid_nonce();
1280 1619 }
@@ -1286,8 +1625,15 @@
1286 1625 $app_container = WPDA_App_Container_Model::select( $app_id, 0 );
1287 1626 if ( 1 === count( $app_container ) && null !== $app_container[0]['cnt_query'] && '' !== trim( (string) $app_container[0]['cnt_query'] ) ) {
1288 1627 $dbs = $app_container[0]['cnt_dbs'];
1289 1628 $query = $app_container[0]['cnt_query'];
1629 + // Process shortcode and url parameters
1630 + $query = $this->process_params(
1631 + $query,
1632 + $search_custom,
1633 + null,
1634 + $shortcode_params
1635 + );
1290 1636 $wpdadb = WPDADB::get_db_connection( $dbs );
1291 1637 if ( null === $wpdadb ) {
1292 1638 // Error connecting.
1293 1639 return new \WP_Error('error', "Error connecting to database {$dbs}", array(
@@ -1295,14 +1641,23 @@
1295 1641 ));
1296 1642 }
1297 1643 $suppress = $wpdadb->suppress_errors( true );
1298 1644 $chart_data = $wpdadb->get_results( $query, 'ARRAY_A' );
1645 + // Add an * before each label to support numeric labels
1646 + $chart_data_converted = array();
1647 + foreach ( $chart_data as $data ) {
1648 + $row = array();
1649 + foreach ( $data as $key => $value ) {
1650 + $row['*' . $key] = $value;
1651 + }
1652 + $chart_data_converted[] = $row;
1653 + }
1299 1654 $wpdadb->get_results( "create temporary table `wpda_chart_data_types` as {$query}", 'ARRAY_A' );
1300 1655 $explain = $wpdadb->get_results( "desc `wpda_chart_data_types`", 'ARRAY_A' );
1301 1656 $wpdadb->get_results( "drop temporary table `wpda_chart_data_types`", 'ARRAY_A' );
1302 1657 $wpdadb->suppress_errors( $suppress );
1303 1658 return array(
1304 - 'data' => $chart_data,
1659 + 'data' => $chart_data_converted,
1305 1660 'explain' => $explain,
1306 1661 );
1307 1662 } else {
1308 1663 return new \WP_Error('error', $msg, array(
@@ -1348,8 +1703,9 @@
1348 1703 $cnt_id = $request->get_param( 'cnt_id' );
1349 1704 $target = $request->get_param( 'target' );
1350 1705 $settings = $request->get_param( 'settings' );
1351 1706 $chart = $request->get_param( 'chart' );
1707 + $map = $request->get_param( 'map' );
1352 1708 $theme = $request->get_param( 'theme' );
1353 1709 return $this->do_app_settings(
1354 1710 $app_id,
1355 1711 $cnt_id,
@@ -1355,8 +1711,9 @@
1355 1711 $cnt_id,
1356 1712 $target,
1357 1713 $settings,
1358 1714 $chart,
1715 + $map,
1359 1716 $theme
1360 1717 );
1361 1718 }
1362 1719
@@ -1520,11 +1877,12 @@
1520 1877 $cnt_id,
1521 1878 $target,
1522 1879 $settings,
1523 1880 $chart,
1881 + $map,
1524 1882 $theme
1525 1883 ) {
1526 - if ( 1 > $app_id || 1 > $cnt_id || 'table' !== $target && 'form' !== $target && 'rform' !== $target && 'theme' !== $target && 'chart' !== $target ) {
1884 + if ( 1 > $app_id || 1 > $cnt_id || 'table' !== $target && 'form' !== $target && 'rform' !== $target && 'theme' !== $target && 'chart' !== $target && 'map' !== $target && 'dashboard' !== $target ) {
1527 1885 return $this->bad_request();
1528 1886 }
1529 1887 if ( null === $settings || '' === $settings ) {
1530 1888 // Perform reset
@@ -1552,8 +1910,16 @@
1552 1910 'status' => 403,
1553 1911 ));
1554 1912 }
1555 1913 break;
1914 + case 'map':
1915 + $error_msg = WPDA_App_Container_Model::update_map_settings( $cnt_id, null );
1916 + if ( '' !== $error_msg ) {
1917 + return new \WP_Error('error', $error_msg, array(
1918 + 'status' => 403,
1919 + ));
1920 + }
1921 + break;
1556 1922 case 'theme':
1557 1923 $error_msg = WPDA_App_Model::update_theme( $app_id, null );
1558 1924 if ( '' !== $error_msg ) {
1559 1925 return new \WP_Error('error', $error_msg, array(
@@ -1560,8 +1926,16 @@
1560 1926 'status' => 403,
1561 1927 ));
1562 1928 }
1563 1929 break;
1930 + case 'dashboard':
1931 + $error_msg = WPDA_App_Container_Model::update_dashboard_settings( $app_id, null );
1932 + if ( '' !== $error_msg ) {
1933 + return new \WP_Error('error', $error_msg, array(
1934 + 'status' => 403,
1935 + ));
1936 + }
1937 + break;
1564 1938 default:
1565 1939 return $this->bad_request();
1566 1940 }
1567 1941 return $this->WPDA_Rest_Response( __( 'Reset was successful', 'wp-data-access' ) );
@@ -1580,8 +1954,15 @@
1580 1954 return new \WP_Error('error', $error_msg, array(
1581 1955 'status' => 403,
1582 1956 ));
1583 1957 }
1958 + // Update map settings
1959 + $error_msg = WPDA_App_Container_Model::update_map_settings( $cnt_id, $map );
1960 + if ( '' !== $error_msg ) {
1961 + return new \WP_Error('error', $error_msg, array(
1962 + 'status' => 403,
1963 + ));
1964 + }
1584 1965 } else {
1585 1966 if ( 'rform' === $target ) {
1586 1967 // Update rform settings
1587 1968 $error_msg = WPDA_App_Container_Model::update_rform_settings( $cnt_id, $settings );
@@ -1599,16 +1980,35 @@
1599 1980 'status' => 403,
1600 1981 ));
1601 1982 }
1602 1983 } else {
1603 - if ( 'form' === $target ) {
1604 - // Update form settings
1605 - $error_msg = WPDA_App_Container_Model::update_form_settings( $cnt_id, $settings );
1984 + if ( 'map' === $target ) {
1985 + // Update chart settings
1986 + $error_msg = WPDA_App_Container_Model::update_map_settings( $cnt_id, $settings );
1606 1987 if ( '' !== $error_msg ) {
1607 1988 return new \WP_Error('error', $error_msg, array(
1608 1989 'status' => 403,
1609 1990 ));
1610 1991 }
1992 + } else {
1993 + if ( 'form' === $target ) {
1994 + // Update form settings
1995 + $error_msg = WPDA_App_Container_Model::update_form_settings( $cnt_id, $settings );
1996 + if ( '' !== $error_msg ) {
1997 + return new \WP_Error('error', $error_msg, array(
1998 + 'status' => 403,
1999 + ));
2000 + }
2001 + } else {
2002 + if ( 'dashboard' === $target ) {
2003 + $error_msg = WPDA_App_Container_Model::update_dashboard_settings( $cnt_id, $settings );
2004 + if ( '' !== $error_msg ) {
2005 + return new \WP_Error('error', $error_msg, array(
2006 + 'status' => 403,
2007 + ));
2008 + }
2009 + }
2010 + }
1611 2011 }
1612 2012 }
1613 2013 }
1614 2014 }
@@ -1729,9 +2129,9 @@
1729 2129 private function get_relation_columns( $container ) {
1730 2130 return null;
1731 2131 }
1732 2132
1733 - private function reorder_details( $cnt_id_from, $cnt_id_to ) {
2133 + private function reorder_details( $app_id, $cnt_id_from, $cnt_id_to ) {
1734 2134 return $this->bad_request();
1735 2135 }
1736 2136
1737 2137 private function get_app_apps_meta( $app, $apps ) {
@@ -1738,8 +2138,9 @@
1738 2138 $app_id_details = array_map( function ( $e ) {
1739 2139 if ( isset( $e['app_id_detail'] ) ) {
1740 2140 return $e['app_id_detail'];
1741 2141 }
2142 + return null;
1742 2143 }, $apps );
1743 2144 $app_titles = array();
1744 2145 foreach ( $app_id_details as $app_id_detail ) {
1745 2146 $app_detail = WPDA_App_Model::get_by_id( $app_id_detail );
@@ -1774,18 +2175,46 @@
1774 2175 }
1775 2176 $settings->env = $this->get_env();
1776 2177 global $wpdb;
1777 2178 $settings->wp = [
1778 - 'roles' => $this->get_wp_roles(),
1779 - 'users' => $this->get_wp_users(),
1780 - 'home' => admin_url( 'admin.php' ),
1781 - 'tables' => array_values( $wpdb->tables() ),
1782 - 'date_format' => get_option( 'date_format' ),
1783 - 'time_format' => get_option( 'time_format' ),
2179 + 'roles' => $this->get_wp_roles(),
2180 + 'users' => $this->get_wp_users(),
2181 + 'home' => admin_url( 'admin.php' ),
2182 + 'siteurl' => site_url(),
2183 + 'tables' => array_values( $wpdb->tables() ),
2184 + 'date_format' => get_option( 'date_format' ),
2185 + 'time_format' => get_option( 'time_format' ),
2186 + 'scroll_offset' => WPDA::get_option( WPDA::OPTION_APPS_SCROLL_OFFSET ),
2187 + 'upload' => @ini_get( 'upload_max_filesize' ),
2188 + 'uploadBytes' => $this->uploadToBytes( @ini_get( 'upload_max_filesize' ) ),
2189 + 'locale' => get_locale(),
1784 2190 ];
1785 2191 return $settings;
1786 2192 }
1787 2193
2194 + /**
2195 + * Convert a PHP shorthand notation (e.g., "2M", "512K", "1G") to bytes.
2196 + *
2197 + * @param string $val The shorthand size string.
2198 + *
2199 + * @return int The size in bytes.
2200 + */
2201 + private function uploadToBytes( $val ) : int {
2202 + $val = trim( $val );
2203 + $lastChar = strtolower( $val[strlen( $val ) - 1] );
2204 + $num = (float) $val;
2205 + switch ( $lastChar ) {
2206 + case 'g':
2207 + $num *= 1024;
2208 + case 'm':
2209 + $num *= 1024;
2210 + case 'k':
2211 + $num *= 1024;
2212 + break;
2213 + }
2214 + return (int) round( $num );
2215 + }
2216 +
1788 2217 private function get_app_container_meta( $app_id, $container, $rel_tab = false ) {
1789 2218 $app = WPDA_App_Model::get_by_id( $app_id );
1790 2219 if ( false === $app ) {
1791 2220 return $this->bad_request();
@@ -1816,10 +2245,18 @@
1816 2245 'update' => array(),
1817 2246 'delete' => array(),
1818 2247 );
1819 2248 $cls = WPDA_List_Columns_Cache::get_list_columns( $dbs, $tbl );
2249 + $columns = $cls->get_table_columns();
2250 + $columns_sorted = array();
2251 + foreach ( $columns as $column ) {
2252 + if ( isset( $column['column_name'] ) ) {
2253 + $columns_sorted[$column['column_name']] = $column;
2254 + }
2255 + }
1820 2256 $media = $this->get_media( $dbs, $tbl, $cls->get_table_columns() );
1821 - $response['columns'] = $cls->get_table_columns();
2257 + $response['columns'] = $columns;
2258 + $response['columns_sorted'] = $columns_sorted;
1822 2259 $response['table_labels'] = $cls->get_table_header_labels();
1823 2260 $response['form_labels'] = $cls->get_table_column_headers();
1824 2261 $response['primary_key'] = $cls->get_table_primary_key();
1825 2262 $response['access'] = $access;
@@ -1854,8 +2291,17 @@
1854 2291 return $this->get_app_container_meta( $app_id, $container );
1855 2292 }
1856 2293 }
1857 2294
2295 + private function escapeUnicodeForExport( $str ) {
2296 + if ( empty( $str ) ) {
2297 + return $str;
2298 + }
2299 + return preg_replace_callback( '/\\\\(u[0-9a-fA-F]{4})/', function ( $matches ) {
2300 + return '\\\\' . $matches[1];
2301 + }, $str );
2302 + }
2303 +
1858 2304 private function do_app_export_app( $app_id, $main_app_id ) {
1859 2305 global $wpdb;
1860 2306 $quotes = function ( $value ) {
1861 2307 return str_replace( array(
@@ -1860,24 +2306,31 @@
1860 2306 $quotes = function ( $value ) {
1861 2307 return str_replace( array(
1862 2308 "'",
1863 2309 '\\"',
2310 + "\\\\t",
1864 2311 "\\t",
2312 + "\\\\n",
1865 2313 "\\n",
1866 2314 "\\r\\n",
1867 - "\\r"
2315 + "\\r",
2316 + "\\d"
1868 2317 ), array(
1869 2318 "''",
1870 2319 '\\\\"',
2320 + "\\\\\\t",
1871 2321 "\\\\t",
2322 + "\\\\\\n",
1872 2323 "\\\\n",
1873 2324 "\\\\r\\\\n",
1874 - "\\\\r"
2325 + "\\\\r",
2326 + "\\\\\\\\\\d"
1875 2327 ), $value );
1876 2328 };
1877 2329 $app = WPDA_App_Model::get_by_id( $app_id );
1878 2330 $app_settings = ( null === $app[0]['app_settings'] ? 'null' : "{$quotes( $app[0]['app_settings'] )}" );
1879 2331 $app_theme = ( null === $app[0]['app_theme'] ? 'null' : "{$quotes( $app[0]['app_theme'] )}" );
2332 + // phpcs:ignore PluginCheck.CodeAnalysis.Heredoc.NotAllowed
1880 2333 $app_sql = <<<SQL
1881 2334 # Import app
1882 2335 insert into `{wp_prefix}wpda_app`
1883 2336 \t(`app_name`
@@ -1907,15 +2360,23 @@
1907 2360 SQL;
1908 2361 $containers = WPDA_App_Container_Model::select_all( $app_id );
1909 2362 $containers_sql = '';
1910 2363 foreach ( $containers as $container ) {
1911 - $cnt_table = ( null === $container['cnt_table'] ? 'null' : "{$quotes( $container['cnt_table'] )}" );
1912 - $cnt_form = ( null === $container['cnt_form'] ? 'null' : "{$quotes( $container['cnt_form'] )}" );
1913 - $cnt_relation = ( null === $container['cnt_relation'] ? 'null' : "{$quotes( $container['cnt_relation'] )}" );
2364 + $cnt_table = ( null === $container['cnt_table'] ? 'null' : "'{$quotes( $container['cnt_table'] )}'" );
2365 + $cnt_form = ( null === $container['cnt_form'] ? 'null' : "'{$quotes( $container['cnt_form'] )}'" );
2366 + $cnt_relation = ( null === $container['cnt_relation'] ? 'null' : "'{$quotes( $container['cnt_relation'] )}'" );
2367 + $cnt_rform = ( null === $container['cnt_rform'] ? 'null' : "'{$quotes( $container['cnt_rform'] )}'" );
2368 + $cnt_chart = ( null === $container['cnt_chart'] ? 'null' : "'{$quotes( $container['cnt_chart'] )}'" );
2369 + $cnt_map = ( null === $container['cnt_map'] ? 'null' : "'{$quotes( $container['cnt_map'] )}'" );
2370 + $cnt_query = ( null === $container['cnt_query'] ? 'null' : "'{$quotes( $container['cnt_query'] )}'" );
1914 2371 // Replace default WordPress database with conversion string
1915 2372 $cnt_dbs = ( $wpdb->dbname === $container['cnt_dbs'] ? '{wp_schema}' : "{$quotes( $container['cnt_dbs'] )}" );
1916 2373 $cnt_table = str_replace( "\"dbs\":\"{$wpdb->dbname}\"", "\"dbs\":\"{wp_schema}\"", $cnt_table );
1917 2374 $cnt_form = str_replace( "\"dbs\":\"{$wpdb->dbname}\"", "\"dbs\":\"{wp_schema}\"", $cnt_form );
2375 + // Replace Unicode characters
2376 + $cnt_table = $this->escapeUnicodeForExport( $cnt_table );
2377 + $cnt_form = $this->escapeUnicodeForExport( $cnt_form );
2378 + // phpcs:ignore PluginCheck.CodeAnalysis.Heredoc.NotAllowed
1918 2379 $containers_sql .= <<<SQL
1919 2380 # Import app container
1920 2381 insert into `{wp_prefix}wpda_app_container`
1921 2382 \t(`cnt_dbs`
@@ -1926,8 +2387,12 @@
1926 2387 \t,`cnt_seq_nr`
1927 2388 \t,`cnt_table`
1928 2389 \t,`cnt_form`
1929 2390 \t,`cnt_relation`
2391 + ,`cnt_rform`
2392 + ,`cnt_chart`
2393 + ,`cnt_map`
2394 + ,`cnt_query`
1930 2395 \t)
1931 2396 values
1932 2397 \t('{$cnt_dbs}'
1933 2398 \t,'{$quotes( $container['cnt_tbl'] )}'
@@ -1934,11 +2399,15 @@
1934 2399 \t,'{$quotes( $container['cnt_cls'] )}'
1935 2400 \t,'{$quotes( $container['cnt_title'] )}'
1936 2401 \t,@APP_ID
1937 2402 \t,{$container['cnt_seq_nr']}
1938 -\t,'{$cnt_table}'
1939 -\t,'{$cnt_form}'
1940 -\t,'{$cnt_relation}'
2403 +\t,{$cnt_table}
2404 +\t,{$cnt_form}
2405 +\t,{$cnt_relation}
2406 +\t,{$cnt_rform}
2407 +\t,{$cnt_chart}
2408 +\t,{$cnt_map}
2409 +\t,{$cnt_query}
1941 2410 \t);
1942 2411
1943 2412 insert into `wpda_transfer_containers_{$main_app_id}`
1944 2413 values
@@ -1949,8 +2418,9 @@
1949 2418
1950 2419 SQL;
1951 2420 }
1952 2421 // Post update: update master container ids
2422 + // phpcs:ignore PluginCheck.CodeAnalysis.Heredoc.NotAllowed
1953 2423 $containers_sql .= <<<SQL
1954 2424 # Update app master container IDs
1955 2425 update `{wp_prefix}wpda_app_container` as a
1956 2426 set a.`cnt_relation` =
@@ -1973,8 +2443,9 @@
1973 2443 foreach ( $apps as $app ) {
1974 2444 $apps_sql .= $this->do_app_export_app( $app['app_id_detail'], $main_app_id );
1975 2445 }
1976 2446 foreach ( $apps as $app ) {
2447 + // phpcs:ignore PluginCheck.CodeAnalysis.Heredoc.NotAllowed
1977 2448 $apps_sql .= <<<SQL
1978 2449 # Import app relationships
1979 2450 insert into `{wp_prefix}wpda_app_apps`
1980 2451 \t(`app_id`
@@ -1995,8 +2466,9 @@
1995 2466
1996 2467 private function do_app_export( $app_id ) {
1997 2468 global $wpdb;
1998 2469 $sql = '';
2470 + // phpcs:ignore PluginCheck.CodeAnalysis.Heredoc.NotAllowed
1999 2471 $begin_sql = <<<SQL
2000 2472 /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
2001 2473 /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
2002 2474 /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
@@ -2017,8 +2489,9 @@
2017 2489
2018 2490
2019 2491 SQL;
2020 2492 $sql .= $this->do_app_export_app( $app_id, $app_id );
2493 + // phpcs:ignore PluginCheck.CodeAnalysis.Heredoc.NotAllowed
2021 2494 $end_sql = <<<SQL
2022 2495 # Drop temporary table
2023 2496 DROP TABLE `wpda_transfer_containers_{$app_id}`;
2024 2497 DROP TABLE `wpda_transfer_apps_{$app_id}`;
@@ -2111,9 +2584,9 @@
2111 2584 }
2112 2585 return $this->WPDA_Rest_Response( __( 'Changes successfully saved', 'wp-data-access' ) );
2113 2586 }
2114 2587
2115 - private function main_app_access( $app_id, &$msg = '' ) {
2588 + private function main_app_access( $app_id, &$msg = '', &$app = null ) {
2116 2589 // Get app info
2117 2590 $app = WPDA_App_Model::get_by_id( $app_id );
2118 2591 if ( false === $app ) {
2119 2592 // App not found
@@ -2156,9 +2629,9 @@
2156 2629 if ( !$this->main_app_access( $app_id, $msg ) ) {
2157 2630 return false;
2158 2631 }
2159 2632 // Get container
2160 - $container = WPDA_App_Container_Model::get_container( $cnt_id );
2633 + $container = WPDA_App_Container_Model::get_container( $app_id, $cnt_id );
2161 2634 if ( !is_array( $container ) || 0 === count( $container ) ) {
2162 2635 // Container not found
2163 2636 $msg = __( 'Bad request', 'wp-data-access' );
2164 2637 return false;
@@ -2165,12 +2638,14 @@
2165 2638 }
2166 2639 if ( 'select' !== $action ) {
2167 2640 $cnt_table = json_decode( (string) $container[0]['cnt_table'], true );
2168 2641 if ( !isset( $cnt_table['table']['transactions'][$action] ) || false === $cnt_table['table']['transactions'][$action] ) {
2169 - $cnt_relation = json_decode( (string) $container[0]['cnt_relation'], true );
2170 - if ( !(isset( $cnt_relation['cnt_id_master'] ) && $this->check_master_container_access( $cnt_relation['cnt_id_master'], $action )) ) {
2171 - $msg = __( 'Unauthorized', 'wp-data-access' );
2172 - return false;
2642 + if ( !isset( $cnt_table['table']['bulkActions'][$action] ) || false === $cnt_table['table']['bulkActions'][$action] ) {
2643 + $cnt_relation = json_decode( (string) $container[0]['cnt_relation'], true );
2644 + if ( !(isset( $cnt_relation['cnt_id_master'] ) && $this->check_master_container_access( $app_id, $cnt_relation['cnt_id_master'], $action )) ) {
2645 + $msg = __( 'Unauthorized', 'wp-data-access' );
2646 + return false;
2647 + }
2173 2648 }
2174 2649 }
2175 2650 }
2176 2651 // Return database name, table name and columns
@@ -2183,10 +2658,10 @@
2183 2658 );
2184 2659 return true;
2185 2660 }
2186 2661
2187 - private function check_master_container_access( $cnt_id, $action ) {
2188 - $container = WPDA_App_Container_Model::get_container( $cnt_id );
2662 + private function check_master_container_access( $app_id, $cnt_id, $action ) {
2663 + $container = WPDA_App_Container_Model::get_container( $app_id, $cnt_id );
2189 2664 if ( !is_array( $container ) || 0 === count( $container ) ) {
2190 2665 // Container not found
2191 2666 return false;
2192 2667 }
@@ -2192,9 +2667,9 @@
2192 2667 }
2193 2668 $cnt_table = json_decode( (string) $container[0]['cnt_table'], true );
2194 2669 if ( !isset( $cnt_table['table']['transactions'][$action] ) || false === $cnt_table['table']['transactions'][$action] ) {
2195 2670 $cnt_relation = json_decode( (string) $container[0]['cnt_relation'], true );
2196 - if ( !(isset( $cnt_relation['cnt_id_master'] ) && $this->check_master_container_access( $cnt_relation['cnt_id_master'], $action )) ) {
2671 + if ( !(isset( $cnt_relation['cnt_id_master'] ) && $this->check_master_container_access( $app_id, $cnt_relation['cnt_id_master'], $action )) ) {
2197 2672 return false;
2198 2673 }
2199 2674 }
2200 2675 return true;
@@ -2203,18 +2678,21 @@
2203 2678 private function process_params(
2204 2679 $where,
2205 2680 $search_custom,
2206 2681 $search_params,
2207 - $shortcode_params
2682 + $shortcode_params,
2683 + $dynamic_params = array()
2208 2684 ) {
2209 2685 // Process $search_custom > URL parameters
2210 2686 global $wpdb;
2687 + $replacements = array();
2688 + $nonce = bin2hex( random_bytes( 8 ) );
2211 2689 foreach ( self::METHODS as $method ) {
2212 2690 $offset = 0;
2213 2691 $search = $method . '[';
2692 + // Get filter
2214 2693 while ( ($pos_start = stripos( $where, $search, $offset )) !== false ) {
2215 2694 if ( ($pos_end = stripos( $where, ']', $pos_start )) !== false ) {
2216 - // Get filter
2217 2695 $filter = substr( $where, $pos_start, $pos_end - $pos_start + 1 );
2218 2696 // Get name
2219 2697 $arg_name = substr( $where, $pos_start + strlen( $search ), $pos_end - $pos_start - strlen( $search ) );
2220 2698 // Remove quotes from name
@@ -2220,44 +2698,36 @@
2220 2698 // Remove quotes from name
2221 2699 if ( substr( $arg_name, 0, 1 ) === "'" && substr( $arg_name, -1 ) === "'" ) {
2222 2700 $arg_name = substr( $arg_name, 1, -1 );
2223 2701 }
2224 - // Remove double quotes from name
2702 + // Remove quotes from name
2225 2703 if ( substr( $arg_name, 0, 1 ) === '"' && substr( $arg_name, -1 ) === '"' ) {
2226 2704 $arg_name = substr( $arg_name, 1, -1 );
2227 2705 }
2706 + $arg_value = null;
2228 2707 // Handle GET args
2229 - if ( $method === self::METHODS[0] || $method === self::METHODS[2] ) {
2708 + if ( $method === self::METHODS[0] && isset( $search_custom['get'][$arg_name] ) ) {
2709 + $arg_value = sanitize_text_field( wp_unslash( $search_custom['get'][$arg_name] ) );
2710 + } elseif ( $method === self::METHODS[1] && isset( $search_custom['post'][$arg_name] ) ) {
2711 + $arg_value = sanitize_text_field( wp_unslash( $search_custom['post'][$arg_name] ) );
2712 + } elseif ( $method === self::METHODS[2] ) {
2230 2713 if ( isset( $search_custom['get'][$arg_name] ) ) {
2231 2714 $arg_value = sanitize_text_field( wp_unslash( $search_custom['get'][$arg_name] ) );
2232 - $where = $wpdb->prepare( substr_replace(
2233 - $where,
2234 - '%s',
2235 - $pos_start,
2236 - $pos_end - $pos_start + 1
2237 - ), $arg_value );
2238 - } else {
2239 - if ( $method === self::METHODS[0] ) {
2240 - $where = str_replace( $filter, 'null', $where );
2241 - }
2715 + } elseif ( isset( $search_custom['post'][$arg_name] ) ) {
2716 + $arg_value = sanitize_text_field( wp_unslash( $search_custom['post'][$arg_name] ) );
2242 2717 }
2243 2718 }
2244 2719 // Handle POST args
2245 - if ( $method === self::METHODS[1] || $method === self::METHODS[2] ) {
2246 - if ( isset( $search_custom['post'][$arg_name] ) ) {
2247 - $arg_value = sanitize_text_field( wp_unslash( $search_custom['post'][$arg_name] ) );
2248 - $where = $wpdb->prepare( substr_replace(
2249 - $where,
2250 - '%s',
2251 - $pos_start,
2252 - $pos_end - $pos_start + 1
2253 - ), $arg_value );
2254 - } else {
2255 - $where = str_replace( $filter, 'null', $where );
2256 - }
2720 + $placeholder = '###URL_PLACEHOLDER_' . $nonce . '_' . md5( $filter ) . '###';
2721 + $where = str_replace( $filter, $placeholder, $where );
2722 + // Handle REQUEST args
2723 + if ( $arg_value !== null ) {
2724 + $replacements[$placeholder] = $wpdb->prepare( '%s', $arg_value );
2725 + } else {
2726 + $replacements[$placeholder] = 'null';
2257 2727 }
2258 2728 }
2259 - $offset = $pos_start + 6;
2729 + $offset = $pos_start + 1;
2260 2730 if ( $offset > strlen( $where ) ) {
2261 2731 $offset = strlen( $where ) - 1;
2262 2732 }
2263 2733 }
@@ -2266,21 +2736,16 @@
2266 2736 if ( is_array( $search_params ) && 1 === count( $search_params ) ) {
2267 2737 $filter_field_name = $this->sanitize_db_identifier( array_keys( $search_params )[0] );
2268 2738 $filter_field_value = sanitize_text_field( $search_params[$filter_field_name] );
2269 2739 $filter_field_name_array = array_map( 'trim', explode( ',', $filter_field_name ) );
2270 - //phpcs:ignore - 8.1 proof
2271 2740 $filter_field_value_array = array_map( 'trim', explode( ',', $filter_field_value ) );
2272 - //phpcs:ignore - 8.1 proof
2273 2741 if ( count( $filter_field_name_array ) === count( $filter_field_value_array ) ) {
2274 - //phpcs:ignore - 8.1 proof
2275 2742 // Add filter to where clause.
2276 2743 for ($i = 0; $i < count( $filter_field_name_array ); $i++) {
2277 - // phpcs:ignore Generic.CodeAnalysis.ForLoopWithTestFunctionCall, Squiz.PHP.DisallowSizeFunctionsInLoops
2278 - $where .= (( '' === $where ? '' : ' and ' )) . $wpdb->prepare(
2279 - ' `%1s` like %s ',
2280 - // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders
2281 - array($filter_field_name_array[$i], $filter_field_value_array[$i])
2282 - );
2744 + $where .= ( '' === $where ? '' : ' and ' );
2745 + $placeholder = '###SHORTCODE_PARAM_' . $nonce . '_' . md5( $filter_field_name_array[$i] ) . '###';
2746 + $where .= ' `' . $filter_field_name_array[$i] . '` like ' . $placeholder;
2747 + $replacements[$placeholder] = $wpdb->prepare( '%s', $filter_field_value_array[$i] );
2283 2748 }
2284 2749 }
2285 2750 }
2286 2751 // Substitute all shortcode parameters
@@ -2287,13 +2752,11 @@
2287 2752 if ( is_array( $shortcode_params ) ) {
2288 2753 foreach ( $shortcode_params as $column_name => $column_value ) {
2289 2754 $occurences = substr_count( strtolower( $where ), strtolower( "shortcodeParam['{$column_name}']" ) );
2290 2755 if ( 0 < $occurences ) {
2291 - $column_values = array();
2292 - for ($i = 0; $i < $occurences; $i++) {
2293 - $column_values[] = sanitize_text_field( $column_value );
2294 - }
2295 - $where = $wpdb->prepare( str_ireplace( "shortcodeParam['{$column_name}']", '%s', $where ), $column_values );
2756 + $placeholder = '###SHORTCODE_PARAM_' . $nonce . '_' . md5( $column_name ) . '###';
2757 + $where = str_ireplace( "shortcodeParam['{$column_name}']", $placeholder, $where );
2758 + $replacements[$placeholder] = $wpdb->prepare( '%s', sanitize_text_field( $column_value ) );
2296 2759 }
2297 2760 }
2298 2761 }
2299 2762 // Substitute all unused shortcode parameter calls with null
@@ -2308,8 +2771,18 @@
2308 2771 if ( $offset > strlen( $where ) ) {
2309 2772 $offset = strlen( $where ) - 1;
2310 2773 }
2311 2774 }
2312 - return $where;
2775 + // Substitute all dynamic parameters
2776 + if ( is_array( $dynamic_params ) && 0 < count( $dynamic_params ) ) {
2777 + foreach ( $dynamic_params as $column_name => $column_value ) {
2778 + if ( stripos( $where, "{:{$column_name}}" ) !== false ) {
2779 + $placeholder = '###DYNAMIC_PARAM_' . $nonce . '_' . md5( $column_name ) . '###';
2780 + $where = str_ireplace( "{:{$column_name}}", $placeholder, $where );
2781 + $replacements[$placeholder] = $wpdb->prepare( '%s', $column_value );
2782 + }
2783 + }
2784 + }
2785 + return strtr( $where, $replacements );
2313 2786 }
2314 2787
2315 2788 }