File "WPDA_Actions.php"

Full Path: /home/vantageo/public_html/cache/cache/cache/cache/.wp-cli/wp-content/plugins/wp-data-access/WPDataAccess/API/WPDA_Actions.php
File size: 23.12 KB
MIME-type: text/x-php
Charset: utf-8

<?php

namespace WPDataAccess\API;

use WPDataAccess\Connection\WPDADB;
use WPDataAccess\Plugin_Table_Models\WPDA_Media_Model;
use WPDataAccess\Plugin_Table_Models\WPDA_Table_Settings_Model;
use WPDataAccess\Plugin_Table_Models\WPDA_User_Menus_Model;
use WPDataAccess\WPDA;
class WPDA_Actions extends WPDA_API_Core {
    protected $file_pointer;

    protected $file_content;

    public function register_rest_routes() {
        register_rest_route( WPDA_API::WPDA_NAMESPACE, 'action/rename', array(
            'methods'             => array('POST'),
            'callback'            => array($this, 'action_rename'),
            'permission_callback' => '__return_true',
            'args'                => array(
                'dbs'      => $this->get_param( 'dbs', __( 'Local database name or remote connection string (does not accept system schemas)', 'wp-data-access' ) ),
                'from_tbl' => $this->get_param( 'tbl', __( 'Source table name (does not rename WordPress tables)', 'wp-data-access' ) ),
                'to_tbl'   => $this->get_param( 'tbl', __( 'Destination table name (cannot overwrite existing table)', 'wp-data-access' ) ),
            ),
        ) );
        register_rest_route( WPDA_API::WPDA_NAMESPACE, 'action/copy', array(
            'methods'             => array('POST'),
            'callback'            => array($this, 'action_copy'),
            'permission_callback' => '__return_true',
            'args'                => array(
                'from_dbs'  => $this->get_param( 'dbs', __( 'Source database name or remote connection string', 'wp-data-access' ) ),
                'to_dbs'    => $this->get_param( 'dbs', __( 'Destination database name or remote connection string', 'wp-data-access' ) ),
                'from_tbl'  => $this->get_param( 'tbl', __( 'Source table name', 'wp-data-access' ) ),
                'to_tbl'    => $this->get_param( 'tbl', __( 'Destination table name', 'wp-data-access' ) ),
                'copy_data' => array(
                    'required'          => true,
                    'type'              => 'boolean',
                    'description'       => __( 'Copy data from source to destination table', 'wp-data-access' ),
                    'sanitize_callback' => 'sanitize_text_field',
                    'validate_callback' => 'rest_validate_request_arg',
                ),
            ),
        ) );
        register_rest_route( WPDA_API::WPDA_NAMESPACE, 'action/truncate', array(
            'methods'             => array('POST'),
            'callback'            => array($this, 'action_truncate'),
            'permission_callback' => '__return_true',
            'args'                => array(
                'dbs' => $this->get_param( 'dbs', __( 'Local database name or remote connection string (does not accept system schemas)', 'wp-data-access' ) ),
                'tbl' => $this->get_param( 'tbl', __( 'Source table name (does not truncate WordPress tables)', 'wp-data-access' ) ),
            ),
        ) );
        register_rest_route( WPDA_API::WPDA_NAMESPACE, 'action/drop', array(
            'methods'             => array('POST'),
            'callback'            => array($this, 'action_drop'),
            'permission_callback' => '__return_true',
            'args'                => array(
                'dbs' => $this->get_param( 'dbs', __( 'Local database name or remote connection string (does not accept system schemas)', 'wp-data-access' ) ),
                'tbl' => $this->get_param( 'tbl', __( 'Source table name (does not drop WordPress tables)', 'wp-data-access' ) ),
                'typ' => $this->get_param( 'typ' ),
            ),
        ) );
        register_rest_route( WPDA_API::WPDA_NAMESPACE, 'action/import', array(
            'methods'             => array('POST'),
            'callback'            => array($this, 'action_import'),
            'permission_callback' => '__return_true',
        ) );
    }

