| 1 |
<?php |
| 2 |
/** |
| 3 |
* Yatra Pro < 3.0 is incompatible with Yatra Free 3.0+ (removed / renamed classes such as Yatra_Form). |
| 4 |
* When both are active, old Pro can fatal before Free runs if Pro loads first in the active-plugins list. |
| 5 |
* |
| 6 |
* This file: |
| 7 |
* 1. Reorders active plugins so yatra/yatra.php loads before yatra-pro/yatra-pro.php when both are active. |
| 8 |
* 2. Deactivates old Pro (version < 3.0.0) when Free 3.0+ is running, and stores an admin notice. |
| 9 |
* |
| 10 |
* @package Yatra |
| 11 |
*/ |
| 12 |
|
| 13 |
declare(strict_types=1); |
| 14 |
|
| 15 |
if (!defined('ABSPATH')) { |
| 16 |
exit; |
| 17 |
} |
| 18 |
|
| 19 |
/** |
| 20 |
* Minimum Yatra Pro version that works with Yatra Free 3.0.x. |
| 21 |
*/ |
| 22 |
function yatra_get_minimum_compatible_pro_version(): string |
| 23 |
{ |
| 24 |
return '3.0.0'; |
| 25 |
} |
| 26 |
|
| 27 |
/** |
| 28 |
* Where customers download Yatra Pro (account / downloads). |
| 29 |
*/ |
| 30 |
function yatra_get_compatible_pro_account_url(): string |
| 31 |
{ |
| 32 |
return 'https://store.mantrabrain.com/account'; |
| 33 |
} |
| 34 |
|
| 35 |
/** |
| 36 |
* Normalize plugin header versions for comparison. PHP's version_compare treats "3.0" as older than "3.0.0", |
| 37 |
* so Pro 3.0 was incorrectly flagged as incompatible with minimum 3.0.0. |
| 38 |
* |
| 39 |
* @param string $version Raw Version header value. |
| 40 |
*/ |
| 41 |
function yatra_normalize_version_for_compare(string $version): string |
| 42 |
{ |
| 43 |
$version = trim($version); |
| 44 |
if ($version === '') { |
| 45 |
return '0.0.0'; |
| 46 |
} |
| 47 |
|
| 48 |
if (preg_match('/^(\d+(?:\.\d+)*)/', $version, $m)) { |
| 49 |
$parts = array_map('intval', explode('.', $m[1])); |
| 50 |
while (count($parts) < 3) { |
| 51 |
$parts[] = 0; |
| 52 |
} |
| 53 |
|
| 54 |
return implode('.', array_slice($parts, 0, 3)); |
| 55 |
} |
| 56 |
|
| 57 |
return $version; |
| 58 |
} |
| 59 |
|
| 60 |
/** |
| 61 |
* Whether installed Yatra Pro version meets the minimum for Yatra Free 3.0+. |
| 62 |
*/ |
| 63 |
function yatra_is_pro_version_compatible(string $pro_version): bool |
| 64 |
{ |
| 65 |
$pro = yatra_normalize_version_for_compare($pro_version); |
| 66 |
$min = yatra_normalize_version_for_compare(yatra_get_minimum_compatible_pro_version()); |
| 67 |
|
| 68 |
return version_compare($pro, $min, '>='); |
| 69 |
} |
| 70 |
|
| 71 |
/** |
| 72 |
* Put Yatra Free and Yatra Pro at the start of the active list, in that order, so Free always boots before Pro. |
| 73 |
* |
| 74 |
* @return bool True if the option was updated. |
| 75 |
*/ |
| 76 |
function yatra_normalize_yatra_plugins_active_order(): bool |
| 77 |
{ |
| 78 |
if (!function_exists('get_option') || !function_exists('update_option')) { |
| 79 |
return false; |
| 80 |
} |
| 81 |
|
| 82 |
$active = get_option('active_plugins', []); |
| 83 |
if (!is_array($active) || $active === []) { |
| 84 |
return false; |
| 85 |
} |
| 86 |
|
| 87 |
$free = 'yatra/yatra.php'; |
| 88 |
$pro = 'yatra-pro/yatra-pro.php'; |
| 89 |
|
| 90 |
$has_free = in_array($free, $active, true); |
| 91 |
$has_pro = in_array($pro, $active, true); |
| 92 |
|
| 93 |
if (!$has_free || !$has_pro) { |
| 94 |
return false; |
| 95 |
} |
| 96 |
|
| 97 |
$rest = array_values(array_diff($active, [$free, $pro])); |
| 98 |
$merged = array_merge([$free, $pro], $rest); |
| 99 |
|
| 100 |
if ($merged === array_values($active)) { |
| 101 |
return false; |
| 102 |
} |
| 103 |
|
| 104 |
update_option('active_plugins', $merged); |
| 105 |
|
| 106 |
return true; |
| 107 |
} |
| 108 |
|
| 109 |
/** |
| 110 |
* If Yatra Free 3.0+ is active and an old Yatra Pro (< 3.0.0) is active, deactivate Pro and queue an admin notice. |
| 111 |
*/ |
| 112 |
function yatra_deactivate_incompatible_old_pro(): void |
| 113 |
{ |
| 114 |
if (!defined('YATRA_VERSION')) { |
| 115 |
return; |
| 116 |
} |
| 117 |
|
| 118 |
if (version_compare(YATRA_VERSION, '3.0.0', '<')) { |
| 119 |
return; |
| 120 |
} |
| 121 |
|
| 122 |
if (!function_exists('get_plugin_data')) { |
| 123 |
require_once ABSPATH . 'wp-admin/includes/plugin.php'; |
| 124 |
} |
| 125 |
|
| 126 |
$pro_file = WP_PLUGIN_DIR . '/yatra-pro/yatra-pro.php'; |
| 127 |
if (!is_readable($pro_file)) { |
| 128 |
return; |
| 129 |
} |
| 130 |
|
| 131 |
$data = get_plugin_data($pro_file, false, false); |
| 132 |
$pro_version = isset($data['Version']) ? trim((string) $data['Version']) : '0'; |
| 133 |
|
| 134 |
if ($pro_version === '' || yatra_is_pro_version_compatible($pro_version)) { |
| 135 |
return; |
| 136 |
} |
| 137 |
|
| 138 |
if (!function_exists('is_plugin_active')) { |
| 139 |
require_once ABSPATH . 'wp-admin/includes/plugin.php'; |
| 140 |
} |
| 141 |
|
| 142 |
if (!is_plugin_active('yatra-pro/yatra-pro.php')) { |
| 143 |
return; |
| 144 |
} |
| 145 |
|
| 146 |
deactivate_plugins('yatra-pro/yatra-pro.php', true); |
| 147 |
|
| 148 |
set_transient( |
| 149 |
'yatra_admin_notice_incompatible_pro_deactivated', |
| 150 |
[ |
| 151 |
'pro_version' => $pro_version, |
| 152 |
'min_required' => yatra_get_minimum_compatible_pro_version(), |
| 153 |
], |
| 154 |
WEEK_IN_SECONDS |
| 155 |
); |
| 156 |
} |
| 157 |
|
| 158 |
/** |
| 159 |
* Run guard once this file is loaded (yatra.php included after constants). |
| 160 |
*/ |
| 161 |
function yatra_run_incompatible_pro_guard(): void |
| 162 |
{ |
| 163 |
yatra_normalize_yatra_plugins_active_order(); |
| 164 |
yatra_deactivate_incompatible_old_pro(); |
| 165 |
} |
| 166 |
|
| 167 |
/** |
| 168 |
* Admin notice after we deactivated incompatible Pro. |
| 169 |
*/ |
| 170 |
function yatra_admin_notice_incompatible_pro_deactivated(): void |
| 171 |
{ |
| 172 |
if (!is_admin() || !current_user_can('activate_plugins')) { |
| 173 |
return; |
| 174 |
} |
| 175 |
|
| 176 |
$payload = get_transient('yatra_admin_notice_incompatible_pro_deactivated'); |
| 177 |
if (!is_array($payload)) { |
| 178 |
return; |
| 179 |
} |
| 180 |
|
| 181 |
$pro_v = isset($payload['pro_version']) ? sanitize_text_field((string) $payload['pro_version']) : ''; |
| 182 |
$min_v = isset($payload['min_required']) ? sanitize_text_field((string) $payload['min_required']) : yatra_get_minimum_compatible_pro_version(); |
| 183 |
|
| 184 |
$dismiss_url = wp_nonce_url( |
| 185 |
add_query_arg('yatra_dismiss_pro_notice', '1'), |
| 186 |
'yatra_dismiss_pro_notice' |
| 187 |
); |
| 188 |
|
| 189 |
?> |
| 190 |
<div class="notice notice-error is-dismissible" data-yatra-pro-notice="1"> |
| 191 |
<p> |
| 192 |
<strong><?php esc_html_e('Yatra Pro was deactivated', 'yatra'); ?></strong> |
| 193 |
</p> |
| 194 |
<p> |
| 195 |
<?php |
| 196 |
printf( |
| 197 |
esc_html( |
| 198 |
/* translators: 1: detected Yatra Pro version, 2: minimum required Pro version, 3: current Yatra Free version. */ |
| 199 |
__( |
| 200 |
'Your site had Yatra Pro %1$s active together with Yatra %3$s. Pro versions older than %2$s are not compatible with Yatra 3.0 and were causing fatal errors (missing classes such as Yatra_Form). Yatra Pro has been deactivated automatically.', |
| 201 |
'yatra' |
| 202 |
) |
| 203 |
), |
| 204 |
esc_html($pro_v !== '' ? $pro_v : '?'), |
| 205 |
esc_html($min_v), |
| 206 |
esc_html(defined('YATRA_VERSION') ? YATRA_VERSION : '3.0') |
| 207 |
); |
| 208 |
?> |
| 209 |
</p> |
| 210 |
<p> |
| 211 |
<?php |
| 212 |
$account_url = yatra_get_compatible_pro_account_url(); |
| 213 |
printf( |
| 214 |
wp_kses_post( |
| 215 |
/* translators: 1: minimum Yatra Pro version, 2: HTML link to MantraBrain store account. */ |
| 216 |
__('Please download and install <strong>Yatra Pro %1$s or newer</strong> from your MantraBrain store account: %2$s to use Pro features with this version of Yatra.', 'yatra') |
| 217 |
), |
| 218 |
esc_html($min_v), |
| 219 |
sprintf( |
| 220 |
'<a href="%1$s" target="_blank" rel="noopener noreferrer">%2$s</a>', |
| 221 |
esc_url($account_url), |
| 222 |
esc_html($account_url) |
| 223 |
) |
| 224 |
); |
| 225 |
?> |
| 226 |
</p> |
| 227 |
<p> |
| 228 |
<a href="<?php echo esc_url($dismiss_url); ?>" class="button"> |
| 229 |
<?php esc_html_e('Dismiss this notice', 'yatra'); ?> |
| 230 |
</a> |
| 231 |
</p> |
| 232 |
</div> |
| 233 |
<?php |
| 234 |
} |
| 235 |
|
| 236 |
/** |
| 237 |
* Dismiss the notice (clears transient). |
| 238 |
*/ |
| 239 |
function yatra_handle_dismiss_incompatible_pro_notice(): void |
| 240 |
{ |
| 241 |
if (!is_admin() || !isset($_GET['yatra_dismiss_pro_notice']) || !current_user_can('activate_plugins')) { |
| 242 |
return; |
| 243 |
} |
| 244 |
|
| 245 |
if (!isset($_GET['_wpnonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['_wpnonce'])), 'yatra_dismiss_pro_notice')) { |
| 246 |
return; |
| 247 |
} |
| 248 |
|
| 249 |
delete_transient('yatra_admin_notice_incompatible_pro_deactivated'); |
| 250 |
wp_safe_redirect(remove_query_arg(['yatra_dismiss_pro_notice', '_wpnonce'])); |
| 251 |
exit; |
| 252 |
} |
| 253 |
|
| 254 |
/** |
| 255 |
* Remove the "Pro was deactivated" notice once installed Yatra Pro meets the minimum (e.g. header "3.0" vs minimum "3.0.0"). |
| 256 |
*/ |
| 257 |
function yatra_clear_stale_pro_incompatible_notice(): void |
| 258 |
{ |
| 259 |
if (!is_admin()) { |
| 260 |
return; |
| 261 |
} |
| 262 |
|
| 263 |
if (!get_transient('yatra_admin_notice_incompatible_pro_deactivated')) { |
| 264 |
return; |
| 265 |
} |
| 266 |
|
| 267 |
if (!function_exists('get_plugin_data')) { |
| 268 |
require_once ABSPATH . 'wp-admin/includes/plugin.php'; |
| 269 |
} |
| 270 |
|
| 271 |
$pro_file = WP_PLUGIN_DIR . '/yatra-pro/yatra-pro.php'; |
| 272 |
if (!is_readable($pro_file)) { |
| 273 |
return; |
| 274 |
} |
| 275 |
|
| 276 |
$data = get_plugin_data($pro_file, false, false); |
| 277 |
$v = isset($data['Version']) ? trim((string) $data['Version']) : '0'; |
| 278 |
|
| 279 |
if ($v !== '' && yatra_is_pro_version_compatible($v)) { |
| 280 |
delete_transient('yatra_admin_notice_incompatible_pro_deactivated'); |
| 281 |
} |
| 282 |
} |
| 283 |
|
| 284 |
add_action('admin_init', 'yatra_clear_stale_pro_incompatible_notice', 0); |
| 285 |
add_action('admin_notices', 'yatra_admin_notice_incompatible_pro_deactivated', 1); |
| 286 |
add_action('admin_init', 'yatra_handle_dismiss_incompatible_pro_notice', 1); |
| 287 |
|
| 288 |
/** |
| 289 |
* After Free is installed/updated via the dashboard, normalize load order before the next request hits plugins. |
| 290 |
*/ |
| 291 |
function yatra_on_upgrader_process_complete($upgrader, array $hook_extra): void |
| 292 |
{ |
| 293 |
if (!isset($hook_extra['plugin']) || $hook_extra['plugin'] !== 'yatra/yatra.php') { |
| 294 |
return; |
| 295 |
} |
| 296 |
yatra_normalize_yatra_plugins_active_order(); |
| 297 |
yatra_deactivate_incompatible_old_pro(); |
| 298 |
} |
| 299 |
|
| 300 |
add_action('upgrader_process_complete', 'yatra_on_upgrader_process_complete', 10, 2); |
| 301 |
|
| 302 |
/** |
| 303 |
* On Plugins → Installed Plugins, show a row warning under Yatra Pro if the installed package is < 3.0 (cannot be safely activated with Yatra 3.0 Free). |
| 304 |
* |
| 305 |
* @param string $plugin_file Relative plugin path. |
| 306 |
* @param array<string, mixed> $plugin_data Headers from the plugin file. |
| 307 |
*/ |
| 308 |
function yatra_plugin_row_old_pro_warning(string $plugin_file, array $plugin_data): void |
| 309 |
{ |
| 310 |
if (!defined('YATRA_VERSION') || version_compare(YATRA_VERSION, '3.0.0', '<')) { |
| 311 |
return; |
| 312 |
} |
| 313 |
|
| 314 |
$v = isset($plugin_data['Version']) ? trim((string) $plugin_data['Version']) : '0'; |
| 315 |
if ($v === '' || yatra_is_pro_version_compatible($v)) { |
| 316 |
return; |
| 317 |
} |
| 318 |
|
| 319 |
$account_url = yatra_get_compatible_pro_account_url(); |
| 320 |
$msg = sprintf( |
| 321 |
/* translators: 1: minimum Yatra Pro version, 2: account URL */ |
| 322 |
esc_html__( |
| 323 |
'This Yatra Pro version is not compatible with Yatra 3.0. Activating it will cause a fatal error. Install Yatra Pro %1$s or newer from your MantraBrain store account (%2$s) before activating.', |
| 324 |
'yatra' |
| 325 |
), |
| 326 |
esc_html(yatra_get_minimum_compatible_pro_version()), |
| 327 |
esc_html($account_url) |
| 328 |
); |
| 329 |
|
| 330 |
printf( |
| 331 |
'<tr class="plugin-update-tr active yatra-pro-incompatible-row"><td colspan="4" class="plugin-update colspanchange"><div class="update-message notice inline notice-error notice-alt"><p>%s</p></div></td></tr>', |
| 332 |
$msg |
| 333 |
); |
| 334 |
} |
| 335 |
|
| 336 |
add_action('after_plugin_row_yatra-pro/yatra-pro.php', 'yatra_plugin_row_old_pro_warning', 10, 2); |
| 337 |
|