| @@ -38,8 +38,15 @@ | ||
| 38 | 38 | 'validate_callback' => function ( $param ) { |
| 39 | 39 | return $this->validate_db_identifier( $param ); |
| 40 | 40 | }, |
| 41 | 41 | ), |
| 42 | + 'client_side' => array( | |
| 43 | + 'required' => false, | |
| 44 | + 'type' => 'boolean', | |
| 45 | + 'description' => __( 'Server side processing', 'wp-data-access' ), | |
| 46 | + 'sanitize_callback' => 'sanitize_text_field', | |
| 47 | + 'validate_callback' => 'rest_validate_request_arg', | |
| 48 | + ), | |
| 42 | 49 | 'app_id' => array( |
| 43 | 50 | 'required' => true, |
| 44 | 51 | 'type' => 'integer', |
| 45 | 52 | 'description' => __( 'App ID', 'wp-data-access' ), |
| @@ -155,10 +162,12 @@ | ||
| 155 | 162 | 'app_query' => array( |
| 156 | 163 | 'required' => false, |
| 157 | 164 | 'type' => 'string', |
| 158 | 165 | 'description' => __( 'Custom query', 'wp-data-access' ), |
| 159 | - 'sanitize_callback' => 'sanitize_textarea_field', | |
| 160 | - '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 | + }, | |
| 161 | 170 | ), |
| 162 | 171 | 'col' => array( |
| 163 | 172 | 'required' => true, |
| 164 | 173 | 'type' => 'string', |
| @@ -188,9 +197,9 @@ | ||
| 188 | 197 | 'page_index' => array( |
| 189 | 198 | 'required' => false, |
| 190 | 199 | 'type' => 'integer', |
| 191 | 200 | 'description' => __( 'Page number', 'wp-data-access' ), |
| 192 | - 'default' => 1, | |
| 201 | + 'default' => 0, | |
| 193 | 202 | 'minimum' => 0, |
| 194 | 203 | 'sanitize_callback' => 'absint', |
| 195 | 204 | 'validate_callback' => 'rest_validate_request_arg', |
| 196 | 205 | ), |
| @@ -383,9 +392,9 @@ | ||
| 383 | 392 | 'sanitize_callback' => 'absint', |
| 384 | 393 | 'validate_callback' => 'rest_validate_request_arg', |
| 385 | 394 | ), |
| 386 | 395 | 'media' => array( |
| 387 | - 'required' => true, | |
| 396 | + 'required' => false, | |
| 388 | 397 | 'type' => 'mixed', |
| 389 | 398 | 'description' => __( 'Media columns', 'wp-data-access' ), |
| 390 | 399 | 'sanitize_callback' => function ( $param ) { |
| 391 | 400 | $media = array(); |
| @@ -397,8 +406,77 @@ | ||
| 397 | 406 | 'validate_callback' => function ( $param ) { |
| 398 | 407 | return is_array( $param ); |
| 399 | 408 | }, |
| 400 | 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 | + ), | |
| 417 | + 'access' => array( | |
| 418 | + 'required' => true, | |
| 419 | + 'type' => 'string', | |
| 420 | + 'description' => __( 'Access (user | global) ', 'wp-data-access' ), | |
| 421 | + 'sanitize_callback' => function ( $param ) { | |
| 422 | + return ( 'global' === strtolower( $param ) ? 'global' : 'user' ); | |
| 423 | + }, | |
| 424 | + 'validate_callback' => function ( $param ) { | |
| 425 | + return 'global' === strtolower( $param ) || 'user' === strtolower( $param ); | |
| 426 | + }, | |
| 427 | + ), | |
| 428 | + 'query' => array( | |
| 429 | + 'required' => true, | |
| 430 | + 'type' => 'string', | |
| 431 | + 'description' => __( 'SQL query', 'wp-data-access' ), | |
| 432 | + 'sanitize_callback' => function ( $param ) { | |
| 433 | + return html_entity_decode( wp_unslash( $param ), ENT_QUOTES ); | |
| 434 | + // Preserve SQL operators | |
| 435 | + }, | |
| 436 | + ), | |
| 437 | + 'name' => array( | |
| 438 | + 'required' => true, | |
| 439 | + 'type' => 'string', | |
| 440 | + 'description' => __( 'Query name', 'wp-data-access' ), | |
| 441 | + 'sanitize_callback' => 'sanitize_text_field', | |
| 442 | + 'validate_callback' => 'rest_validate_request_arg', | |
| 443 | + ), | |
| 444 | + 'vqb' => array( | |
| 445 | + 'required' => false, | |
| 446 | + 'type' => 'mixed', | |
| 447 | + 'description' => __( 'Visual Query Builder', 'wp-data-access' ), | |
| 448 | + 'sanitize_callback' => function ( $param ) { | |
| 449 | + return rest_sanitize_object( $param ); | |
| 450 | + }, | |
| 451 | + 'validate_callback' => 'rest_validate_request_arg', | |
| 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 | + ), | |
| 401 | 479 | ); |
| 402 | 480 | } |
| 403 | 481 | |
| 404 | 482 | protected function get_param( $key, $description = null ) { |
| @@ -430,10 +508,10 @@ | ||
| 430 | 508 | } |
| 431 | 509 | return WPDA_API_Core::$user_login; |
| 432 | 510 | } |
| 433 | 511 | |
| 434 | - protected function current_user_can_access( $admins_only = false ) { | |
| 435 | - return current_user_can( 'manage_options' ); | |
| 512 | + protected function current_user_can_access() { | |
| 513 | + return WPDA::current_user_is_admin(); | |
| 436 | 514 | } |
| 437 | 515 | |
| 438 | 516 | protected function unauthorized() { |
| 439 | 517 | return new \WP_Error('error', __( 'Unauthorized', 'wp-data-access' ), array( |
| @@ -440,9 +518,9 @@ | ||
| 440 | 518 | 'status' => 401, |
| 441 | 519 | )); |
| 442 | 520 | } |
| 443 | 521 | |
| 444 | - protected function current_user_token_valid( $request, $token_required = false ) { | |
| 522 | + protected function current_user_token_valid( $request ) { | |
| 445 | 523 | return wp_verify_nonce( $request->get_header( 'X-WP-Nonce' ), 'wp_rest' ); |
| 446 | 524 | } |
| 447 | 525 | |
| 448 | 526 | protected function invalid_nonce() { |
| @@ -467,9 +545,9 @@ | ||
| 467 | 545 | return false; |
| 468 | 546 | } |
| 469 | 547 | |
| 470 | 548 | public static function sanitize_db_identifier( $param ) { |
| 471 | - if ( null === $param ) { | |
| 549 | + if ( !is_string( $param ) ) { | |
| 472 | 550 | return null; |
| 473 | 551 | } |
| 474 | 552 | // Preserve starting and trailing spaces |
| 475 | 553 | $spaces_before = strlen( $param ) - strlen( ltrim( $param ) ); |
| @@ -525,15 +603,16 @@ | ||
| 525 | 603 | return $users; |
| 526 | 604 | } |
| 527 | 605 | |
| 528 | 606 | protected function get_env() { |
| 529 | - return array( | |
| 530 | - 'ip' => $_SERVER['REMOTE_ADDR'], | |
| 607 | + $env = array( | |
| 608 | + 'ip' => ( isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : '' ), | |
| 531 | 609 | 'id' => WPDA::get_current_user_id(), |
| 532 | 610 | 'user' => WPDA::get_current_user_login(), |
| 533 | 611 | 'roles' => WPDA::get_current_user_roles(), |
| 534 | 612 | 'login' => 'anonymous' !== WPDA::get_current_user_login(), |
| 535 | 613 | ); |
| 614 | + return $env; | |
| 536 | 615 | } |
| 537 | 616 | |
| 538 | 617 | protected function get_table_info( $dbs, $tbl, $default_where = '' ) { |
| 539 | 618 | $wpdadb = WPDADB::get_db_connection( $dbs ); |
| @@ -543,11 +622,10 @@ | ||
| 543 | 622 | 'engine' => null, |
| 544 | 623 | 'count' => null, |
| 545 | 624 | ); |
| 546 | 625 | } |
| 547 | - $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) ); | |
| 548 | 627 | $resultset = $wpdadb->get_results( $query, 'ARRAY_N' ); |
| 549 | - // phpcs:ignore Standard.Category.SniffName.ErrorCode | |
| 550 | 628 | if ( count( $resultset ) === 1 ) { |
| 551 | 629 | if ( null !== $resultset[0][2] ) { |
| 552 | 630 | return array( |
| 553 | 631 | 'type' => $resultset[0][0], |
| @@ -584,16 +662,22 @@ | ||
| 584 | 662 | return -1; |
| 585 | 663 | } |
| 586 | 664 | } |
| 587 | 665 | |
| 588 | - protected function get_media( $dbs, $tbl, $columns ) { | |
| 666 | + protected function get_media( | |
| 667 | + $dbs, | |
| 668 | + $tbl, | |
| 669 | + $columns, | |
| 670 | + $prefix = '' | |
| 671 | + ) { | |
| 589 | 672 | $media = array(); |
| 590 | 673 | $wp_media = array(); |
| 591 | 674 | foreach ( $columns as $column ) { |
| 592 | 675 | $media_type = WPDA_Media_Model::get_column_media( $tbl, $column['column_name'], $dbs ); |
| 676 | + $column_name = $prefix . $column['column_name']; | |
| 593 | 677 | switch ( $media_type ) { |
| 594 | 678 | case 'ImageURL': |
| 595 | - $media[$column['column_name']] = $media_type; | |
| 679 | + $media[$column_name] = $media_type; | |
| 596 | 680 | break; |
| 597 | 681 | case 'Hyperlink': |
| 598 | 682 | // Get table settings. |
| 599 | 683 | $table_settings_db = WPDA_Table_Settings_Model::query( $tbl, $dbs ); |
| @@ -603,20 +687,20 @@ | ||
| 603 | 687 | $table_settings = null; |
| 604 | 688 | } |
| 605 | 689 | // Check hyperlink format. |
| 606 | 690 | if ( isset( $table_settings['table_settings']['hyperlink_definition'] ) && 'text' === $table_settings['table_settings']['hyperlink_definition'] ) { |
| 607 | - $media[$column['column_name']] = 'HyperlinkURL'; | |
| 691 | + $media[$column_name] = 'HyperlinkURL'; | |
| 608 | 692 | } else { |
| 609 | - $media[$column['column_name']] = 'HyperlinkObject'; | |
| 693 | + $media[$column_name] = 'HyperlinkObject'; | |
| 610 | 694 | } |
| 611 | 695 | break; |
| 612 | 696 | default: |
| 613 | 697 | if ( false !== $media_type ) { |
| 614 | 698 | // Handle WordPress Media Library integration |
| 615 | - $media[$column['column_name']] = "WP-{$media_type}"; | |
| 699 | + $media[$column_name] = "WP-{$media_type}"; | |
| 616 | 700 | } |
| 617 | 701 | } |
| 618 | - $wp_media[$column['column_name']] = $media_type; | |
| 702 | + $wp_media[$column_name] = $media_type; | |
| 619 | 703 | } |
| 620 | 704 | return [ |
| 621 | 705 | 'media' => $media, |
| 622 | 706 | 'wp_media' => $wp_media, |