    public function action_import( $request ) {
        if ( !$this->current_user_can_access( true ) ) {
            return $this->unauthorized();
        }
        if ( !$this->current_user_token_valid( $request, true ) ) {
            return $this->invalid_nonce();
        }
        $dbs = $this->sanitize_db_identifier( $request->get_param( 'dbs' ) );
        $files = $request->get_file_params();
        $response = array();
        $errors = false;
        if ( 0 === count( $files ) || '' === trim( $dbs ) ) {
            return $this->bad_request();
        } else {
            foreach ( $files as $file ) {
                // phpcs:disable
                $temp_file_name = sanitize_text_field( $file['tmp_name'] );
                // For Windows: do NOT unslash!
                // phpcs:enable
                $temp_file_type = sanitize_text_field( wp_unslash( $file['type'] ) );
                $orig_file_name = sanitize_text_field( wp_unslash( $file['name'] ) );
                if ( 0 === $file['error'] && is_uploaded_file( $temp_file_name ) ) {
                    if ( 'application/zip' === $temp_file_type || 'application/x-zip' === $temp_file_type || 'application/x-zip-compressed' === $temp_file_type ) {
                        // Process ZIP file.
                        if ( class_exists( '\\ZipArchive' ) ) {
                            $zip = new \ZipArchive();
                            if ( $zip->open( $temp_file_name ) ) {
                                for ($i = 0; $i < $zip->numFiles; $i++) {
                                    $this->file_pointer = $zip->getStream( $zip->getNameIndex( $i ) );
                                    $status = $this->import( $zip->getNameIndex( $i ), $dbs );
                                    if ( isset( $status['status'], $status['msg'] ) ) {
                                        $errors = $errors || 'error' === $status['status'];
                                        $response[] = array(
                                            $zip->getNameIndex( $i ) => array(
                                                'status' => $status['status'],
                                                'msg'    => $status['msg'],
                                                'errors' => $status['errors'],
                                            ),
                                        );
                                    }
                                }
                            } else {
                                // Error reading ZIP file.
                                $errors = true;
                                $response[] = array(
                                    $orig_file_name => array(
                                        'status' => 'error',
                                        'msg'    => sprintf( __( 'Import failed [error reading ZIP file `%s`]', 'wp-data-access' ), $orig_file_name ),
                                    ),
                                );
                            }
                        } else {
                            // ZipArchive not installed.
                            $errors = true;
                            $response[] = array(
                                $orig_file_name => array(
                                    'status' => 'error',
                                    'msg'    => sprintf( __( 'Import failed - ZipArchive not installed %s', 'wp-data-access' ) ),
                                ),
                            );
                        }
                    } else {
                        // Process plain file.
                        $this->file_pointer = fopen( $temp_file_name, 'rb' );
                        $status = $this->import( $orig_file_name, $dbs );
                        if ( isset( $status['status'], $status['msg'] ) ) {
                            $errors = $errors || 'error' === $status['status'];
                            $response[] = array(
                                $orig_file_name => array(
                                    'status' => $status['status'],
                                    'msg'    => $status['msg'],
                                    'errors' => $status['errors'],
                                ),
                            );
                        }
                    }
                }
            }
        }
        if ( $errors ) {
            $msg = __( 'File(s) imported with errors', 'wp-data-access' );
        } else {
            $msg = __( 'File(s) successfully imported', 'wp-data-access' );
        }
        return $this->WPDA_Rest_Response( $msg, null, array(
            'imported' => $response,
        ) );
    }

    public function action_drop( $request ) {
        if ( !$this->current_user_can_access( true ) ) {
            return $this->unauthorized();
        }
        if ( !$this->current_user_token_valid( $request, true ) ) {
            return $this->invalid_nonce();
        }
        $dbs = $request->get_param( 'dbs' );
        $tbl = $request->get_param( 'tbl' );
        $typ = $request->get_param( 'typ' );
        if ( '' === $dbs || '' === $tbl ) {
            return $this->bad_request();
        }
        global $wpdb;
        if ( $wpdb->dbname === $dbs && in_array( $tbl, $wpdb->tables() ) ) {
            return $this->unauthorized();
        }
        $msg = $this->drop( $dbs, $tbl, $typ );
        if ( '' === $msg ) {
            if ( 1 === $typ ) {
                return $this->WPDA_Rest_Response( __( 'View successfully dropped', 'wp-data-access' ) );
            } else {
                return $this->WPDA_Rest_Response( __( 'Table successfully dropped', 'wp-data-access' ) );
            }
        } else {
            return new \WP_Error('error', $msg, array(
                'status' => 403,
            ));
        }
    }

