PluginProbe
Yatra – Travel Booking & Tour Operator Software / trunk
Yatra – Travel Booking & Tour Operator Software vtrunk
3.0.14 3.0.14.1 3.0.14.2 3.0.12 3.0.13 3.0.11 3.0.10 3.0.9 3.0.8 3.0.7 3.0.6 3.0.5 3.0.5.1 3.0.4 3.0.3 3.0.2.9 3.0.2.7 3.0.2.8 3.0.2.6 trunk 1.0.0 2.0.0 2.0.1 2.0.10 2.0.11 All 82 releases
yatra / includes / incompatible-pro-guard.php

incompatible-pro-guard.php in Yatra – Travel Booking & Tour Operator Software trunk, at includes/incompatible-pro-guard.php

337 lines 10.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Yatra Pro &lt; 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 &lt; 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 (&lt; 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 &lt; 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