PluginProbe
WindPress – Tailwind CSS integration for WordPress / trunk
WindPress – Tailwind CSS integration for WordPress vtrunk
3.2.88 3.2.87 3.2.86 3.2.85 3.2.84 3.2.83 3.2.82 3.2.81 trunk 3.0.0 3.0.1 3.0.10 3.0.11 3.0.12 3.0.13 3.0.14 3.0.15 3.0.16 3.0.17 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 All 142 releases
windpress / src / Upgrade / UpgradeManager.php

UpgradeManager.php in WindPress – Tailwind CSS integration for WordPress trunk, at src/Upgrade/UpgradeManager.php

267 lines 10.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /*
4 * This file is part of the WindPress package.
5 *
6 * (c) Joshua Gugun Siagian <suabahasa@gmail.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11 declare (strict_types=1);
12 namespace WindPress\WindPress\Upgrade;
13
14 use Exception;
15 use WIND_PRESS;
16 use WindPress\WindPress\Core\Volume;
17 use WindPress\WindPress\Utils\Common;
18 /**
19 * @since 3.0.0
20 */
21 class UpgradeManager
22 {
23 /**
24 * Stores the instance, implementing a Singleton pattern.
25 */
26 private static self $instance;
27 private const UPGRADE_OPTION_KEY = 'windpress_upgrade_state';
28 private array $upgrades = ['3.3.51' => 'up_to_3_3_51'];
29 /**
30 * The Singleton's constructor should always be private to prevent direct
31 * construction calls with the `new` operator.
32 */
33 private function __construct()
34 {
35 }
36 /**
37 * Singletons should not be cloneable.
38 */
39 private function __clone()
40 {
41 }
42 /**
43 * Singletons should not be restorable from strings.
44 *
45 * @throws Exception Cannot unserialize a singleton.
46 */
47 public function __wakeup()
48 {
49 throw new Exception('Cannot unserialize a singleton.');
50 }
51 /**
52 * This is the static method that controls the access to the singleton
53 * instance. On the first run, it creates a singleton object and places it
54 * into the static property. On subsequent runs, it returns the client existing
55 * object stored in the static property.
56 */
57 public static function get_instance(): self
58 {
59 if (!isset(self::$instance)) {
60 self::$instance = new self();
61 }
62 return self::$instance;
63 }
64 public function run(): void
65 {
66 // Store current version metadata for future upgrade tracking
67 $this->store_version_metadata();
68 $upgrade_state = $this->get_upgrade_state();
69 $current_version = $this->get_current_version();
70 foreach ($this->upgrades as $version => $method) {
71 if ($this->should_run_upgrade($version, $upgrade_state, $current_version)) {
72 try {
73 $this->{$method}();
74 $this->mark_upgrade_complete($version);
75 } catch (Exception $e) {
76 error_log("WindPress Upgrade Error for version {$version}: " . $e->getMessage());
77 $this->mark_upgrade_failed($version, $e->getMessage());
78 }
79 }
80 }
81 }
82 /**
83 * Store version metadata for tracking upgrade scenarios.
84 * This should be called during plugin activation and upgrades.
85 */
86 public function store_version_metadata(): void
87 {
88 $current_version = $this->get_current_version();
89 $is_wp_org = $this->is_wp_org_installation();
90 // Get the previously stored version to track upgrade scenarios
91 $previous_version = get_option(WIND_PRESS::WP_OPTION . '_version', null);
92 $previous_was_wp_org = get_option(WIND_PRESS::WP_OPTION . '_was_wp_org', null);
93 // Store the previous version info before updating
94 if ($previous_version && $previous_version !== $current_version) {
95 update_option(WIND_PRESS::WP_OPTION . '_previous_version', $previous_version);
96 update_option(WIND_PRESS::WP_OPTION . '_previous_was_wp_org', $previous_was_wp_org ?? \false);
97 }
98 // Update current version info
99 update_option(WIND_PRESS::WP_OPTION . '_version', $current_version);
100 update_option(WIND_PRESS::WP_OPTION . '_was_wp_org', $is_wp_org);
101 }
102 private function get_upgrade_state(): array
103 {
104 return get_option(self::UPGRADE_OPTION_KEY, []);
105 }
106 private function save_upgrade_state(array $state): void
107 {
108 update_option(self::UPGRADE_OPTION_KEY, $state);
109 }
110 private function get_current_version(): string
111 {
112 return WIND_PRESS::VERSION;
113 }
114 /**
115 * Check if this is a WP.org installation (Free version).
116 * WP.org versions have a minor version that is 1 lower than Pro versions.
117 *
118 * @return bool True if installed from WP.org, false if Pro version
119 */
120 private function is_wp_org_installation(): bool
121 {
122 return !Common::is_updater_library_available();
123 }
124 /**
125 * Get the previous version stored during plugin activation/upgrade.
126 * This helps determine upgrade scenarios.
127 *
128 * @return string|null Previous version or null if not available
129 */
130 private function get_previous_version(): ?string
131 {
132 return get_option(WIND_PRESS::WP_OPTION . '_previous_version', null);
133 }
134 /**
135 * Check if the previous version was from WP.org.
136 * This is determined by checking if the previous version metadata indicates WP.org origin.
137 *
138 * @return bool True if previous version was from WP.org
139 */
140 private function was_previous_version_wp_org(): bool
141 {
142 return get_option(WIND_PRESS::WP_OPTION . '_previous_was_wp_org', \false);
143 }
144 private function should_run_upgrade(string $version, array $upgrade_state, string $current_version): bool
145 {
146 if (isset($upgrade_state[$version]) && $upgrade_state[$version]['status'] === 'completed') {
147 return \false;
148 }
149 $is_wp_org = $this->is_wp_org_installation();
150 $was_previous_wp_org = $this->was_previous_version_wp_org();
151 // If current installation is from WP.org, we need to adjust the version comparison
152 if ($is_wp_org) {
153 // For WP.org installations, the current version has minor version - 1
154 // So we need to compare against an adjusted target version
155 $adjusted_version = $this->adjust_version_for_wp_org($version);
156 return version_compare($current_version, $adjusted_version, '>=');
157 }
158 // If previous version was from WP.org but current is Pro, handle upgrade from WP.org to Pro
159 if ($was_previous_wp_org && !$is_wp_org) {
160 $previous_version = $this->get_previous_version();
161 if ($previous_version) {
162 // The previous WP.org version might have a lower minor version, so we need special handling
163 $adjusted_previous = $this->adjust_version_from_wp_org($previous_version);
164 return version_compare($adjusted_previous, $version, '<') && version_compare($current_version, $version, '>=');
165 }
166 }
167 // Standard version comparison for Pro versions
168 return version_compare($current_version, $version, '>=');
169 }
170 /**
171 * Adjust a Pro version number to WP.org equivalent (minor version - 1).
172 * Example: 3.3.50 -> 3.2.50
173 *
174 * @param string $version Pro version
175 * @return string WP.org equivalent version
176 */
177 private function adjust_version_for_wp_org(string $version): string
178 {
179 $parts = explode('.', $version);
180 if (count($parts) >= 3) {
181 $major = (int) $parts[0];
182 $minor = (int) $parts[1];
183 $patch = (int) $parts[2];
184 // Decrease minor version by 1 for WP.org
185 $wp_org_minor = max(0, $minor - 1);
186 return "{$major}.{$wp_org_minor}.{$patch}";
187 }
188 return $version;
189 }
190 /**
191 * Adjust a WP.org version number to Pro equivalent (minor version + 1).
192 * Example: 3.2.50 -> 3.3.50
193 *
194 * @param string $version WP.org version
195 * @return string Pro equivalent version
196 */
197 private function adjust_version_from_wp_org(string $version): string
198 {
199 $parts = explode('.', $version);
200 if (count($parts) >= 3) {
201 $major = (int) $parts[0];
202 $minor = (int) $parts[1];
203 $patch = (int) $parts[2];
204 // Increase minor version by 1 to get Pro equivalent
205 $pro_minor = $minor + 1;
206 return "{$major}.{$pro_minor}.{$patch}";
207 }
208 return $version;
209 }
210 private function mark_upgrade_complete(string $version): void
211 {
212 $upgrade_state = $this->get_upgrade_state();
213 $upgrade_state[$version] = ['status' => 'completed', 'completed_at' => current_time('mysql')];
214 $this->save_upgrade_state($upgrade_state);
215 }
216 private function mark_upgrade_failed(string $version, string $error_message): void
217 {
218 $upgrade_state = $this->get_upgrade_state();
219 $upgrade_state[$version] = ['status' => 'failed', 'error' => $error_message, 'failed_at' => current_time('mysql')];
220 $this->save_upgrade_state($upgrade_state);
221 }
222 public function reset_upgrade(string $version): bool
223 {
224 if (!isset($this->upgrades[$version])) {
225 return \false;
226 }
227 $upgrade_state = $this->get_upgrade_state();
228 unset($upgrade_state[$version]);
229 $this->save_upgrade_state($upgrade_state);
230 return \true;
231 }
232 public function get_upgrade_status(string $version): ?array
233 {
234 $upgrade_state = $this->get_upgrade_state();
235 return $upgrade_state[$version] ?? null;
236 }
237 public function get_all_upgrade_statuses(): array
238 {
239 $upgrade_state = $this->get_upgrade_state();
240 $result = [];
241 foreach ($this->upgrades as $version => $method) {
242 $result[$version] = ['method' => $method, 'status' => $upgrade_state[$version] ?? ['status' => 'pending']];
243 }
244 return $result;
245 }
246 /**
247 * @version 3.3.51
248 */
249 public function up_to_3_3_51()
250 {
251 $entries = Volume::get_entries();
252 // Introducing the Wizard feature - add wizard.css import to main.css
253 $main_css_key = array_search('main.css', array_column($entries, 'name'), \true);
254 if ($main_css_key !== \false) {
255 $main_css_content = $entries[$main_css_key]['content'];
256 // Check if wizard.css import is already present
257 if (!preg_match('/@import\s+["\']\.\/wizard\.css["\'];/', $main_css_content)) {
258 // Add the import at the end of the file
259 $main_css_content .= "\n@import \"./wizard.css\";";
260 $entries[$main_css_key]['content'] = $main_css_content;
261 // Save the updated entries
262 Volume::save_entries($entries);
263 }
264 }
265 }
266 }
267