'',
'version' => '',
'callback' => '',
];
$args = wp_parse_args( $args, $args_default );
// You can only register database upgrade.
$args['type'] = 'database';
// Bailout.
if (
empty( $args['id'] ) ||
empty( $args['version'] ) ||
empty( $args['callback'] ) ||
! is_callable( $args['callback'] )
) {
return;
}
// Change depend param to array.
if ( isset( $args['depend'] ) && is_string( $args['depend'] ) ) {
$args['depend'] = [ $args['depend'] ];
}
$this->updates[ $args['type'] ][] = $args;
}
/**
* Get instance.
*
* @since
* @access static
* @return static
*/
static function get_instance() {
if ( is_null( self::$instance ) ) {
self::$instance = new self();
}
return self::$instance;
}
/**
*
* Setup hook
*
* @since 1.8.12
* @access public
*/
public function setup() {
/**
* Load file
*/
require_once GIVE_PLUGIN_DIR . 'includes/class-give-background-updater.php';
require_once GIVE_PLUGIN_DIR . 'includes/admin/upgrades/upgrade-functions.php';
self::$background_updater = new Give_Background_Updater();
/**
* Setup hooks.
*/
add_action( 'init', [ $this, '__register_upgrade' ], 9999 );
add_action( 'give_set_upgrade_completed', [$this, 'flush_resume_updates'], 9999 );
add_action( 'wp_ajax_give_db_updates_info', [$this, 'give_db_updates_info'] );
add_action( 'wp_ajax_give_run_db_updates', [$this, 'give_start_updating'] );
add_action( 'admin_init', [$this, 'redirect_admin'] );
add_action( 'admin_init', [$this, 'pause_db_update'], - 1 );
add_action( 'admin_init', [$this, 'restart_db_update'], - 1 );
add_action( 'admin_notices', [$this, 'show_notice'] );
add_action( 'give_restart_db_upgrade', [$this, 'health_background_update'] );
if ( is_admin() ) {
add_action( 'admin_init', [$this, 'change_donations_label'], 9999 );
add_action( 'admin_menu', [$this, 'register_menu'], 55 );
}
}
/**
* Register plugin add-on updates.
*
* @since 4.9.0 rename function - PHP 8 compatibility
* @since 1.8.12
* @access public
*/
public function register_plugin_addon_updates() {
$addons = give_get_plugins( [ 'only_premium_add_ons' => true ] );
$plugin_updates = get_plugin_updates();
foreach ( $addons as $key => $info ) {
if ( empty( $plugin_updates[ $key ] ) ) {
continue;
}
$this->updates['plugin'][] = array_merge( $info, (array) $plugin_updates[ $key ] );
}
}
/**
* Fire custom action hook to register updates
*
* @since 1.8.12
* @access public
*/
public function __register_upgrade() {
if ( ! is_admin() ) {
return;
}
/**
* Fire the hook
*
* @since 1.8.12
*/
do_action( 'give_register_updates', $this );
}
/**
* Rename `Donations` menu title if updates exists
*
* @since 4.9.0 rename function - PHP 8 compatibility
* @since 1.8.12
* @access public
*/
function change_donations_label() {
global $menu;
// Bailout.
if ( empty( $menu ) || ! $this->get_total_update_count() ) {
return;
}
$is_update = ( $this->is_doing_updates() && ! self::$background_updater->is_paused_process() );
foreach ( $menu as $index => $menu_item ) {
if ( 'edit.php?post_type=give_forms' !== $menu_item[2] ) {
continue;
}
$menu[ $index ][0] = sprintf(
'%1$s %2$s%3$s',
__( 'GiveWP', 'give' ),
$is_update ?
$this->get_db_update_processing_percentage() :
$this->get_total_update_count(),
$is_update ? '%' : ''
);
break;
}
}
/**
* Register updates menu
*
* @since 4.14.0 update permission capability to use facade
* @since 4.9.0 rename function - PHP 8 compatibility
* @since 1.8.12
* @access public
*/
public function register_menu() {
// Load plugin updates.
$this->register_plugin_addon_updates();
// Bailout.
if ( ! $this->get_total_update_count() ) {
// Show complete update message if still on update setting page.
if ( isset( $_GET['page'] ) && 'give-updates' === $_GET['page'] ) {
// Upgrades
add_submenu_page(
'edit.php?post_type=give_forms',
esc_html__( 'GiveWP Updates Complete', 'give' ),
__( 'Updates', 'give' ),
'manage_give_settings',
'give-updates',
[ $this, 'render_complete_page' ]
);
}
return;
}
$is_update = ( $this->is_doing_updates() && ! self::$background_updater->is_paused_process() );
// Upgrades
add_submenu_page(
'edit.php?post_type=give_forms',
esc_html__( 'GiveWP Updates', 'give' ),
sprintf(
'%1$s %3$s%4$s',
__( 'Updates', 'give' ),
isset( $_GET['give-pause-db-upgrades'] ) ? ' style="display:none;"' : '',
$is_update ?
$this->get_db_update_processing_percentage() :
$this->get_total_update_count(),
$is_update ? '%' : ''
),
UserPermissions::settings()->manageCap(),
'give-updates',
[ $this, 'render_page' ]
);
}
/**
* Show update related notices
*
* @since 4.9.0 rename function - PHP 8 compatibility
* @since 2.0
* @access public
*/
public function redirect_admin() {
// Show db upgrade completed notice.
if (
! wp_doing_ajax() &&
current_user_can( 'manage_give_settings' ) &&
get_option( 'give_show_db_upgrade_complete_notice' ) &&
! isset( $_GET['give-db-update-completed'] )
) {
delete_option( 'give_show_db_upgrade_complete_notice' );
wp_redirect( admin_url( 'edit.php?post_type=give_forms&page=give-updates&give-db-update-completed=give_db_upgrade_completed' ) );
exit();
}
}
/**
* Pause db upgrade
*
* @since 4.9.0 rename function - PHP 8 compatibility
* @since 2.0.1
* @access public
*
* @param bool $force
*
* @return bool
*/
public function pause_db_update( $force = false ) {
// Bailout.
if (
! $force &&
(
wp_doing_ajax() ||
! isset( $_GET['page'] ) ||
'give-updates' !== $_GET['page'] ||
! isset( $_GET['give-pause-db-upgrades'] ) ||
self::$background_updater->is_paused_process()
)
) {
return false;
}
delete_option( 'give_upgrade_error' );
$this->health_background_update( $this );
$batch = self::$background_updater->get_all_batch();
// Bailout: if batch is empty
if ( empty( $batch->data ) ) {
return false;
}
// Remove cache.
Give_Background_Updater::flush_cache();
// Do not stop background process immediately if task running.
// @see Give_Background_Updater::lock_process
if ( ! $force && self::$background_updater->is_process_running() ) {
update_option( 'give_pause_upgrade', 1, false );
return true;
}
update_option( 'give_paused_batches', $batch, false );
delete_option( $batch->key );
delete_site_transient( self::$background_updater->get_identifier() . '_process_lock' );
wp_clear_scheduled_hook( self::$background_updater->get_cron_identifier() );
Give()->logs->add( 'Update Pause', print_r( $batch, true ), 0, 'update' );
/**
* Fire action when pause db updates
*
* @since 2.0.1
*/
do_action( 'give_pause_db_upgrade', $this );
return true;
}
/**
* Restart db upgrade
*
* @since 4.9.0 rename function - PHP 8 compatibility
* @since 2.0.1
* @access public
*
* @return bool
*/
public function restart_db_update() {
// Bailout.
if (
wp_doing_ajax() ||
! isset( $_GET['page'] ) ||
'give-updates' !== $_GET['page'] ||
! isset( $_GET['give-restart-db-upgrades'] ) ||
! self::$background_updater->is_paused_process()
) {
return false;
}
Give_Background_Updater::flush_cache();
$batch = get_option( 'give_paused_batches' );
if ( ! empty( $batch ) ) {
wp_cache_delete( $batch->key, 'options' );
update_option( $batch->key, $batch->data, false );
delete_option( 'give_paused_batches' );
Give()->logs->add( 'Update Restart', print_r( $batch, true ), 0, 'update' );
/** Fire action when restart db updates
*
* @since 2.0.1
*/
do_action( 'give_restart_db_upgrade', $this );
self::$background_updater->dispatch();
}
return true;
}
/**
* Health check for updates.
*
* @since 4.9.0 rename function - PHP 8 compatibility
* @since 2.0
* @access public
*
* @param Give_Updates $give_updates
*/
public function health_background_update( $give_updates ) {
if ( ! $this->is_doing_updates() ) {
return;
}
Give_Background_Updater::flush_cache();
/* @var stdClass $batch */
$batch = self::$background_updater->get_all_batch();
$old_batch_update_ids = is_array( $batch->data ) ? wp_list_pluck( $batch->data, 'id' ) : [];
$all_updates = $give_updates->get_updates( 'database', 'all' );
$all_update_ids = wp_list_pluck( $all_updates, 'id' );
$all_batch_update_ids = ! empty( $batch->data ) ? wp_list_pluck( $batch->data, 'id' ) : [];
$log_data = '';
$doing_upgrade_args = get_option( 'give_doing_upgrade' );
if ( ! empty( $doing_upgrade_args ) ) {
$log_data .= 'Doing update:' . "\n";
$log_data .= print_r( $doing_upgrade_args, true ) . "\n";
}
/**
* Add remove upgrade from batch
*/
if ( ! empty( $batch->data ) ) {
foreach ( $batch->data as $index => $update ) {
$log_data = print_r( $update, true ) . "\n";
if ( ! is_callable( $update['callback'] ) ) {
$log_data .= 'Removing missing callback update: ' . "{$update['id']}\n";
unset( $batch->data[ $index ] );
} elseif ( give_has_upgrade_completed( $update['id'] ) ) {
$log_data .= 'Removing already completed update: ' . "{$update['id']}\n";
unset( $batch->data[ $index ] );
}
if ( ! empty( $update['depend'] ) ) {
foreach ( $update['depend'] as $depend ) {
if ( give_has_upgrade_completed( $depend ) ) {
$log_data .= 'Completed update: ' . "{$depend}\n";
continue;
}
if ( in_array( $depend, $all_update_ids ) && ! in_array( $depend, $all_batch_update_ids ) ) {
$log_data .= 'Adding missing update: ' . "{$depend}\n";
array_unshift( $batch->data, $all_updates[ array_search( $depend, $all_update_ids ) ] );
}
}
}
}
}
/**
* Add new upgrade to batch
*/
if ( $new_updates = $this->get_updates( 'database', 'new' ) ) {
$all_batch_update_ids = ! empty( $batch->data ) ? wp_list_pluck( $batch->data, 'id' ) : [];
foreach ( $new_updates as $index => $new_update ) {
if ( give_has_upgrade_completed( $new_update['id'] ) || in_array( $new_update['id'], $all_batch_update_ids ) ) {
unset( $new_updates[ $index ] );
}
}
if ( ! empty( $new_updates ) ) {
$log_data .= 'Adding new update: ' . "\n";
$log_data .= print_r( $new_updates, true ) . "\n";
$batch->data = array_merge( (array) $batch->data, $new_updates );
update_option( 'give_db_update_count', ( absint( get_option( 'give_db_update_count' ) ) + count( $new_updates ) ), false );
}
}
/**
* Fix batch
*/
if ( empty( $batch->data ) ) {
// Complete batch if do not have any data to process.
self::$background_updater->delete( $batch->key );
if ( self::$background_updater->has_queue() ) {
$this->health_background_update( $this );
} else {
delete_site_transient( self::$background_updater->get_identifier() . '_process_lock' );
wp_clear_scheduled_hook( self::$background_updater->get_cron_identifier() );
self::$background_updater->complete();
}
} elseif ( array_diff( wp_list_pluck( $batch->data, 'id' ), $old_batch_update_ids ) ) {
$log_data .= 'Updating batch' . "\n";
$log_data .= print_r( $batch, true );
if ( ! empty( $batch->key ) ) {
wp_cache_delete( $batch->key, 'options' );
update_option( $batch->key, $batch->data, false );
} else {
foreach ( $batch->data as $data ) {
self::$background_updater->push_to_queue( $data );
}
self::$background_updater->save();
}
}
/**
* Fix give_doing_upgrade option
*/
if ( $fresh_new_db_count = $this->get_total_new_db_update_count( true ) ) {
update_option( 'give_db_update_count', $fresh_new_db_count, false );
}
$doing_upgrade_args['update'] = 1;
$doing_upgrade_args['heading'] = sprintf( 'Update %s of %s', 1, $fresh_new_db_count );
$doing_upgrade_args['total_percentage'] = $this->get_db_update_processing_percentage( true );
// Remove already completed update from info.
if (
empty( $doing_upgrade_args['update_info'] )
|| give_has_upgrade_completed( $doing_upgrade_args['update_info']['id'] )
) {
$doing_upgrade_args['update_info'] = current( array_values( $batch->data ) );
$doing_upgrade_args['step'] = 1;
}
// Check if dependency completed or not.
if ( isset( $doing_upgrade_args['update_info']['depend'] ) ) {
foreach ( $doing_upgrade_args['update_info']['depend'] as $depend ) {
if ( give_has_upgrade_completed( $depend ) ) {
continue;
}
$doing_upgrade_args['update_info'] = $all_updates[ array_search( $depend, $all_update_ids ) ];
$doing_upgrade_args['step'] = 1;
$doing_upgrade_args['percentage'] = 0;
$doing_upgrade_args['total_percentage'] = 0;
break;
}
}
if ( ! empty( $doing_upgrade_args['update_info'] ) ) {
update_option( 'give_doing_upgrade', $doing_upgrade_args, false );
$log_data .= 'Updated doing update:' . "\n";
$log_data .= print_r( $doing_upgrade_args, true ) . "\n";
}
Give()->logs->add( 'Update Health Check', $log_data, 0, 'update' );
}
/**
* Show update related notices
*
* @since 4.9.0 rename function - PHP 8 compatibility
* @since 2.0
* @access public
*/
public function show_notice() {
$current_screen = get_current_screen();
$hide_on_pages = [
'give_forms_page_give-updates',
'update-core',
'give_forms_page_give-addons',
];
// Bailout.
if ( ! current_user_can( 'manage_give_settings' ) ) {
return;
}
// Run DB updates.
if ( ! empty( $_GET['give-run-db-update'] ) ) {
$this->run_db_update();
}
// Bailout.
if ( in_array( $current_screen->base, $hide_on_pages ) ) {
return;
}
// Show notice if upgrade paused.
if ( self::$background_updater->is_paused_process() ) {
ob_start();
$upgrade_error = get_option( 'give_upgrade_error' );
if ( ! $upgrade_error ) : ?>
–
–
»
notices->register_notice(
[
'id' => 'give_upgrade_db',
'type' => 'error',
'dismissible' => false,
'description' => $desc_html,
]
);
}
// Bailout if doing upgrades.
if ( $this->is_doing_updates() ) {
return;
}
// Show db upgrade completed notice.
if ( ! empty( $_GET['give-db-update-completed'] ) ) {
Give()->notices->register_notice(
[
'id' => 'give_db_upgrade_completed',
'type' => 'updated',
'description' => __( 'GiveWP database updates completed successfully. Thank you for updating to the latest version!', 'give' ),
'show' => true,
]
);
// Start update.
} elseif ( ! empty( $_GET['give-run-db-update'] ) ) {
$this->run_db_update();
// Show run the update notice.
} elseif ( $this->get_total_new_db_update_count() ) {
ob_start();
?>
–
notices->register_notice( [ 'id' => 'give_upgrade_db', 'type' => 'updated', 'dismissible' => false, 'description' => $desc_html, ] ); } } /** * Render Give Updates Completed page * * @since 1.8.12 * @access public */ public function render_complete_page() { include_once GIVE_PLUGIN_DIR . 'includes/admin/upgrades/views/upgrades-complete.php'; } /** * Render Give Updates page * * @since 1.8.12 * @access public */ public function render_page() { include_once GIVE_PLUGIN_DIR . 'includes/admin/upgrades/views/upgrades.php'; } /** * Run database upgrades * * @since 2.0 * @access private */ private function run_db_update() { // Bailout. if ( $this->is_doing_updates() || ! $this->get_total_new_db_update_count() ) { return; } $updates = $this->get_updates( 'database', 'new' ); foreach ( $updates as $update ) { self::$background_updater->push_to_queue( $update ); } add_option( 'give_db_update_count', count( $updates ), '', false ); add_option( 'give_doing_upgrade', [ 'update_info' => $updates[0], 'step' => 1, 'update' => 1, 'heading' => sprintf( 'Update %s of %s', 1, count( $updates ) ), 'percentage' => 0, 'total_percentage' => 0, ], '', false ); self::$background_updater->save()->dispatch(); } /** * Delete resume updates * * @since 4.9.0 rename function - PHP 8 compatibility * @since 1.8.12 * @access public */ public function flush_resume_updates() { $this->step = $this->percentage = 0; $this->update = ( $this->get_total_db_update_count() > $this->update ) ? ( $this->update + 1 ) : $this->update; } /** * Initialize updates * * @since 4.9.0 rename function - PHP 8 compatibility * @since 2.0 * @access public * * @return void */ public function give_start_updating() { // Check permission. if ( ! current_user_can( 'manage_give_settings' ) || $this->is_doing_updates() ) { // Run update via ajax self::$background_updater->dispatch(); wp_send_json_error(); } // @todo: validate nonce // @todo: set http method to post if ( empty( $_POST['run_db_update'] ) ) { wp_send_json_error(); } $this->run_db_update(); wp_send_json_success(); } /** * This function handle ajax query for dn update status. * * @since 4.9.0 rename function - PHP 8 compatibility * @since 2.0 * @access public * * @return string */ public function give_db_updates_info() { // Check permission. if ( ! current_user_can( 'manage_give_settings' ) ) { give_die(); } $update_info = get_option( 'give_doing_upgrade' ); $response_type = ''; if ( self::$background_updater->is_paused_process() ) { $update_info = [ 'message' => __( 'The updates have been paused.', 'give' ), 'heading' => '', 'percentage' => 0, ]; if ( get_option( 'give_upgrade_error' ) ) { $update_info['message'] = __( 'An unexpected issue occurred during the database update which caused it to stop automatically. Please contact support for assistance.', 'give' ); } $response_type = 'error'; } elseif ( empty( $update_info ) || ! $this->get_total_new_db_update_count( true ) ) { $update_info = [ 'message' => __( 'GiveWP database updates completed successfully. Thank you for updating to the latest version!', 'give' ), 'heading' => __( 'Updates Completed.', 'give' ), 'percentage' => 0, ]; $response_type = 'success'; delete_option( 'give_show_db_upgrade_complete_notice' ); } $this->send_ajax_response( $update_info, $response_type ); } /** * Send ajax response * * @since 1.8.12 * @access public * * @param $data * @param string $type */ public function send_ajax_response( $data, $type = '' ) { $default = [ 'message' => '', 'heading' => '', 'percentage' => 0, 'step' => 0, 'update' => 0, ]; // Set data. $data = wp_parse_args( $data, $default ); // Enable cache. Give_Cache::enable(); switch ( $type ) { case 'success': wp_send_json_success( $data ); break; case 'error': wp_send_json_error( $data ); break; default: wp_send_json( [ 'data' => $data, ] ); break; } } /** * Set current update percentage. * * @since 1.8.12 * @access public * * @param $total * @param $current_total */ public function set_percentage( $total, $current_total ) { // Set percentage. $this->percentage = $total ? ( ( $current_total ) / $total ) * 100 : 0; // Verify percentage. $this->percentage = ( 100 < $this->percentage ) ? 100 : $this->percentage; } /** * Check if parent update completed or not. * * @since 2.0 * @access private * * @param array $update * * @return bool|null */ public function is_parent_updates_completed( $update ) { // Bailout. if ( empty( $update['depend'] ) ) { return true; } // Check if dependency is valid or not. if ( ! $this->has_valid_dependency( $update ) ) { return null; } $is_dependency_completed = true; foreach ( $update['depend'] as $depend ) { if ( ! give_has_upgrade_completed( $depend ) ) { $is_dependency_completed = false; break; } } return $is_dependency_completed; } /** * Flag to check if DB updates running or not. * * @since 2.0 * @access public * @return bool */ public function is_doing_updates() { return (bool) Give_Cache_Setting::get_option( 'give_doing_upgrade' ); } /** * Check if update has valid dependency or not. * * @since 2.0 * @access public * * @param $update * * @return bool */ public function has_valid_dependency( $update ) { $is_valid_dependency = true; // $update_ids = wp_list_pluck( $this->get_updates( 'database', 'all' ), 'id' ); // // foreach ( $update['depend'] as $depend ) { // Check if dependency is valid or not. // if ( ! in_array( $depend, $update_ids ) ) { // $is_valid_dependency = false; // break; // } // } return $is_valid_dependency; } /** * Get updates. * * @since 1.8.12 * @access public * * @param string $update_type Tye of update. * @param string $status Tye of update. * * @return array */ public function get_updates( $update_type = '', $status = 'all' ) { // return all updates. if ( empty( $update_type ) ) { return $this->updates; } // Get specific update. $updates = ! empty( $this->updates[ $update_type ] ) ? $this->updates[ $update_type ] : []; // Bailout. if ( empty( $updates ) ) { return $updates; } switch ( $status ) { case 'new': // Remove already completed updates. wp_cache_delete( 'give_completed_upgrades', 'options' ); $completed_updates = give_get_completed_upgrades(); if ( ! empty( $completed_updates ) ) { foreach ( $updates as $index => $update ) { if ( in_array( $update['id'], $completed_updates ) ) { unset( $updates[ $index ] ); } } $updates = array_values( $updates ); } break; } return $updates; } /** * Get addon update count. * * @since 1.8.12 * @access public * @return int */ public function get_total_plugin_update_count() { return count( $this->get_updates( 'plugin' ) ); } /** * Get total update count * * @since 1.8.12 * @access public * * @return int */ public function get_total_update_count() { $db_update_count = $this->get_pending_db_update_count(); $plugin_update_count = $this->get_total_plugin_update_count(); return ( $db_update_count + $plugin_update_count ); } /** * Get total pending updates count * * @since 1.8.12 * @access public * * @return int */ public function get_pending_db_update_count() { return count( $this->get_updates( 'database', 'new' ) ); } /** * Get total updates count * * @since 1.8.18 * @access public * * @return int */ public function get_total_db_update_count() { return count( $this->get_updates( 'database', 'all' ) ); } /** * Get total new updates count * * @since 2.0 * @access public * * @param bool $refresh * * @return int */ public function get_total_new_db_update_count( $refresh = false ) { $update_count = $this->is_doing_updates() && ! $refresh ? get_option( 'give_db_update_count' ) : $this->get_pending_db_update_count(); return $update_count; } /** * Get total new updates count * * @since 2.0 * @access public * * @param bool $refresh * * @return int */ public function get_running_db_update( $refresh = false ) { $current_update = 1; if ( $this->is_doing_updates() && ! $refresh ) { $current_update = get_option( 'give_doing_upgrade' ); $current_update = $current_update['update']; } return $current_update; } /** * Get database update processing percentage. * * @since 2.0 * @access public * * @param bool $refresh * * @return float|int */ public function get_db_update_processing_percentage( $refresh = false ) { // Bailout. if ( ! $this->get_total_new_db_update_count( $refresh ) ) { return 0; } $resume_update = get_option( 'give_doing_upgrade' ); $update_count_percentages = ( ( $this->get_running_db_update( $refresh ) - 1 ) / $this->get_total_new_db_update_count( $refresh ) ) * 100; $update_percentage_share = ( 1 / $this->get_total_new_db_update_count() ) * 100; $upgrade_percentage = ( ( $resume_update['percentage'] * $update_percentage_share ) / 100 ); $final_percentage = $update_count_percentages + $upgrade_percentage; return $this->is_doing_updates() ? ( absint( $final_percentage ) ? absint( $final_percentage ) : round( $final_percentage, 2 ) ) : 0; } /** * Get all update ids. * * @since 2.0.3 * * @return array */ public function get_update_ids() { $all_updates = $this->get_updates( 'database', 'all' ); $all_update_ids = wp_list_pluck( $all_updates, 'id' ); return $all_update_ids; } /** * Get offset count * * @since 2.0.5 * @access public * * @param int $process_item_count * * @return float|int */ public function get_offset( $process_item_count ) { return ( 1 === $this->step ) ? 0 : ( $this->step - 1 ) * $process_item_count; } } Give_Updates::get_instance()->setup();