    public function action_truncate( $request ) {
        if ( !$this->current_user_can_access( true ) ) {
            return $this->unauthorized();
        }
        if ( !$this->current_user_token_valid( $request, true ) ) {
            return $this->invalid_nonce();
        }
        $dbs = $request->get_param( 'dbs' );
        $tbl = $request->get_param( 'tbl' );
        if ( '' === $dbs || '' === $tbl ) {
            return $this->bad_request();
        }
        global $wpdb;
        if ( $wpdb->dbname === $dbs && in_array( $tbl, $wpdb->tables() ) ) {
            return $this->unauthorized();
        }
        $msg = $this->truncate( $dbs, $tbl );
        if ( '' === $msg ) {
            return $this->WPDA_Rest_Response( __( 'Table successfully truncated', 'wp-data-access' ) );
        } else {
            return new \WP_Error('error', $msg, array(
                'status' => 403,
            ));
        }
    }

    public function action_copy( $request ) {
        if ( !$this->current_user_can_access( true ) ) {
            return $this->unauthorized();
        }
        if ( !$this->current_user_token_valid( $request, true ) ) {
            return $this->invalid_nonce();
        }
        $from_dbs = $request->get_param( 'from_dbs' );
        $to_dbs = $request->get_param( 'to_dbs' );
        $from_tbl = $request->get_param( 'from_tbl' );
        $to_tbl = $request->get_param( 'to_tbl' );
        $copy_data = $request->get_param( 'copy_data' );
        if ( '' === $from_dbs || '' === $to_dbs || '' === $from_tbl || '' === $to_tbl ) {
            return $this->bad_request();
        }
        $msg = $this->copy(
            $from_dbs,
            $to_dbs,
            $from_tbl,
            $to_tbl,
            $copy_data
        );
        if ( '' === $msg ) {
            return $this->WPDA_Rest_Response( __( 'Table successfully copied', 'wp-data-access' ) );
        } else {
            return new \WP_Error('error', $msg, array(
                'status' => 403,
            ));
        }
    }

    public function action_rename( $request ) {
        if ( !$this->current_user_can_access( true ) ) {
            return $this->unauthorized();
        }
        if ( !$this->current_user_token_valid( $request, true ) ) {
            return $this->invalid_nonce();
        }
        $dbs = $request->get_param( 'dbs' );
        $from_tbl = $request->get_param( 'from_tbl' );
        $to_tbl = $request->get_param( 'to_tbl' );
        if ( '' === $dbs || '' === $from_tbl || '' === $to_tbl ) {
            return $this->bad_request();
        }
        if ( 'information_schema' === $dbs || 'mysql' === $dbs || 'performance_schema' === $dbs || 'sys' === $dbs || '' === $dbs ) {
            return $this->unauthorized();
        }
        global $wpdb;
        if ( $wpdb->dbname === $dbs && in_array( $from_tbl, $wpdb->tables() ) ) {
            return $this->unauthorized();
        }
        $msg = $this->rename( $dbs, $from_tbl, $to_tbl );
        if ( '' === $msg ) {
            if ( 1 === $typ ) {
                return $this->WPDA_Rest_Response( __( 'View successfully renamed', 'wp-data-access' ) );
            } else {
                return $this->WPDA_Rest_Response( __( 'Table successfully renamed', 'wp-data-access' ) );
            }
        } else {
            return new \WP_Error('error', $msg, array(
                'status' => 403,
            ));
        }
    }

    private function rename( $dbs, $from_tbl, $to_tbl ) {
        // All values have already been validated and sanitized in the rest route registration.
        if ( !current_user_can( 'manage_options' ) ) {
            return 'Unauthorized';
        }
        $wpdadb = WPDADB::get_db_connection( $dbs );
        if ( null === $wpdadb ) {
            return sprintf( __( 'Remote database %s not available', 'wp-data-access' ), esc_attr( $dbs ) );
        }
        $suppress_errors = $wpdadb->suppress_errors;
        $wpdadb->suppress_errors = true;
        $wpdadb->query( $wpdadb->prepare( 'rename table `%1s` to `%1s`', array($from_tbl, $to_tbl) ) );
        $wpdadb->suppress_errors = $suppress_errors;
        return $wpdadb->last_error;
    }

