PluginProbe
Import WP – CSV & XML Import Export for WordPress / 2.15.0
Import WP – CSV & XML Import Export for WordPress v2.15.0
2.15.1 2.15.0 2.14.24 2.14.23 2.7.0 2.7.1 2.7.10 2.7.11 2.7.12 2.7.13 2.7.14 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 2.7.8 2.7.9 2.8.0 2.8.1 2.8.2 2.8.3 2.9.0 2.9.1 All 144 releases
← All changes | class/Common/Migration/Migrations.php +243 -14 2.7.1 → 2.15.0 View file →
@@ -1,8 +1,9 @@
1 1 <?php
2 2
3 3 namespace ImportWP\Common\Migration;
4 4
5 +use ImportWP\Common\Filesystem\Filesystem;
5 6 use ImportWP\Common\Importer\ImporterManager;
6 7 use ImportWP\Common\Model\ImporterModel;
7 8 use ImportWP\Common\Util\Logger;
8 9 use ImportWP\Container;
@@ -14,9 +15,9 @@
14 15
15 16 public function __construct()
16 17 {
17 18
18 - $starting_version = intval(get_site_option('iwp_db_version', 0));
19 + $starting_version = intval(get_option('iwp_db_version', 0));
19 20 if ($starting_version <= 3 && $starting_version > 0) {
20 21 // v1 migrations
21 22 $this->_migrations[] = array($this, 'migration_01');
22 23 $this->_migrations[] = array($this, 'migration_02');
@@ -33,8 +34,12 @@
33 34 // v2 migrations
34 35 $this->_migrations[] = array($this, 'migration_05_multiple_crons');
35 36 $this->_migrations[] = array($this, 'migration_06_cron_update');
36 37 $this->_migrations[] = array($this, 'migration_07_add_session_table');
38 + $this->_migrations[] = array($this, 'migration_08_migrate_taxonomy_settings');
39 + $this->_migrations[] = array($this, 'migration_09_migrate_attachment_settings');
40 + $this->_migrations[] = array($this, 'migration_10_relative_importer_file_paths');
41 + $this->_migrations[] = array($this, 'migration_11_prefix_custom_methods');
37 42
38 43 $this->_version = count($this->_migrations);
39 44 }
40 45
@@ -39,9 +44,9 @@
39 44 }
40 45
41 46 public function isSetup()
42 47 {
43 - $version = intval(get_site_option('iwp_db_version', get_site_option('jci_db_version', 0)));
48 + $version = intval(get_option('iwp_db_version', get_option('jci_db_version', 0)));
44 49 if ($version < count($this->_migrations)) {
45 50 return false;
46 51 }
47 52 return true;
@@ -58,11 +63,11 @@
58 63 {
59 64 global $wpdb;
60 65 $wpdb->query("DROP TABLE IF EXISTS `" . $wpdb->prefix . "importer_log`;");
61 66 $wpdb->query("DROP TABLE IF EXISTS `" . $wpdb->prefix . "importer_files`;");
62 - delete_site_option('iwp_db_version');
63 - delete_site_option('jci_db_version');
64 - delete_site_option('iwp_is_migrating');
67 + delete_option('iwp_db_version');
68 + delete_option('jci_db_version');
69 + delete_option('iwp_is_migrating');
65 70 }
66 71
67 72 public function migrate($migrate_data = true)
68 73 {
@@ -67,10 +72,10 @@
67 72 public function migrate($migrate_data = true)
68 73 {
69 74
70 75 $verion_key = 'iwp_db_version';
71 - $version = intval(get_site_option('iwp_db_version', get_site_option('jci_db_version', 0)));
72 - $migrating = get_site_option('iwp_is_migrating', 'no');
76 + $version = intval(get_option('iwp_db_version', get_option('jci_db_version', 0)));
77 + $migrating = get_option('iwp_is_migrating', 'no');
73 78 if ('yes' === $migrating) {
74 79 return;
75 80 }
76 81
@@ -82,9 +87,9 @@
82 87
83 88 $migration_version = $i + 1;
84 89 if ($version < $migration_version) {
85 90
86 - update_site_option('iwp_is_migrating', 'yes');
91 + update_option('iwp_is_migrating', 'yes');
87 92
88 93 set_time_limit(0);
89 94
90 95 // Run migration
@@ -92,15 +97,15 @@
92 97 call_user_func($this->_migrations[$i], $migrate_data);
93 98 }
94 99
95 100 // Flag as migrated
96 - update_site_option($verion_key, $migration_version);
97 - update_site_option('iwp_is_migrating', 'no');
101 + update_option($verion_key, $migration_version);
102 + update_option('iwp_is_migrating', 'no');
98 103 }
99 104 }
100 105 }
101 106
102 - // update_site_option('iwp_is_setup', 'yes');
107 + // update_option('iwp_is_setup', 'yes');
103 108 }
104 109
105 110 public function get_charset()
106 111 {
@@ -706,11 +711,9 @@
706 711 unset($data['settings']['cron_disabled']);
707 712
708 713 $data['settings']['cron'] = $cron;
709 714
710 - remove_filter('content_save_pre', 'wp_filter_post_kses');
711 - wp_update_post(['ID' => $id, 'post_content' => serialize($data)]);
712 - add_filter('content_save_pre', 'wp_filter_post_kses');
715 + $this->update_importer_post_content($id, $data);
713 716 }
714 717 }
715 718
716 719 public function migration_06_cron_update()
@@ -782,8 +785,9 @@
782 785 'session' => $post['meta_value']
783 786 ];
784 787 $format = ['%d', '%d', '%s', '%s'];
785 788
789 + // TODO: is this needed as blog_id is more relevant since there is a sessions table per site?
786 790 if (is_multisite()) {
787 791 $data['site_id'] = $wpdb->siteid;
788 792 $format[] = '%d';
789 793 }
@@ -808,8 +812,9 @@
808 812 'session' => $post['meta_value']
809 813 ];
810 814 $format = ['%d', '%d', '%s', '%s'];
811 815
816 + // TODO: is this needed as blog_id is more relevant since there is a sessions table per site?
812 817 if (is_multisite()) {
813 818 $data['site_id'] = $wpdb->siteid;
814 819 $format[] = '%d';
815 820 }
@@ -834,8 +839,9 @@
834 839 'session' => $post['meta_value']
835 840 ];
836 841 $format = ['%d', '%d', '%s', '%s'];
837 842
843 + // TODO: is this needed as blog_id is more relevant since there is a sessions table per site?
838 844 if (is_multisite()) {
839 845 $data['site_id'] = $wpdb->siteid;
840 846 $format[] = '%d';
841 847 }
@@ -842,6 +848,229 @@
842 848
843 849 $wpdb->insert($wpdb->prefix . 'iwp_sessions', $data, $format);
844 850 }
845 851 }
852 + }
853 +
854 + public function migration_08_migrate_taxonomy_settings($migrate_data = true)
855 + {
856 + /**
857 + * @var \wpdb $wpdb
858 + */
859 + global $wpdb;
860 +
861 + // TODO: loop through serialsed post_content, switching from single cron to array
862 + $importers = $wpdb->get_results("SELECT * FROM {$wpdb->posts} WHERE post_type='" . IWP_POST_TYPE . "'", ARRAY_A);
863 +
864 + foreach ($importers as $importer) {
865 +
866 + $data = maybe_unserialize($importer['post_content']);
867 + $modified = false;
868 +
869 + $tmp = [];
870 + foreach ($data['map'] as $field_id => $field_value) {
871 + $count = 0;
872 + $tmp[preg_replace('/^(taxonomies\.\d+\.)(_.*?)$/', '$1settings.$2', $field_id, -1, $count)] = $field_value;
873 + if ($count > 0) {
874 + $modified = true;
875 + }
876 + }
877 +
878 + if (!$modified) {
879 + continue;
880 + }
881 +
882 + $data['map'] = $tmp;
883 +
884 + $this->update_importer_post_content($importer['ID'], $data);
885 + }
886 + }
887 +
888 + // TODO: do we need this? can we get around this with manipulating the data if it exists?
889 + public function migration_09_migrate_attachment_settings($migrate_data = true)
890 + {
891 + /**
892 + * @var \wpdb $wpdb
893 + */
894 + global $wpdb;
895 +
896 + $importers = $wpdb->get_results("SELECT * FROM {$wpdb->posts} WHERE post_type='" . IWP_POST_TYPE . "'", ARRAY_A);
897 +
898 + foreach ($importers as $importer) {
899 +
900 + $data = maybe_unserialize($importer['post_content']);
901 + $modified = false;
902 +
903 + $tmp = [];
904 + foreach ($data['map'] as $field_id => $field_value) {
905 + $count = 0;
906 +
907 + $ends_with = [
908 + '_download',
909 + '_enable_image_hash',
910 + '_featured',
911 + '_ftp_host',
912 + '_ftp_pass',
913 + '_ftp_path',
914 + '_ftp_user',
915 + '_local_url',
916 + '_meta\._alt',
917 + '_meta\._caption',
918 + '_meta\._description',
919 + '_meta\._enabled',
920 + '_meta\._title',
921 + '_remote_url',
922 + '_return'
923 + ];
924 +
925 + $tmp[preg_replace('/^(.+)(?<!\.settings)\.(' . implode('|', $ends_with) . ')$/', '$1.settings.$2', $field_id, -1, $count)] = $field_value;
926 +
927 + if ($count > 0) {
928 + $modified = true;
929 + }
930 + }
931 +
932 + if (!$modified) {
933 + continue;
934 + }
935 +
936 + $data['map'] = $tmp;
937 +
938 + $this->update_importer_post_content($importer['ID'], $data);
939 + }
940 + }
941 +
942 + /**
943 + * Convert stored absolute importer file paths to uploads-relative paths.
944 + *
945 + * Prevents open_basedir warnings after hosting path / WordPress root changes.
946 + *
947 + * @param bool $migrate_data
948 + * @return void
949 + */
950 + public function migration_10_relative_importer_file_paths($migrate_data = true)
951 + {
952 + if (!$migrate_data) {
953 + return;
954 + }
955 +
956 + /**
957 + * @var \wpdb $wpdb
958 + */
959 + global $wpdb;
960 +
961 + $results = $wpdb->get_results(
962 + "SELECT meta_id, post_id, meta_key, meta_value
963 + FROM {$wpdb->postmeta}
964 + WHERE meta_key LIKE '\\_importer\\_file\\_%'",
965 + ARRAY_A
966 + );
967 +
968 + if (empty($results)) {
969 + return;
970 + }
971 +
972 + foreach ($results as $row) {
973 + $stored = $row['meta_value'];
974 + if (!Filesystem::is_absolute_path($stored)) {
975 + continue;
976 + }
977 +
978 + $resolved = Filesystem::resolve_importer_file_path($stored);
979 + if (!$resolved) {
980 + continue;
981 + }
982 +
983 + $relative = Filesystem::to_uploads_relative_path($resolved);
984 + if ($relative === '' || $relative === $stored) {
985 + continue;
986 + }
987 +
988 + update_post_meta((int) $row['post_id'], $row['meta_key'], $relative);
989 + }
990 + }
991 +
992 + /**
993 + * Prefix custom method calls with [iwp:...] so they do not collide with
994 + * shortcodes or Gutenberg content that uses [name(...)].
995 + *
996 + * Rewrites [strtoupper("x")] → [iwp:strtoupper("x")] in importer maps,
997 + * filters, and other stored string settings. Already-prefixed calls are left alone.
998 + *
999 + * @param bool $migrate_data
1000 + * @return void
1001 + */
1002 + public function migration_11_prefix_custom_methods($migrate_data = true)
1003 + {
1004 + if (!$migrate_data) {
1005 + return;
1006 + }
1007 +
1008 + /**
1009 + * @var \wpdb $wpdb
1010 + */
1011 + global $wpdb;
1012 +
1013 + $importers = $wpdb->get_results("SELECT * FROM {$wpdb->posts} WHERE post_type='" . IWP_POST_TYPE . "'", ARRAY_A);
1014 + if (empty($importers)) {
1015 + return;
1016 + }
1017 +
1018 + foreach ($importers as $importer) {
1019 + $data = maybe_unserialize($importer['post_content']);
1020 + if (!is_array($data)) {
1021 + continue;
1022 + }
1023 +
1024 + $migrated = $this->migration_11_prefix_custom_methods_in_value($data);
1025 + if ($migrated === $data) {
1026 + continue;
1027 + }
1028 +
1029 + $this->update_importer_post_content($importer['ID'], $migrated);
1030 + }
1031 + }
1032 +
1033 + /**
1034 + * Persist serialized importer settings without corrupting backslashes.
1035 + *
1036 + * wp_update_post() only slashes the existing DB row, then merges in the
1037 + * caller-supplied fields. Those fields must already be slashed or
1038 + * wp_insert_post() will stripslashes() the payload and break serialize()
1039 + * strings such as the CSV escape character "\".
1040 + *
1041 + * @param int $id
1042 + * @param array $data
1043 + * @return void
1044 + */
1045 + private function update_importer_post_content($id, array $data)
1046 + {
1047 + remove_filter('content_save_pre', 'wp_filter_post_kses');
1048 + wp_update_post([
1049 + 'ID' => $id,
1050 + 'post_content' => wp_slash(serialize($data)),
1051 + ]);
1052 + add_filter('content_save_pre', 'wp_filter_post_kses');
1053 + }
1054 +
1055 + /**
1056 + * Recursively rewrite [method( → [iwp:method( in strings.
1057 + *
1058 + * @param mixed $value
1059 + * @return mixed
1060 + */
1061 + private function migration_11_prefix_custom_methods_in_value($value)
1062 + {
1063 + if (is_array($value)) {
1064 + foreach ($value as $key => $item) {
1065 + $value[$key] = $this->migration_11_prefix_custom_methods_in_value($item);
1066 + }
1067 + return $value;
1068 + }
1069 +
1070 + if (!is_string($value) || $value === '' || strpos($value, '[') === false) {
1071 + return $value;
1072 + }
1073 +
1074 + return preg_replace('/\[(?!iwp:)(\w+)\(/', '[iwp:$1(', $value);
846 1075 }
847 1076 }