PluginProbe
YayMail – WooCommerce Email Customizer / 4.4.0
YayMail – WooCommerce Email Customizer v4.4.0
4.4.4 4.4.3 4.4.2 4.4.1 trunk 1.9.6 2.1.4 2.1.5 3.2.2 3.2.6 3.2.7.1 3.2.8.1 3.2.9 3.3 3.3.1 3.3.4 3.3.5 3.3.6 3.3.7 3.3.8 3.3.9 3.4 3.4.1 3.4.2 3.4.3 All 55 releases
yaymail / src / Models / MigrationModel.php

MigrationModel.php in YayMail – WooCommerce Email Customizer 4.4.0, at src/Models/MigrationModel.php

318 lines 13.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace YayMail\Models;
3
4 use YayMail\Migrations\AbstractMigration;
5 use YayMail\Utils\SingletonTrait;
6 use YayMail\Utils\Logger;
7 use YayMail\Migrations\MainMigration;
8 use YayMail\Migrations\MigrationHelper;
9
10 /**
11 * Migration Model
12 *
13 * @method static MigrationModel get_instance()
14 */
15 class MigrationModel {
16 use SingletonTrait;
17
18 /** @var Logger */
19 private $logger;
20
21 private function __construct() {
22 $this->logger = new Logger();
23 }
24
25 /**
26 * Safely unserialize values coming from backup/import data.
27 *
28 * @param mixed $value The value to unserialize.
29 * @return mixed
30 */
31 private function safe_maybe_unserialize( $value ) {
32 if ( ! is_string( $value ) ) {
33 return $value;
34 }
35
36 if ( ! function_exists( 'is_serialized' ) || ! is_serialized( $value ) ) {
37 return $value;
38 }
39
40 $unserialized = @unserialize( $value, [ 'allowed_classes' => false ] );
41
42 if ( false === $unserialized && 'b:0;' !== $value ) {
43 return $value;
44 }
45
46 if ( is_object( $unserialized ) ) {
47 return $value;
48 }
49
50 return $unserialized;
51 }
52
53 public function get_onload_data() {
54 return [
55 'required_migrations' => $this->get_required_migration_names(),
56 'backups' => $this->get_backups(),
57 ];
58 }
59
60 public function migrate() {
61 $is_migration_succeeded = MainMigration::get_instance()->migrate();
62 if ( ! $is_migration_succeeded ) {
63 throw new \Exception( esc_html__( 'Migration failed, please contact our Customer Support for help!', 'yaymail' ), 500 );
64 }
65 return [ 'is_critical_migration_required' => $this->check_if_critical_migration_required() ];
66 }
67
68 /**
69 * Restore backup to a specific version
70 *
71 * @param array $backup Backup data containing posts, postmeta, options, metadata, and version.
72 *
73 * @return array Returns array with 'is_critical_migration_required' boolean flag.
74 * @throws \Exception When backup doesn't exist or restoration fails.
75 */
76 public function reset( $backup ) {
77 global $wpdb;
78
79 if ( ! $backup || ! $backup['version'] || ! $backup['name'] ) {
80 throw new \Exception( 'Back up does not exist', 500 );
81 }
82
83 try {
84 $backup_name = $backup['name'];
85 $version = $backup['version'];
86
87 $wpdb->query( 'START TRANSACTION' );
88 $this->logger->log( '***** Start reset transaction ****' );
89 $this->logger->log( "Backup name: $backup_name" );
90
91 // Get IDs of posts to be deleted
92 $post_ids_to_delete = $wpdb->get_col(
93 $wpdb->prepare(
94 "SELECT ID FROM {$wpdb->posts} WHERE post_type = %s",
95 'yaymail_template'
96 )
97 );
98 if ( $post_ids_to_delete ) {
99 // Delete current post meta data
100 $delete_postmeta_query = sprintf(
101 "DELETE FROM {$wpdb->postmeta} WHERE post_id IN (%s)",
102 implode( ',', array_map( 'intval', $post_ids_to_delete ) )
103 );
104 $this->logger->log( $delete_postmeta_query );
105 $wpdb->query( $delete_postmeta_query );// phpcs:ignore
106
107 // Delete the posts
108 $delete_template_posts_query = sprintf(
109 "DELETE FROM {$wpdb->posts} WHERE ID IN (%s)",
110 implode( ',', array_map( 'intval', $post_ids_to_delete ) )
111 );
112 $this->logger->log( $delete_template_posts_query );
113 $wpdb->query( $delete_template_posts_query );// phpcs:ignore
114 }
115
116 $delete_template_posts_query = $wpdb->prepare(
117 "DELETE FROM {$wpdb->posts} WHERE post_type = %s",
118 'yaymail_template'
119 );
120 $this->logger->log( $delete_template_posts_query );
121 $wpdb->query( $delete_template_posts_query );// phpcs:ignore
122
123 // Restore the backed-up posts
124 $backed_up_posts = $backup['posts'];
125 $backed_up_postmeta = $backup['postmeta'];
126 foreach ( $backed_up_posts as $post ) {
127 if ( ! isset( $post->post_type ) || 'yaymail_template' !== $post->post_type ) {
128 continue;
129 }
130 $wpdb->insert(
131 $wpdb->posts,
132 [
133 'post_author' => $post->post_author,
134 'post_date' => $post->post_date,
135 'post_date_gmt' => $post->post_date_gmt,
136 'post_content' => $post->post_content,
137 'post_title' => $post->post_title,
138 'post_excerpt' => $post->post_excerpt,
139 'post_status' => $post->post_status,
140 'comment_status' => $post->comment_status,
141 'ping_status' => $post->ping_status,
142 'post_password' => $post->post_password,
143 'post_name' => $post->post_name,
144 'to_ping' => $post->to_ping,
145 'pinged' => $post->pinged,
146 'post_modified' => $post->post_modified,
147 'post_modified_gmt' => $post->post_modified_gmt,
148 'post_content_filtered' => $post->post_content_filtered,
149 'post_parent' => $post->post_parent,
150 'guid' => $post->guid,
151 'menu_order' => $post->menu_order,
152 'post_type' => $post->post_type,
153 'post_mime_type' => $post->post_mime_type,
154 'comment_count' => $post->comment_count,
155 ]
156 );
157
158 // Insert post meta data
159 $inserted_post_id = $wpdb->insert_id;
160 $postmetas_for_current_post = array_filter(
161 $backed_up_postmeta,
162 function( $postmeta ) use ( $post ) {
163 return $postmeta->post_id === $post->ID;
164 }
165 );
166 if ( ! empty( $postmetas_for_current_post ) ) {
167 foreach ( $postmetas_for_current_post as $postmeta ) {
168 if ( ! isset( $postmeta->meta_key ) || strpos( (string) $postmeta->meta_key, 'yaymail' ) === false ) {
169 continue;
170 }
171 add_post_meta(
172 $inserted_post_id,
173 $postmeta->meta_key,
174 $this->safe_maybe_unserialize( $postmeta->meta_value )
175 );
176 }
177 }
178 }//end foreach
179
180 // Restore backed-up options
181 foreach ( $backup['options'] as $option ) {
182 // Only restore options that belong to YayMail (same pattern as export)
183 if ( strpos( $option->option_name, 'yaymail' ) !== false ) {
184 update_option( $option->option_name, $this->safe_maybe_unserialize( $option->option_value ) );
185 }
186 }
187
188 // Remove the succeeded migration log from db
189 $successful_migrations = get_option( AbstractMigration::SUCCESSFUL_MIGRATIONS, null );
190 $removed_migrations = [];
191 if ( ! empty( $successful_migrations ) ) {
192 $filtered_successful_migrations = array_filter(
193 $successful_migrations,
194 function( $migration ) use ( $version, $backup ) {
195 $backup_date = new \DateTime( $backup['created_date'], wp_timezone() );
196 $migration_date = new \DateTime( $migration['created_date'], wp_timezone() );
197 $should_remove = $migration_date >= $backup_date;
198
199 return ! $should_remove;
200 }
201 );
202
203 update_option( AbstractMigration::SUCCESSFUL_MIGRATIONS, $filtered_successful_migrations );
204 $removed_migrations = array_udiff(
205 $successful_migrations,
206 $filtered_successful_migrations,
207 function( $a, $b ) {
208 return strcmp( serialize( $a ), serialize( $b ) );
209 }
210 );
211 }//end if
212
213 // Restore core version
214 update_option( 'yaymail_version', $version );
215 wp_cache_delete( 'yaymail_version', 'options' );
216
217 // Restore addon versions
218 $addon_versions = get_option( 'yaymail_addon_versions', [] );
219 $restored_addon_versions = array_filter(
220 $addon_versions,
221 function( $addon_version, $addon_namespace ) use ( $removed_migrations ) {
222 foreach ( $removed_migrations as $migration ) {
223 if ( ! empty( $migration['addon_namespace'] ) && $migration['addon_namespace'] === $addon_namespace && version_compare( $addon_version, $migration['to_version'], '<=' ) ) {
224 return false;
225 }
226 }
227 return true;
228 },
229 ARRAY_FILTER_USE_BOTH
230 );
231 update_option( 'yaymail_addon_versions', $restored_addon_versions );
232
233 do_action( 'yaymail_before_reset_migration_commit', $backup, $removed_migrations );
234
235 $wpdb->query( 'COMMIT' );
236 $this->logger->log( '***** Finish reset transaction ****' );
237 $this->logger->log( "$backup_name restored successfully!" );
238
239 return [ 'is_critical_migration_required' => $this->check_if_critical_migration_required() ];
240
241 } catch ( \Exception $e ) {
242 $wpdb->query( 'ROLLBACK' );
243 $this->logger->log( '***** Transaction rolled back. ' . $e->getMessage() );
244 throw new \Exception( esc_html__( 'Restoration failed', 'yaymail' ), 500 );
245 }//end try
246 }
247
248 public function check_if_critical_migration_required() {
249 $old_version = get_option( 'yaymail_version' );
250 if ( $old_version ) {
251 return version_compare( $old_version, '4.0.0', '<' );
252 }
253 return false;
254 }
255
256 private function get_required_migration_names() {
257 $old_version = MigrationHelper::format_version_number( get_option( 'yaymail_version' ) );
258 $new_version = MigrationHelper::format_version_number( yaymail_version() );
259 $successful_migrations = get_option( AbstractMigration::SUCCESSFUL_MIGRATIONS, [] );
260
261 // If freshly installed or already on latest version with no migrations needed
262 if ( ( version_compare( $old_version, $new_version, '>=' ) && empty( $successful_migrations ) ) ) {
263 return apply_filters( 'yaymail_required_migration_names', [] );
264 }
265
266 $args = [
267 'post_type' => 'yaymail_template',
268 'post_status' => 'any',
269 'posts_per_page' => -1,
270 ];
271
272 $query = new \WP_Query( $args );
273
274 $has_yaymail_template = $query->have_posts();
275 if ( ( $old_version == null && ! $has_yaymail_template ) && empty( $successful_migrations ) ) {
276 return [];
277 }
278
279 // Check if any migrations have already been run up to current version
280 foreach ( $successful_migrations as $migration ) {
281 if ( empty( $migration['addon_namespace'] ) && version_compare( $migration['to_version'], yaymail_version(), '>=' ) ) {
282 return apply_filters( 'yaymail_required_migration_names', [] );
283 }
284 }
285
286 // Get available migrations that need to be run
287 $core_migrations = MainMigration::CORE_MIGRATIONS;
288 $filtered_migrations = MigrationHelper::filter_migrations( $core_migrations, $old_version, $new_version );
289
290 $needed_migrations = empty( $filtered_migrations ) ? [] : [ 'YayMail' ];
291
292 return apply_filters( 'yaymail_required_migration_names', $needed_migrations );
293 }
294
295 private function get_backups() {
296 global $wpdb;
297
298 $backup_prefix = AbstractMigration::BACKUP_PREFIX;
299
300 $backup_options_query = $wpdb->prepare(
301 "SELECT option_name, option_value FROM {$wpdb->options} WHERE option_name LIKE %s",
302 $backup_prefix . '%'
303 );
304 $backups_result = $wpdb->get_results( $backup_options_query );// phpcs:ignore
305
306 $backups = [];
307 foreach ( $backups_result as $backup ) {
308 $data = $this->safe_maybe_unserialize( $backup->option_value );
309 $backups[] = [
310 'created_date' => $data['created_date'] ?? 'created_date not found',
311 'name' => $backup->option_name ?? 'backup name not found',
312 ];
313 }
314
315 return $backups;
316 }
317 }
318