    private function copy(
        $from_dbs,
        $to_dbs,
        $from_tbl,
        $to_tbl,
        $copy_data
    ) {
        // All values have already been validated and sanitized in the rest route registration.
        if ( !current_user_can( 'manage_options' ) ) {
            return 'Unauthorized';
        }
        $wpdadb_from = WPDADB::get_db_connection( $from_dbs );
        if ( null === $wpdadb_from ) {
            return sprintf( __( 'Remote database %s not available', 'wp-data-access' ), esc_attr( $from_dbs ) );
        }
        $wpdadb_to = WPDADB::get_db_connection( $to_dbs );
        if ( null === $wpdadb_to ) {
            return sprintf( __( 'Remote database %s not available', 'wp-data-access' ), esc_attr( $to_dbs ) );
        }
        $suppress_errors_from = $wpdadb_from->suppress_errors;
        $wpdadb_from->suppress_errors = true;
        $suppress_errors_to = $wpdadb_to->suppress_errors;
        $wpdadb_to->suppress_errors = true;
        // Get create table statement.
        $wpdadb_from->query( "SET sql_mode = 'NO_TABLE_OPTIONS'" );
        $sql_cmd = $wpdadb_from->get_results( $wpdadb_from->prepare( 'show create table `%1s`', array($from_tbl) ), 'ARRAY_A' );
        // Check for errors.
        if ( '' !== $wpdadb_from->last_error ) {
            $wpdadb_from->suppress_errors = $suppress_errors_from;
            $wpdadb_to->suppress_errors = $suppress_errors_to;
            return $wpdadb_from->last_error;
        }
        if ( !isset( $sql_cmd[0]['Create Table'] ) ) {
            $wpdadb_from->suppress_errors = $suppress_errors_from;
            $wpdadb_to->suppress_errors = $suppress_errors_to;
            return 'Create command table failed';
        }
        // Update destination table name if applicable.
        $create_table_statement = $sql_cmd[0]['Create Table'];
        if ( $from_tbl !== $to_tbl ) {
            // Modify create table statement
            $pos = strpos( $create_table_statement, $from_tbl );
            if ( $pos !== false ) {
                $create_table_statement = substr_replace(
                    $create_table_statement,
                    $to_tbl,
                    $pos,
                    strlen( $from_tbl )
                );
            }
        }
        // Create new table.
        $wpdadb_to->query( $create_table_statement );
        // Check for errors.
        if ( '' !== $wpdadb_to->last_error ) {
            $wpdadb_from->suppress_errors = $suppress_errors_from;
            $wpdadb_to->suppress_errors = $suppress_errors_to;
            return $wpdadb_to->last_error;
        }
        if ( '1' === $copy_data ) {
            // Copy data from source to destination table.
            set_time_limit( 0 );
            // Prevent time out.
            // Use a cursor to process all rows and prevent exhausting memory.
            // Process 100 rows per batch to prevent exhausting memory.
            $buffer_size = 100;
            $index = 0;
            $loop_done = false;
            while ( !$loop_done ) {
                // Get rows.
                $rows = $wpdadb_from->get_results( $wpdadb_from->prepare( 'select * from `%1s` limit %1s offset %1s', array($from_tbl, $buffer_size, $index * $buffer_size) ), 'ARRAY_A' );
                // Process rows.
                foreach ( $rows as $row ) {
                    $wpdadb_to->insert( $to_tbl, $row );
                }
                if ( 100 > count( $rows ) ) {
                    // No more rows to process.
                    $loop_done = true;
                }
                $index++;
            }
        }
        $wpdadb_from->suppress_errors = $suppress_errors_from;
        $wpdadb_to->suppress_errors = $suppress_errors_to;
        return '';
    }

    private function truncate( $dbs, $tbl ) {
        // All values have already been validated and sanitized in the rest route registration.
        if ( !current_user_can( 'manage_options' ) ) {
            return 'Unauthorized';
        }
        $wpdadb = WPDADB::get_db_connection( $dbs );
        if ( null === $wpdadb ) {
            return sprintf( __( 'Remote database %s not available', 'wp-data-access' ), esc_attr( $dbs ) );
        }
        $suppress_errors = $wpdadb->suppress_errors;
        $wpdadb->suppress_errors = true;
        $wpdadb->query( $wpdadb->prepare( 'truncate table `%1s`', array($tbl) ) );
        $wpdadb->suppress_errors = $suppress_errors;
        return $wpdadb->last_error;
    }

