Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- a/wp-data-access/WPDataAccess/API/WPDA_API_Core.php
+++ b/wp-data-access/WPDataAccess/API/WPDA_API_Core.php
@@ -407,6 +407,13 @@
return is_array( $param );
},
),
+ 'copy_key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'description' => __( 'Internal copy action identifier', 'wp-data-access' ),
+ 'sanitize_callback' => 'sanitize_text_field',
+ 'validate_callback' => 'rest_validate_request_arg',
+ ),
'access' => array(
'required' => true,
'type' => 'string',
--- a/wp-data-access/WPDataAccess/API/WPDA_Actions.php
+++ b/wp-data-access/WPDataAccess/API/WPDA_Actions.php
@@ -2,6 +2,7 @@
namespace WPDataAccessAPI;
+use PDOException;
use WPDataAccessConnectionWPDADB;
use WPDataAccessPlugin_Table_ModelsWPDA_Media_Model;
use WPDataAccessPlugin_Table_ModelsWPDA_Table_Settings_Model;
@@ -9,6 +10,8 @@
use WPDataAccessUtilitiesWPDA_Mail;
use WPDataAccessWPDA;
class WPDA_Actions extends WPDA_API_Core {
+ const WPDA_COPY_TABLE = 'wpda_copy_table';
+
protected $file_pointer;
protected $file_content;
@@ -40,6 +43,23 @@
'sanitize_callback' => 'sanitize_text_field',
'validate_callback' => 'rest_validate_request_arg',
),
+ 'copy_key' => $this->get_param( 'copy_key' ),
+ ),
+ ) );
+ register_rest_route( WPDA_API::WPDA_NAMESPACE, 'action/copy/start', array(
+ 'methods' => array('POST'),
+ 'callback' => array($this, 'action_copy_start'),
+ 'permission_callback' => '__return_true',
+ 'args' => array(
+ 'copy_key' => $this->get_param( 'copy_key' ),
+ ),
+ ) );
+ register_rest_route( WPDA_API::WPDA_NAMESPACE, 'action/copy/cancel', array(
+ 'methods' => array('POST'),
+ 'callback' => array($this, 'action_copy_cancel'),
+ 'permission_callback' => '__return_true',
+ 'args' => array(
+ 'copy_key' => $this->get_param( 'copy_key' ),
),
) );
register_rest_route( WPDA_API::WPDA_NAMESPACE, 'action/truncate', array(
@@ -354,6 +374,7 @@
$from_tbl = $request->get_param( 'from_tbl' );
$to_tbl = $request->get_param( 'to_tbl' );
$copy_data = $request->get_param( 'copy_data' );
+ $copy_key = $request->get_param( 'copy_key' );
if ( '' === $from_dbs || '' === $to_dbs || '' === $from_tbl || '' === $to_tbl ) {
return $this->bad_request();
}
@@ -362,7 +383,8 @@
$to_dbs,
$from_tbl,
$to_tbl,
- $copy_data
+ $copy_data,
+ $copy_key
);
if ( '' === $msg ) {
return $this->WPDA_Rest_Response( __( 'Table successfully copied', 'wp-data-access' ) );
@@ -373,6 +395,42 @@
}
}
+ public function action_copy_start( $request ) {
+ if ( !$this->current_user_can_access() ) {
+ return $this->unauthorized();
+ }
+ if ( !$this->current_user_token_valid( $request ) ) {
+ return $this->invalid_nonce();
+ }
+ if ( !WPDA::current_user_is_admin() ) {
+ return $this->unauthorized();
+ }
+ $copy_key = $request->get_param( 'copy_key' );
+ if ( '' === $copy_key ) {
+ return $this->bad_request();
+ }
+ $this->copy_start( $copy_key );
+ return $this->WPDA_Rest_Response( 'ok', self::copy_in_progress() );
+ }
+
+ public function action_copy_cancel( $request ) {
+ if ( !$this->current_user_can_access() ) {
+ return $this->unauthorized();
+ }
+ if ( !$this->current_user_token_valid( $request ) ) {
+ return $this->invalid_nonce();
+ }
+ if ( !WPDA::current_user_is_admin() ) {
+ return $this->unauthorized();
+ }
+ $copy_key = $request->get_param( 'copy_key' );
+ if ( '' === $copy_key ) {
+ return $this->bad_request();
+ }
+ $this->copy_stop( $copy_key );
+ return $this->WPDA_Rest_Response( 'ok', self::copy_in_progress() );
+ }
+
public function action_rename( $request ) {
if ( !$this->current_user_can_access() ) {
return $this->unauthorized();
@@ -429,7 +487,8 @@
$to_dbs,
$from_tbl,
$to_tbl,
- $copy_data
+ $copy_data,
+ $copy_key
) {
// All values have already been validated and sanitized in the rest route registration.
if ( !WPDA::current_user_is_admin() ) {
@@ -488,30 +547,90 @@
// 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;
+ // Buffer rows to prevent exhausting memory.
+ $buffer_size = 1000;
+ // Default buffer
+ $settings_db = WPDA_Table_Settings_Model::query( $from_tbl, $from_dbs );
+ if ( isset( $settings_db[0]['wpda_table_settings'] ) ) {
+ // Convert string to JSON.
+ $settings = json_decode( $settings_db[0]['wpda_table_settings'], true );
+ if ( isset( $settings['table_settings']['query_buffer_size'] ) && is_numeric( $settings['table_settings']['query_buffer_size'] ) ) {
+ $buffer_size = intval( $settings['table_settings']['query_buffer_size'] );
+ }
+ }
$index = 0;
$loop_done = false;
+ $wpdadb_from->save_queries = false;
+ $wpdadb_to->save_queries = 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' );
+ $rows = $wpdadb_from->get_results( $wpdadb_from->prepare( 'select * from `%1s` limit %d offset %d', array($from_tbl, $buffer_size, $index * $buffer_size) ), 'ARRAY_A' );
// Process rows.
foreach ( $rows as $row ) {
$wpdadb_to->insert( $to_tbl, $row );
}
+ // Cleanup = IMPORTANT!
+ $wpdadb_from->flush();
+ $wpdadb_to->flush();
+ $wpdadb_to->queries = array();
+ $wpdadb_to->last_query = '';
+ $wpdadb_to->last_error = '';
+ $wpdadb_to->result = null;
+ $wpdadb_to->num_rows = 0;
+ $wpdadb_to->rows_affected = 0;
+ wp_cache_flush();
+ if ( function_exists( 'gc_collect_cycles' ) ) {
+ gc_collect_cycles();
+ }
if ( 100 > count( $rows ) ) {
// No more rows to process.
$loop_done = true;
}
+ unset($rows);
$index++;
+ // Check if job was cancelled
+ $in_progress = self::copy_in_progress();
+ if ( !in_array( $copy_key, $in_progress ) ) {
+ $loop_done = true;
+ }
}
}
$wpdadb_from->suppress_errors = $suppress_errors_from;
$wpdadb_to->suppress_errors = $suppress_errors_to;
+ $this->copy_stop( $copy_key );
+ if ( '' !== $wpdadb_from->last_error ) {
+ return $wpdadb_from->last_error;
+ }
+ if ( '' !== $wpdadb_to->last_error ) {
+ return $wpdadb_to->last_error;
+ }
return '';
}
+ private function copy_start( $copy_key ) {
+ $in_progress = self::copy_in_progress();
+ $in_progress[] = $copy_key;
+ update_user_meta( WPDA::get_current_user_id(), self::WPDA_COPY_TABLE, $in_progress );
+ }
+
+ private function copy_stop( $copy_key ) {
+ $in_progress = self::copy_in_progress();
+ if ( ($array_key = array_search( $copy_key, $in_progress )) !== false ) {
+ unset($in_progress[$array_key]);
+ $in_progress = array_values( $in_progress );
+ // Reset indexes to start at 0
+ }
+ update_user_meta( WPDA::get_current_user_id(), self::WPDA_COPY_TABLE, $in_progress );
+ }
+
+ public static function copy_in_progress() {
+ $in_progress = get_user_meta( WPDA::get_current_user_id(), self::WPDA_COPY_TABLE, true );
+ if ( $in_progress === false || $in_progress === '' ) {
+ return array();
+ }
+ return maybe_unserialize( $in_progress );
+ }
+
private function truncate( $dbs, $tbl ) {
// All values have already been validated and sanitized in the rest route registration.
if ( !WPDA::current_user_is_admin() ) {
--- a/wp-data-access/WPDataAccess/API/WPDA_Apps.php
+++ b/wp-data-access/WPDataAccess/API/WPDA_Apps.php
@@ -1991,12 +1991,13 @@
$settings->env = $this->get_env();
global $wpdb;
$settings->wp = [
- 'roles' => $this->get_wp_roles(),
- 'users' => $this->get_wp_users(),
- 'home' => admin_url( 'admin.php' ),
- 'tables' => array_values( $wpdb->tables() ),
- 'date_format' => get_option( 'date_format' ),
- 'time_format' => get_option( 'time_format' ),
+ 'roles' => $this->get_wp_roles(),
+ 'users' => $this->get_wp_users(),
+ 'home' => admin_url( 'admin.php' ),
+ 'tables' => array_values( $wpdb->tables() ),
+ 'date_format' => get_option( 'date_format' ),
+ 'time_format' => get_option( 'time_format' ),
+ 'scroll_offset' => WPDA::get_option( WPDA::OPTION_APPS_SCROLL_OFFSET ),
];
return $settings;
}
--- a/wp-data-access/WPDataAccess/API/WPDA_Table.php
+++ b/wp-data-access/WPDataAccess/API/WPDA_Table.php
@@ -1178,16 +1178,18 @@
$connect = null;
global $wpdb;
$settings->wp = [
- 'roles' => $this->get_wp_roles(),
- 'users' => $this->get_wp_users(),
- 'home' => admin_url( 'admin.php' ),
- 'homea' => admin_url( 'admin-ajax.php' ),
- 'tables' => array_values( $wpdb->tables() ),
- 'date_format' => get_option( 'date_format' ),
- 'time_format' => get_option( 'time_format' ),
- 'alter' => $wp_nonce_alter,
- 'refresh' => $wp_nonce_refresh,
- 'connect' => $connect,
+ 'roles' => $this->get_wp_roles(),
+ 'users' => $this->get_wp_users(),
+ 'home' => admin_url( 'admin.php' ),
+ 'homea' => admin_url( 'admin-ajax.php' ),
+ 'tables' => array_values( $wpdb->tables() ),
+ 'date_format' => get_option( 'date_format' ),
+ 'time_format' => get_option( 'time_format' ),
+ 'alter' => $wp_nonce_alter,
+ 'refresh' => $wp_nonce_refresh,
+ 'connect' => $connect,
+ 'copyinprogress' => WPDA_Actions::copy_in_progress(),
+ 'scroll_offset' => WPDA::get_option( WPDA::OPTION_APPS_SCROLL_OFFSET ),
];
if ( true === $waa ) {
$settings->wp['aonce'] = implode( '-', array(
--- a/wp-data-access/WPDataAccess/Data_Tables/WPDA_Data_Tables.php
+++ b/wp-data-access/WPDataAccess/Data_Tables/WPDA_Data_Tables.php
@@ -634,22 +634,22 @@
$this->serverSide = true;
// Set pagination values.
$offset = 0;
- if ( isset( $_REQUEST['start'] ) ) {
- $offset = sanitize_text_field( wp_unslash( $_REQUEST['start'] ) );
+ if ( isset( $_REQUEST['start'] ) && ctype_digit( $_REQUEST['start'] ) ) {
+ $offset = (int) $_REQUEST['start'];
// input var okay.
}
$limit = -1;
// jQuery DataTables default.
- if ( isset( $_REQUEST['length'] ) ) {
- $limit = sanitize_text_field( wp_unslash( $_REQUEST['length'] ) );
+ if ( isset( $_REQUEST['length'] ) && ctype_digit( $_REQUEST['length'] ) ) {
+ $limit = (int) $_REQUEST['length'];
// input var okay.
}
$publication_mode = 'normal';
- if ( -1 == $limit && isset( $_REQUEST['more_start'] ) && isset( $_REQUEST['more_limit'] ) ) {
+ if ( -1 == $limit && isset( $_REQUEST['more_start'] ) && ctype_digit( $_REQUEST['more_start'] ) && isset( $_REQUEST['more_limit'] ) && ctype_digit( $_REQUEST['more_limit'] ) ) {
$publication_mode = 'more';
- $offset = sanitize_text_field( wp_unslash( $_REQUEST['more_start'] ) );
+ $offset = (int) $_REQUEST['more_start'];
// input var okay.
- $limit = sanitize_text_field( wp_unslash( $_REQUEST['more_limit'] ) );
+ $limit = (int) $_REQUEST['more_limit'];
// input var okay.
}
if ( '' !== $pub_id && '0' != $pub_id ) {
@@ -970,7 +970,7 @@
$query .= " order by {$orderby} ";
}
if ( -1 != $limit ) {
- $query .= " limit {$limit} offset {$offset}";
+ $query .= $wpdadb->prepare( " limit %d offset %d", [$limit, $offset] );
}
$hyperlinks = array();
if ( count( $hyperlinks_column_index ) ) {
@@ -1315,7 +1315,9 @@
$obj->recordsFiltered = 0;
$obj->data = array();
$obj->error = $error;
- $obj->debug = $debug;
+ if ( 'on' === WPDA::get_option( WPDA::OPTION_PLUGIN_DEBUG ) ) {
+ $obj->debug = $debug;
+ }
echo json_encode( $obj );
}
--- a/wp-data-access/WPDataAccess/Settings/WPDA_Settings.php
+++ b/wp-data-access/WPDataAccess/Settings/WPDA_Settings.php
@@ -73,6 +73,7 @@
// Tabs array is filled in constructor to add i18n.
$this->tabs = array(
'plugin' => 'Plugin',
+ 'apps' => 'Apps',
'backend' => 'Back-end',
'frontend' => 'Front-end',
'uninstall' => 'Uninstall',
--- a/wp-data-access/WPDataAccess/Settings/WPDA_Settings_Apps.php
+++ b/wp-data-access/WPDataAccess/Settings/WPDA_Settings_Apps.php
@@ -0,0 +1,82 @@
+<?php
+
+namespace WPDataAccessSettings {
+
+ use WPDataAccessWPDA;
+
+ class WPDA_Settings_Apps extends WPDA_Settings {
+
+ /**
+ * Add back-end tab content
+ *
+ * See class documentation for flow explanation.
+ *
+ * @since 5.5.71
+ */
+ protected function add_content() {
+
+ if ( isset( $_REQUEST['action'] ) ) {
+ $action = sanitize_text_field(wp_unslash($_REQUEST['action'])); // input var okay.
+
+ // Security check.
+ $wp_nonce = isset( $_REQUEST['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ) : ''; // input var okay.
+ if ( ! wp_verify_nonce( $wp_nonce, 'wpda-apps-settings' ) ) {
+ wp_die( __( 'ERROR: Not authorized', 'wp-data-access' ) );
+ }
+
+ if ( 'save' === $action ) {
+
+ if (
+ isset( $_REQUEST['scroll_offset'] ) &&
+ '' !== $_REQUEST['scroll_offset']
+ ) {
+ $scroll_offset = sanitize_text_field( wp_unslash( $_REQUEST['scroll_offset'] ) ); // input var okay.
+ update_option( 'wpda_apps_scroll_offset', $scroll_offset );
+ }
+
+ } elseif ( 'setdefaults' === $action ) {
+
+ WPDA::set_option( WPDA::OPTION_APPS_SCROLL_OFFSET );
+
+ }
+ }
+
+ $scroll_offset = WPDA::get_option( WPDA::OPTION_APPS_SCROLL_OFFSET );
+
+ ?>
+ <form id="wpda_settings_backend" method="post"
+ action="?page=<?php echo esc_attr( $this->page ); ?>&tab=apps">
+ <table class="wpda-table-settings">
+ <tr>
+ <th><?php echo __( 'Scroll Offset', 'wp-data-access' ); ?></th>
+ <td>
+ <input
+ type="number" step="1" min="0" max="999" name="scroll_offset" maxlength="3"
+ value="<?php echo esc_attr( $scroll_offset ); ?>">
+ </td>
+ </tr>
+ </table>
+ <div class="wpda-table-settings-button">
+ <input type="hidden" name="action" value="save"/>
+ <button type="submit" class="button button-primary">
+ <i class="fas fa-check wpda_icon_on_button"></i>
+ <?php echo __( 'Save Apps Settings', 'wp-data-access' ); ?>
+ </button>
+ <a href="javascript:void(0)"
+ onclick="if (confirm('<?php echo __( 'Reset to defaults?', 'wp-data-access' ); ?>')) {
+ jQuery('input[name="action"]').val('setdefaults');
+ jQuery('#wpda_settings_backend').trigger('submit')
+ }"
+ class="button">
+ <i class="fas fa-times-circle wpda_icon_on_button"></i>
+ <?php echo __( 'Reset Apps Settings To Defaults', 'wp-data-access' ); ?>
+ </a>
+ </div>
+ <?php wp_nonce_field( 'wpda-apps-settings', '_wpnonce', false ); ?>
+ </form>
+ <?php
+
+ }
+ }
+
+}
No newline at end of file
--- a/wp-data-access/WPDataAccess/WPDA.php
+++ b/wp-data-access/WPDataAccess/WPDA.php
@@ -51,8 +51,8 @@
/**
* Option wpda_version and it's default value
*/
- const OPTION_WPDA_VERSION = array( 'wpda_version', '5.5.70' );
- const OPTION_WPDA_CLIENT_VERSION = array( 'wpda_client_version', '1.0.68' );
+ const OPTION_WPDA_VERSION = array( 'wpda_version', '5.5.71' );
+ const OPTION_WPDA_CLIENT_VERSION = array( 'wpda_client_version', '1.0.69' );
const OPTION_WPDA_UPGRADED = array( 'wpda_upgraded', false );
/**
* Option wpda_setup_error and it's default value
@@ -127,6 +127,9 @@
const OPTION_PLUGIN_TIME_PLACEHOLDER = array( 'wpda_plugin_time_placeholder', 'hh:mi');
const OPTION_PLUGIN_SET_FORMAT = array( 'wpda_plugin_set_format', 'csv');
+ // App options
+ const OPTION_APPS_SCROLL_OFFSET = array( 'wpda_apps_scroll_offset', '120');
+
// Plugin debug mode
const OPTION_PLUGIN_DEBUG = array( 'wpda_plugin_debug', 'off');
--- a/wp-data-access/includes/class-wp-data-access.php
+++ b/wp-data-access/includes/class-wp-data-access.php
@@ -495,6 +495,9 @@
$current_tab = ( isset( $_REQUEST['tab'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['tab'] ) ) : 'plugin' );
// phpcs:ignore WordPress.Security.NonceVerification
switch ( $current_tab ) {
+ case 'apps':
+ $wpda_settings_class_name = 'WPDA_Settings_Apps';
+ break;
case 'backend':
$wpda_settings_class_name = 'WPDA_Settings_BackEnd';
break;
--- a/wp-data-access/wp-data-access.php
+++ b/wp-data-access/wp-data-access.php
@@ -4,7 +4,7 @@
* Plugin Name: WP Data Access
* Plugin URI: https://wpdataaccess.com/
* Description: A powerful data-driven App Builder with an intuitive Table Builder, a highly customizable Form Builder and interactive Chart support in 35 languages
- * Version: 5.5.70
+ * Version: 5.5.71
* Author: Passionate Programmers B.V.
* Author URI: https://wpdataaccess.com/
* Text Domain: wp-data-access