'booking_version_num',
'option-is_delete_if_deactive' => 'booking_is_delete_if_deactive',
'option-activation_process' => 'booking_activation_process',
'transient-wpbc_activation_redirect' => '_booking_activation_redirect',
'message-delete_data' => 'Warning! All plugin data will be deleted when plugin had deactivated. ' . sprintf( 'If you want to save your plugin data, please uncheck the %s"Delete plugin data"%s at the', '', '' ),
'link_settings' => 'Settings',
'link_whats_new' => 'Whats New',
);
$init_option = $this->get_init_option_names();
$this->init_option = wp_parse_args( $init_option, $default_init_option_names );
// WordPress > Plugins > "Activate" link.
register_activation_hook( WPBC_FILE, array( $this, 'wpbc_activate_initial' ) );
// WordPress > Plugins > "Deactivate" link.
register_deactivation_hook( WPBC_FILE, array( $this, 'wpbc_deactivate' ) );
// Upgrade during bulk upgrade of plugins.
add_filter( 'upgrader_post_install', array( $this, 'wpbc_install_in_bulk_upgrade' ), 10, 2 );
// Settings link at the plugin page.
add_filter( 'plugin_action_links', array( $this, 'plugin_links' ), 10, 2 );
// Warning message in plugin info.
add_filter( 'plugin_row_meta', array( $this, 'plugin_row_meta' ), 10, 4 );
// TODO: 2025-06-06 - required to be commented, if we use the Pro version as separate plugin.
$this->check_if_need_to_update(); // Check upgrade, if was no activation process
}
/**
* Must be overloaded in child CLASS
*
* * Important! for correct loading of trasnaltions later, we must do not use here loacale of plugin. So here will be untranslated strings!!!
*
* Exmaple:
* return array(
'option-version_num' => 'booking_version_num'
, 'option-is_delete_if_deactive' => 'booking_is_delete_if_deactive'
, 'option-activation_process' => 'booking_activation_process'
, 'transient-wpbc_activation_redirect' => '_booking_activation_redirect'
, 'message-delete_data' => 'Warning !!! ' . 'All plugin data will be deleted when plugin had deactivated.' . ' '
. sprintf( 'If you want to save your plugin data, please uncheck the %s"Delete plugin data"%s at the settings page.', '', '')
, 'link_settings' => 'Settings'
, 'link_whats_new' => 'Whats New'
);
*/
abstract function get_init_option_names();
/**
* Must be overloaded in child CLASS
* Exmaple:
*
return false
*/
abstract function is_update_from_lower_to_high_version();
// -----------------------------------------------------------------------------------------------------------------
//
/** Update info of plugin at the plugins section */
function plugin_row_meta( $plugin_meta, $plugin_file, $plugin_data, $context ) {
$this_plugin = plugin_basename( WPBC_FILE );
if ($plugin_file == $this_plugin ) {
$is_delete_if_deactive = get_bk_option( $this->init_option['option-is_delete_if_deactive'] ); // check
if ($is_delete_if_deactive == 'On') { ?>
init_option['message-delete_data']; ?>
Free + ' . ucfirst( $version ) . '';
} else {
$plugin_meta[0] .= ' | ' . __( 'Version type', 'booking' ) . ': ' . ucfirst( $version ) . '';
}
/*
[$plugin_meta] => Array
(
[0] => Version 2.8.35
[1] => By wpdevelop
[2] => Visit plugin site
)
[$plugin_file] => booking/WPBC.php
[$plugin_data] => Array
(
[Name] => Booking Calendar
[PluginURI] => https://wpbookingcalendar.com/demo/
[Version] => 2.8.35
[Description] => Online booking and availability checking service for your site.
[Author] => wpdevelop
[AuthorURI] => https://wpbookingcalendar.com/
[TextDomain] =>
[DomainPath] =>
[Network] =>
[Title] => Booking Calendar
[AuthorName] => wpdevelop
)
[$context] => all
/**/
// Echo plugin description here
return $plugin_meta;
} else
return $plugin_meta;
}
// Adds Settings link to plugins settings
function plugin_links($links, $file) {
$this_plugin = plugin_basename( WPBC_FILE );
if ( $file == $this_plugin ) {
array_unshift( $links, $this->init_option['link_settings'] );
//array_unshift( $links, $this->init_option['link_whats_new'] );
array_unshift( $links, $this->init_option['link_faq'] );
$links = array_merge( $links, array(
'' . esc_attr__( 'Start Setup Wizard', 'booking' ) . ''
)
);
if ( ! class_exists( 'wpdev_bk_personal' ) ) {
if ( wpbc_is_updated_paid_to_free() ) {
array_unshift(
$links,
'' . esc_attr__( 'Request Pro Version', 'booking' ) . ''
);
} else {
array_unshift( $links, $this->init_option['link_up'] );
}
} else {
if ( ! class_exists( 'wpdev_bk_multiuser' ) ) {
array_unshift( $links, $this->init_option['link_upgrade'] );
}
}
}
return $links;
}
//
// -----------------------------------------------------------------------------------------------------------------
/**
* Check about ability to upgrade, if was no activation process
*
* @return void
*/
private function check_if_need_to_update() {
if ( is_admin() ) {
$wpbc_version_num = get_option( $this->init_option['option-version_num'] );
if ( false === $wpbc_version_num ) {
$wpbc_version_num = '0';
}
$is_make_activation = false;
if ( version_compare( WP_BK_VERSION_NUM, $wpbc_version_num ) > 0 ) {
$is_make_activation = true;
} else {
// Check if we was update from free to paid or from lower to higher versions, and do not make normal activation. In this case we need to make update.
// TODO: 2025-11-23 - Commented, if we use the Pro version as separate plugin.
// $is_make_activation = $this->is_update_from_lower_to_high_version();
}
// Run the installed version's activation on the next request after files change.
if ( $is_make_activation ) {
add_action( 'init', array( $this, 'wpbc_activate_detected_update' ), 1030 );
}
}
}
/**
* Run activation work after WordPress successfully replaces this plugin.
*
* WordPress continues running the previously loaded PHP code during the
* updater request. The completed-update context bridges that request to the
* newly installed code so the next request can distinguish an interactive
* Plugins-screen update from a non-interactive updater.
*
* @param bool|WP_Error $return_val Installation result.
* @param array $hook_extra WordPress upgrader context.
*
* @return bool|WP_Error Unchanged installation result.
*/
public function wpbc_install_in_bulk_upgrade( $return_val, $hook_extra ) {
if ( is_wp_error( $return_val ) ) {
return $return_val;
}
$updated_plugin = isset( $hook_extra['plugin'] ) && is_scalar( $hook_extra['plugin'] )
? plugin_basename( sanitize_text_field( (string) $hook_extra['plugin'] ) )
: '';
if ( plugin_basename( WPBC_FILE ) === $updated_plugin ) {
$this->wpbc_activate();
if ( class_exists( 'WPBC_Setup_Wizard_First_Install_State' ) ) {
WPBC_Setup_Wizard_First_Install_State::record_completed_plugin_update( WPBC_FILE, WP_BK_VERSION_NUM );
}
}
return $return_val;
}
/**
* User clicked on "Activate" link at Plugins Menu.
*
* Genuine-install state is captured before activation creates canonical
* tables and options. A dedicated versioned intent lets the following request
* distinguish first-install onboarding from update/reactivation What's New,
* while the released boolean redirect transient remains backward compatible.
*
* @return void
*/
public function wpbc_activate_initial() {
$is_initial_install = function_exists( 'wpbc_is_plugin_initial_install' )
? wpbc_is_plugin_initial_install()
: false;
// Activate the plugin.
$this->wpbc_activate();
$this->wpbc_schedule_activation_redirect( $is_initial_install, 'activation' );
}
/**
* Run activation after the installed plugin version changes.
*
* A matching updater handoff suppresses navigation for automatic and other
* non-interactive updates, while an interactive update opens What's New. When
* no handoff exists, the files were replaced outside the standard upgrader
* and retain the released one-time What's New behavior.
*
* @return void
*/
public function wpbc_activate_detected_update() {
$update_context = class_exists( 'WPBC_Setup_Wizard_First_Install_State' )
? WPBC_Setup_Wizard_First_Install_State::get_plugin_update_context( WPBC_FILE, WP_BK_VERSION_NUM )
: false;
$this->wpbc_activate();
if ( class_exists( 'WPBC_Setup_Wizard_First_Install_State' ) ) {
WPBC_Setup_Wizard_First_Install_State::clear_plugin_update_context( WPBC_FILE );
}
if (
is_array( $update_context )
&& WPBC_Setup_Wizard_First_Install_State::UPDATE_SOURCE_SUPPRESSED === $update_context['source']
) {
return;
}
$is_manual_update = is_array( $update_context )
&& WPBC_Setup_Wizard_First_Install_State::UPDATE_SOURCE_MANUAL === $update_context['source'];
$redirect_user_id = $is_manual_update && isset( $update_context['user_id'] ) ? absint( $update_context['user_id'] ) : null;
$this->wpbc_schedule_activation_redirect(
false,
$is_manual_update ? 'manual_update' : 'detected_file_update',
$redirect_user_id,
$is_manual_update ? WPBC_Setup_Wizard_First_Install_State::MANUAL_UPDATE_INTENT_TTL : 30,
! $is_manual_update
);
}
/**
* Schedule one allowed post-activation navigation intent.
*
* @param bool $is_initial_install Whether activation began from an empty installation.
* @param string $source Activation or update source identifier.
* @param int|null $user_id User who should receive the redirect, or null for the current user.
* @param int $intent_ttl Structured redirect lifetime in seconds.
* @param bool $write_legacy Whether to retain the released boolean redirect for older consumers.
*
* @return void
*/
private function wpbc_schedule_activation_redirect( $is_initial_install, $source, $user_id = null, $intent_ttl = 30, $write_legacy = true ) {
$is_restricted = class_exists( 'WPBC_Setup_Wizard_First_Install_State' )
? WPBC_Setup_Wizard_First_Install_State::is_automatic_onboarding_restricted()
: (
is_network_admin()
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only activation context detection.
|| isset( $_GET['activate-multi'] )
|| wpbc_is_this_demo()
|| ( defined( 'WPBC_IS_PLAYGROUND' ) && true === WPBC_IS_PLAYGROUND )
|| defined( 'IFRAME_REQUEST' )
);
if ( $is_restricted ) {
return;
}
if ( class_exists( 'WPBC_Setup_Wizard_First_Install_State' ) ) {
$redirect_intent = WPBC_Setup_Wizard_First_Install_State::create_activation_redirect_intent(
$is_initial_install,
WP_BK_VERSION_NUM,
$source,
$user_id
);
set_transient(
WPBC_Setup_Wizard_First_Install_State::ACTIVATION_INTENT_TRANSIENT,
$redirect_intent,
absint( $intent_ttl )
);
}
if ( $write_legacy ) {
// Keep the released boolean payload for older paid editions and Welcome consumers.
set_transient( $this->init_option['transient-wpbc_activation_redirect'], true, 30 );
}
}
// -----------------------------------------------------------------------------------------------------------------
/**
* Run Activate
*/
public function wpbc_activate() {
WPBC_Action_Scheduler_Compatibility::raise_memory_limit();
WPBC_Action_Scheduler_Compatibility::raise_time_limit( 300 );
update_bk_option( $this->init_option['option-activation_process'], 'On' );
make_bk_action( 'wpbc_activation' ); // S T A R T.
update_bk_option( $this->init_option['option-version_num'], WP_BK_VERSION_NUM );
update_bk_option( $this->init_option['option-activation_process'], 'Off' );
}
/**
* Run Deactivate
*/
public function wpbc_deactivate() {
wpbc_set_plugin_deactivation_state( true );
WPBC_Action_Scheduler_Compatibility::raise_memory_limit();
WPBC_Action_Scheduler_Compatibility::raise_time_limit( 300 );
$is_delete_if_deactive = get_bk_option( $this->init_option['option-is_delete_if_deactive'] ); // check.
if ( 'On' === $is_delete_if_deactive ) {
make_bk_action( 'wpbc_deactivation' ); // F I N I S H.
delete_bk_option( $this->init_option['option-version_num'] );
delete_bk_option( $this->init_option['option-activation_process'] );
}
}
}