    private function drop( $dbs, $tbl, $typ ) {
        // All values have already been validated and sanitized in the rest route registration.
        if ( !current_user_can( 'manage_options' ) ) {
            return 'Unauthorized';
        }
        $wpdadb = WPDADB::get_db_connection( $dbs );
        if ( null === $wpdadb ) {
            return sprintf( __( 'Remote database %s not available', 'wp-data-access' ), esc_attr( $dbs ) );
        }
        $suppress_errors = $wpdadb->suppress_errors;
        $wpdadb->suppress_errors = true;
        if ( 1 === $typ ) {
            $wpdadb->query( $wpdadb->prepare( 'drop view `%1s`', array($tbl) ) );
        } else {
            $wpdadb->query( $wpdadb->prepare( 'drop table `%1s`', array($tbl) ) );
        }
        $this->post_drop_table( $dbs, $tbl );
        $wpdadb->suppress_errors = $suppress_errors;
        return $wpdadb->last_error;
    }

    private function post_drop_table( $dbs, $tbl ) {
        global $wpdb;
        $suppress = $wpdb->suppress_errors( true );
        // Table settings...
        $wpdb->query( $wpdb->prepare( 
            'delete from `%1s` where wpda_schema_name = %s and wpda_table_name = %s ',
            // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders
            array(WPDA::remove_backticks( WPDA_Table_Settings_Model::get_base_table_name() ), $dbs, $tbl)
         ) );
        // WordPress media library columns...
        $wpdb->query( $wpdb->prepare( 
            'delete from `%1s` where media_schema_name = %s and media_table_name = %s ',
            // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders
            array(WPDA::remove_backticks( WPDA_Media_Model::get_base_table_name() ), $dbs, $tbl)
         ) );
        // Data menus...
        $wpdb->query( $wpdb->prepare( 
            'delete from `%1s` where menu_schema_name = %s and menu_table_name = %s ',
            // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders
            array(WPDA::remove_backticks( WPDA_User_Menus_Model::get_base_table_name() ), $dbs, $tbl)
         ) );
        $wpdb->suppress_errors( $suppress );
    }

    private function import( $file_name, $dbs ) {
        if ( !current_user_can( 'manage_options' ) ) {
            return array(
                'status' => 'error',
                'msg'    => 'Unauthorized',
            );
        }
        $errors = array();
        global $wpdb;
        $wpdadb = WPDADB::get_db_connection( $dbs );
        if ( null === $wpdadb ) {
            return array(
                'status' => 'error',
                'msg'    => sprintf( __( 'ERROR - Remote database %s not available', 'wp-data-access' ), esc_attr( $dbs ) ),
            );
        }
        $suppress = $wpdadb->suppress_errors( true );
        if ( false !== $this->file_pointer ) {
            while ( !feof( $this->file_pointer ) ) {
                $this->file_content .= fread( $this->file_pointer, 4096 );
                // Replace WP prefix and WPDA prefix.
                $this->file_content = str_replace( '{wp_schema}', $wpdb->dbname, $this->file_content );
                $this->file_content = str_replace( '{wp_prefix}', $wpdb->prefix, $this->file_content );
                $this->file_content = str_replace( '{wpda_prefix}', 'wpda', $this->file_content );
                // for backward compatibility
                // Find and process SQL statements.
                $sql_end_unix = strpos( $this->file_content, ";\n" );
                $sql_end_windows = strpos( $this->file_content, ";\r\n" );
                while ( false !== $sql_end_unix || false !== $sql_end_windows ) {
                    if ( false === $sql_end_unix ) {
                        $sql_end = $sql_end_windows;
                    } elseif ( false === $sql_end_windows ) {
                        $sql_end = $sql_end_unix;
                    } else {
                        $sql_end = min( $sql_end_unix, $sql_end_windows );
                    }
                    $sql = rtrim( substr( $this->file_content, 0, $sql_end ) );
                    $this->file_content = substr( $this->file_content, strpos( $this->file_content, $sql ) + strlen( $sql ) + 1 );
                    if ( false === $wpdadb->query( $sql ) ) {
                        $errors[] = $wpdadb->last_error;
                    }
                    // Find next SQL statement.
                    $sql_end_unix = strpos( $this->file_content, ";\n" );
                    $sql_end_windows = strpos( $this->file_content, ";\r\n" );
                }
            }
        }
        $wpdadb->suppress_errors( $suppress );
        // Process file content.
        if ( 0 < count( $errors ) ) {
            return array(
                'status' => 'error',
                'msg'    => sprintf( __( 'Import `%s` failed [check import file]', 'wp-data-access' ), $file_name ),
                'errors' => $errors,
            );
        } else {
            // Import succeeded.
            return array(
                'status' => 'ok',
                'msg'    => sprintf( __( 'Import `%s` completed succesfully', 'wp-data-access' ), $file_name ),
                'errors' => $errors,
            );
        }
    }

}