| @@ -162,10 +162,12 @@ | ||
| 162 | 162 | 'app_query' => array( |
| 163 | 163 | 'required' => false, |
| 164 | 164 | 'type' => 'string', |
| 165 | 165 | 'description' => __( 'Custom query', 'wp-data-access' ), |
| 166 | - 'sanitize_callback' => 'sanitize_textarea_field', | |
| 167 | - 'validate_callback' => 'rest_validate_request_arg', | |
| 166 | + 'sanitize_callback' => function ( $param ) { | |
| 167 | + return html_entity_decode( wp_unslash( $param ), ENT_QUOTES ); | |
| 168 | + // Preserve SQL operators | |
| 169 | + }, | |
| 168 | 170 | ), |
| 169 | 171 | 'col' => array( |
| 170 | 172 | 'required' => true, |
| 171 | 173 | 'type' => 'string', |
| @@ -195,9 +197,9 @@ | ||
| 195 | 197 | 'page_index' => array( |
| 196 | 198 | 'required' => false, |
| 197 | 199 | 'type' => 'integer', |
| 198 | 200 | 'description' => __( 'Page number', 'wp-data-access' ), |
| 199 | - 'default' => 1, | |
| 201 | + 'default' => 0, | |
| 200 | 202 | 'minimum' => 0, |
| 201 | 203 | 'sanitize_callback' => 'absint', |
| 202 | 204 | 'validate_callback' => 'rest_validate_request_arg', |
| 203 | 205 | ), |
| @@ -404,16 +406,25 @@ | ||
| 404 | 406 | 'validate_callback' => function ( $param ) { |
| 405 | 407 | return is_array( $param ); |
| 406 | 408 | }, |
| 407 | 409 | ), |
| 410 | + 'copy_key' => array( | |
| 411 | + 'required' => true, | |
| 412 | + 'type' => 'string', | |
| 413 | + 'description' => __( 'Internal copy action identifier', 'wp-data-access' ), | |
| 414 | + 'sanitize_callback' => 'sanitize_text_field', | |
| 415 | + 'validate_callback' => 'rest_validate_request_arg', | |
| 416 | + ), | |
| 408 | 417 | 'access' => array( |
| 409 | 418 | 'required' => true, |
| 410 | 419 | 'type' => 'string', |
| 411 | 420 | 'description' => __( 'Access (user | global) ', 'wp-data-access' ), |
| 412 | 421 | 'sanitize_callback' => function ( $param ) { |
| 413 | - return ( 'global' === $param ? 'global' : 'user' ); | |
| 422 | + return ( 'global' === strtolower( $param ) ? 'global' : 'user' ); | |
| 414 | 423 | }, |
| 415 | - 'validate_callback' => 'rest_validate_request_arg', | |
| 424 | + 'validate_callback' => function ( $param ) { | |
| 425 | + return 'global' === strtolower( $param ) || 'user' === strtolower( $param ); | |
| 426 | + }, | |
| 416 | 427 | ), |
| 417 | 428 | 'query' => array( |
| 418 | 429 | 'required' => true, |
| 419 | 430 | 'type' => 'string', |
| @@ -418,9 +429,10 @@ | ||
| 418 | 429 | 'required' => true, |
| 419 | 430 | 'type' => 'string', |
| 420 | 431 | 'description' => __( 'SQL query', 'wp-data-access' ), |
| 421 | 432 | 'sanitize_callback' => function ( $param ) { |
| 422 | - return wp_kses_post( $param ); | |
| 433 | + return html_entity_decode( wp_unslash( $param ), ENT_QUOTES ); | |
| 434 | + // Preserve SQL operators | |
| 423 | 435 | }, |
| 424 | 436 | ), |
| 425 | 437 | 'name' => array( |
| 426 | 438 | 'required' => true, |
| @@ -430,13 +442,41 @@ | ||
| 430 | 442 | 'validate_callback' => 'rest_validate_request_arg', |
| 431 | 443 | ), |
| 432 | 444 | 'vqb' => array( |
| 433 | 445 | 'required' => false, |
| 434 | - 'type' => 'boolean', | |
| 435 | - 'description' => __( 'Query uses Visual Query Builder', 'wp-data-access' ), | |
| 436 | - 'sanitize_callback' => 'sanitize_text_field', | |
| 446 | + 'type' => 'mixed', | |
| 447 | + 'description' => __( 'Visual Query Builder', 'wp-data-access' ), | |
| 448 | + 'sanitize_callback' => function ( $param ) { | |
| 449 | + return rest_sanitize_object( $param ); | |
| 450 | + }, | |
| 437 | 451 | 'validate_callback' => 'rest_validate_request_arg', |
| 438 | 452 | ), |
| 453 | + 'params' => array( | |
| 454 | + 'required' => false, | |
| 455 | + 'type' => 'array', | |
| 456 | + 'description' => __( 'Cron job parameters', 'wp-data-access' ), | |
| 457 | + 'sanitize_callback' => function ( $param ) { | |
| 458 | + $params = array(); | |
| 459 | + if ( is_array( $param ) ) { | |
| 460 | + foreach ( $param as $key => $value ) { | |
| 461 | + if ( 'params' === $key || 'notify' === $key ) { | |
| 462 | + // Sanitize custom parameters | |
| 463 | + $custom_params = array(); | |
| 464 | + foreach ( $value as $param_key => $param_value ) { | |
| 465 | + $custom_params[sanitize_text_field( $param_key )] = sanitize_text_field( $param_value ); | |
| 466 | + } | |
| 467 | + $params[sanitize_text_field( $key )] = $custom_params; | |
| 468 | + } else { | |
| 469 | + $params[sanitize_text_field( $key )] = sanitize_text_field( $value ); | |
| 470 | + } | |
| 471 | + } | |
| 472 | + } | |
| 473 | + return $params; | |
| 474 | + }, | |
| 475 | + 'validate_callback' => function ( $param ) { | |
| 476 | + return is_array( $param ); | |
| 477 | + }, | |
| 478 | + ), | |
| 439 | 479 | ); |
| 440 | 480 | } |
| 441 | 481 | |
| 442 | 482 | protected function get_param( $key, $description = null ) { |
| @@ -505,9 +545,9 @@ | ||
| 505 | 545 | return false; |
| 506 | 546 | } |
| 507 | 547 | |
| 508 | 548 | public static function sanitize_db_identifier( $param ) { |
| 509 | - if ( null === $param ) { | |
| 549 | + if ( !is_string( $param ) ) { | |
| 510 | 550 | return null; |
| 511 | 551 | } |
| 512 | 552 | // Preserve starting and trailing spaces |
| 513 | 553 | $spaces_before = strlen( $param ) - strlen( ltrim( $param ) ); |
| @@ -563,15 +603,16 @@ | ||
| 563 | 603 | return $users; |
| 564 | 604 | } |
| 565 | 605 | |
| 566 | 606 | protected function get_env() { |
| 567 | - return array( | |
| 568 | - 'ip' => $_SERVER['REMOTE_ADDR'], | |
| 607 | + $env = array( | |
| 608 | + 'ip' => ( isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : '' ), | |
| 569 | 609 | 'id' => WPDA::get_current_user_id(), |
| 570 | 610 | 'user' => WPDA::get_current_user_login(), |
| 571 | 611 | 'roles' => WPDA::get_current_user_roles(), |
| 572 | 612 | 'login' => 'anonymous' !== WPDA::get_current_user_login(), |
| 573 | 613 | ); |
| 614 | + return $env; | |
| 574 | 615 | } |
| 575 | 616 | |
| 576 | 617 | protected function get_table_info( $dbs, $tbl, $default_where = '' ) { |
| 577 | 618 | $wpdadb = WPDADB::get_db_connection( $dbs ); |
| @@ -581,11 +622,10 @@ | ||
| 581 | 622 | 'engine' => null, |
| 582 | 623 | 'count' => null, |
| 583 | 624 | ); |
| 584 | 625 | } |
| 585 | - $query = $wpdadb->prepare( "\n\t\t\t\t\tselect table_type,\n\t\t\t\t\t engine,\n\t\t\t\t\t table_rows\n\t\t\t\t\t from information_schema.tables\n\t\t\t\t\t where table_schema = %s\n\t\t\t\t\t and table_name = %s\n\t\t\t\t\t order by table_name\n\t\t\t\t", array($wpdadb->dbname, $tbl) ); | |
| 626 | + $query = $wpdadb->prepare( "\n\t\t\t\t\tselect table_type,\n\t\t\t\t\t engine,\n\t\t\t\t\t table_rows\n\t\t\t\t\t from information_schema.tables\n\t\t\t\t\t where table_schema = %s\n\t\t\t\t\t and table_name = %s\n\t\t\t\t", array($wpdadb->dbname, $tbl) ); | |
| 586 | 627 | $resultset = $wpdadb->get_results( $query, 'ARRAY_N' ); |
| 587 | - // phpcs:ignore Standard.Category.SniffName.ErrorCode | |
| 588 | 628 | if ( count( $resultset ) === 1 ) { |
| 589 | 629 | if ( null !== $resultset[0][2] ) { |
| 590 | 630 | return array( |
| 591 | 631 | 'type' => $resultset[0][0], |
| @@ -622,16 +662,22 @@ | ||
| 622 | 662 | return -1; |
| 623 | 663 | } |
| 624 | 664 | } |
| 625 | 665 | |
| 626 | - protected function get_media( $dbs, $tbl, $columns ) { | |
| 666 | + protected function get_media( | |
| 667 | + $dbs, | |
| 668 | + $tbl, | |
| 669 | + $columns, | |
| 670 | + $prefix = '' | |
| 671 | + ) { | |
| 627 | 672 | $media = array(); |
| 628 | 673 | $wp_media = array(); |
| 629 | 674 | foreach ( $columns as $column ) { |
| 630 | 675 | $media_type = WPDA_Media_Model::get_column_media( $tbl, $column['column_name'], $dbs ); |
| 676 | + $column_name = $prefix . $column['column_name']; | |
| 631 | 677 | switch ( $media_type ) { |
| 632 | 678 | case 'ImageURL': |
| 633 | - $media[$column['column_name']] = $media_type; | |
| 679 | + $media[$column_name] = $media_type; | |
| 634 | 680 | break; |
| 635 | 681 | case 'Hyperlink': |
| 636 | 682 | // Get table settings. |
| 637 | 683 | $table_settings_db = WPDA_Table_Settings_Model::query( $tbl, $dbs ); |
| @@ -641,20 +687,20 @@ | ||
| 641 | 687 | $table_settings = null; |
| 642 | 688 | } |
| 643 | 689 | // Check hyperlink format. |
| 644 | 690 | if ( isset( $table_settings['table_settings']['hyperlink_definition'] ) && 'text' === $table_settings['table_settings']['hyperlink_definition'] ) { |
| 645 | - $media[$column['column_name']] = 'HyperlinkURL'; | |
| 691 | + $media[$column_name] = 'HyperlinkURL'; | |
| 646 | 692 | } else { |
| 647 | - $media[$column['column_name']] = 'HyperlinkObject'; | |
| 693 | + $media[$column_name] = 'HyperlinkObject'; | |
| 648 | 694 | } |
| 649 | 695 | break; |
| 650 | 696 | default: |
| 651 | 697 | if ( false !== $media_type ) { |
| 652 | 698 | // Handle WordPress Media Library integration |
| 653 | - $media[$column['column_name']] = "WP-{$media_type}"; | |
| 699 | + $media[$column_name] = "WP-{$media_type}"; | |
| 654 | 700 | } |
| 655 | 701 | } |
| 656 | - $wp_media[$column['column_name']] = $media_type; | |
| 702 | + $wp_media[$column_name] = $media_type; | |
| 657 | 703 | } |
| 658 | 704 | return [ |
| 659 | 705 | 'media' => $media, |
| 660 | 706 | 'wp_media' => $wp_media, |