| 1 |
<?php |
| 2 |
|
| 3 |
/** |
| 4 |
* @package Tag Groups |
| 5 |
* @author Christoph Amthor |
| 6 |
* @copyright 2020 Christoph Amthor (@ Chatty Mango, chattymango.com) |
| 7 |
* @license GPL-3.0+ |
| 8 |
*/ |
| 9 |
|
| 10 |
if (! class_exists('TagGroups_Import')) { |
| 11 |
/** |
| 12 |
* |
| 13 |
* @since 1.38.0 |
| 14 |
*/ |
| 15 |
// phpcs:ignore PSR1.Classes.ClassDeclaration.MissingNamespace, Squiz.Classes.ValidClassName.NotCamelCaps -- Legacy class structure |
| 16 |
class TagGroups_Import |
| 17 |
{ |
| 18 |
/** |
| 19 |
* File contents for import |
| 20 |
* |
| 21 |
* @var string |
| 22 |
*/ |
| 23 |
private $contents; |
| 24 |
/** |
| 25 |
* type of data in the imported file |
| 26 |
* |
| 27 |
* @var string |
| 28 |
*/ |
| 29 |
private $file_type; |
| 30 |
/** |
| 31 |
* whether an error occured |
| 32 |
* |
| 33 |
* @var integer |
| 34 |
*/ |
| 35 |
private $error; |
| 36 |
|
| 37 |
// phpcs:ignore Squiz.Scope.MethodScope.Missing -- Legacy code |
| 38 |
function __construct() |
| 39 |
{ |
| 40 |
|
| 41 |
$this->error = false; |
| 42 |
} |
| 43 |
|
| 44 |
|
| 45 |
/** |
| 46 |
* Determines the type of imported file |
| 47 |
* |
| 48 |
* @return void |
| 49 |
*/ |
| 50 |
// phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps, Squiz.Scope.MethodScope.Missing -- Legacy method naming |
| 51 |
function determine_file_type() |
| 52 |
{ |
| 53 |
|
| 54 |
// phpcs:ignore WordPress.Security.NonceVerification.Missing -- File upload verification |
| 55 |
if (! isset($_FILES['settings_file'])) { |
| 56 |
die("File missing"); |
| 57 |
} |
| 58 |
|
| 59 |
// Check file name, but allow for some additional characters in file name since downloading multiple times may add something to the original name. |
| 60 |
// Allow extension txt for backwards compatibility |
| 61 |
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotValidated -- File upload validation |
| 62 |
preg_match('/^tag_groups_settings-\w{10}[\w,\s-]*\.((txt)|(json))$/', $_FILES['settings_file']['name'], $matches_settings); |
| 63 |
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotValidated -- File upload validation |
| 64 |
preg_match('/^tag_groups_terms-\w{10}[\w,\s-]*\.json$/', $_FILES['settings_file']['name'], $matches_terms); |
| 65 |
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotValidated -- File upload validation |
| 66 |
if (! empty($matches_settings) && ! empty($matches_settings[0]) && $matches_settings[0] == $_FILES['settings_file']['name']) { |
| 67 |
$this->file_type = 'settings'; |
| 68 |
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotValidated -- File upload validation |
| 69 |
} elseif (! empty($matches_terms) && ! empty($matches_terms[0]) && $matches_terms[0] == $_FILES['settings_file']['name']) { |
| 70 |
$this->file_type = 'terms'; |
| 71 |
} else { |
| 72 |
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing -- File upload validation |
| 73 |
if (! empty($_FILES['settings_file']['name'])) { |
| 74 |
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing -- File upload validation |
| 75 |
$file_info = ' ' . $_FILES['settings_file']['name']; |
| 76 |
} else { |
| 77 |
$file_info = ''; |
| 78 |
} |
| 79 |
|
| 80 |
$this->error = true; |
| 81 |
TagGroups_Admin_Notice::add('error', __('Error uploading the file.', 'tag-groups') . $file_info); |
| 82 |
} |
| 83 |
} |
| 84 |
|
| 85 |
|
| 86 |
/** |
| 87 |
* Reads the contents |
| 88 |
* |
| 89 |
* @return void |
| 90 |
*/ |
| 91 |
// phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps, Squiz.Scope.MethodScope.Missing -- Legacy method naming |
| 92 |
function read_file() |
| 93 |
{ |
| 94 |
|
| 95 |
if ($this->error) { |
| 96 |
return; |
| 97 |
} |
| 98 |
|
| 99 |
// phpcs:ignore WordPress.Security.NonceVerification.Missing -- File upload verification |
| 100 |
if (! isset($_FILES['settings_file'])) { |
| 101 |
die("File missing"); |
| 102 |
} |
| 103 |
|
| 104 |
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPressVIPMinimum.Performance.FetchingRemoteData.FileGetContentsUnknown -- Reads PHP's local upload temporary file, not a remote URL. |
| 105 |
$this->contents = @file_get_contents($_FILES['settings_file']['tmp_name']); |
| 106 |
if (false === $this->contents) { |
| 107 |
$this->error = true; |
| 108 |
TagGroups_Admin_Notice::add('error', __('Error reading the file.', 'tag-groups')); |
| 109 |
} |
| 110 |
} |
| 111 |
|
| 112 |
|
| 113 |
/** |
| 114 |
* Parses the content and saves the imported data to the database, depending on the file type |
| 115 |
* |
| 116 |
* @return void |
| 117 |
*/ |
| 118 |
// phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps, Squiz.Scope.MethodScope.Missing -- Legacy method naming |
| 119 |
function parse_and_save() |
| 120 |
{ |
| 121 |
|
| 122 |
if ($this->error) { |
| 123 |
return; |
| 124 |
} |
| 125 |
|
| 126 |
if ('settings' == $this->file_type) { |
| 127 |
$this->parse_and_save_options(); |
| 128 |
} elseif ('terms' == $this->file_type) { |
| 129 |
$this->parse_and_save_terms(); |
| 130 |
} |
| 131 |
} |
| 132 |
|
| 133 |
|
| 134 |
/** |
| 135 |
* Parses the content and saves the imported data to the options |
| 136 |
* |
| 137 |
* @return void |
| 138 |
*/ |
| 139 |
// phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps, Squiz.Scope.MethodScope.Missing -- Legacy method naming |
| 140 |
function parse_and_save_options() |
| 141 |
{ |
| 142 |
|
| 143 |
$options = @json_decode($this->contents, true); |
| 144 |
if (empty($options) || ! is_array($options) || $options['name'] != 'tag_groups_options') { |
| 145 |
$this->error = true; |
| 146 |
TagGroups_Admin_Notice::add('error', __('Error parsing the file.', 'tag-groups')); |
| 147 |
return; |
| 148 |
} |
| 149 |
|
| 150 |
$available_options = TagGroups_Options::get_available_options(); |
| 151 |
$count_changed = 0; |
| 152 |
// import only whitelisted options |
| 153 |
foreach ($available_options as $key => $value) { |
| 154 |
if ($available_options[ $key ]['export'] && isset($options[ $key ])) { |
| 155 |
$result = TagGroups_Options::update_option($key, $options[ $key ]) ? 1 : 0; |
| 156 |
if (1 == $result) { |
| 157 |
TagGroups_Error::verbose_log('[Tag Groups] We updated ' . $key); |
| 158 |
} |
| 159 |
|
| 160 |
$count_changed += $result; |
| 161 |
} |
| 162 |
} |
| 163 |
|
| 164 |
if (! isset($options['date'])) { |
| 165 |
$options['date'] = ' - ' . __('date unknown', 'tag-groups') . ' - '; |
| 166 |
} |
| 167 |
|
| 168 |
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.NonceVerification.Missing -- File upload validation |
| 169 |
TagGroups_Admin_Notice::add('success', sprintf( |
| 170 |
/* translators: 1: Filename, 2: Plugin version, 3: Export file creation date. */ |
| 171 |
__('Your settings and groups have been imported from the file %1$s (created with plugin version %2$s on %3$s).', 'tag-groups'), |
| 172 |
'<b>' . $_FILES['settings_file']['name'] . '</b>', //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.NonceVerification.Missing |
| 173 |
$options['version'], |
| 174 |
$options['date'] |
| 175 |
) . '</p><p>' . |
| 176 |
/* translators: %d is the number of options */ |
| 177 |
sprintf(_n('%d option was added or changed.', '%d options were added or changed.', $count_changed, 'tag-groups'), $count_changed)); |
| 178 |
do_action('tag_groups_settings_imported'); |
| 179 |
} |
| 180 |
|
| 181 |
|
| 182 |
/** |
| 183 |
* Parses the content and saves the imported data to the terms |
| 184 |
* |
| 185 |
* @return void |
| 186 |
*/ |
| 187 |
// phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps, Squiz.Scope.MethodScope.Missing -- Legacy method naming |
| 188 |
function parse_and_save_terms() |
| 189 |
{ |
| 190 |
|
| 191 |
$terms = @json_decode($this->contents, true); |
| 192 |
if (empty($terms) || ! is_array($terms) || $terms['name'] != 'tag_groups_terms') { |
| 193 |
$this->error = true; |
| 194 |
TagGroups_Admin_Notice::add('error', __('Error parsing the file.', 'tag-groups')); |
| 195 |
return; |
| 196 |
} |
| 197 |
|
| 198 |
$count_processed = 0; |
| 199 |
$count_changed = 0; |
| 200 |
$count_saved = 0; |
| 201 |
remove_all_filters('pre_insert_term'); |
| 202 |
remove_all_filters('wp_insert_term_data'); |
| 203 |
remove_all_actions('edit_terms'); |
| 204 |
remove_all_actions('edited_terms'); |
| 205 |
remove_all_actions('edit_term_taxonomy'); |
| 206 |
remove_all_actions('edited_term_taxonomy'); |
| 207 |
remove_all_actions('edit_term'); |
| 208 |
remove_all_actions('edited_term'); |
| 209 |
foreach ($terms['terms'] as $term) { |
| 210 |
$is_term_saved = false; |
| 211 |
$wp_term = get_term($term['term_id']); |
| 212 |
// change only terms with the same name, else create new one |
| 213 |
if (empty($wp_term)) { |
| 214 |
// check by name - maybe it has a different ID |
| 215 |
$term_by_name = get_term_by('name', $term['name'], $term['taxonomy']); |
| 216 |
if ($term_by_name && is_object($term_by_name)) { |
| 217 |
$term['term_id'] = $term_by_name->term_id; |
| 218 |
} else { |
| 219 |
$inserted_term = wp_insert_term($term['name'], $term['taxonomy']); |
| 220 |
if (is_array($inserted_term)) { |
| 221 |
$is_term_saved = true; |
| 222 |
$term['term_id'] = $inserted_term['term_id']; |
| 223 |
} else { |
| 224 |
// an error occured |
| 225 |
|
| 226 |
TagGroups_Error::log('[Tag Groups] Problem inserting ' . $term['name']); |
| 227 |
continue; |
| 228 |
} |
| 229 |
} |
| 230 |
|
| 231 |
$wp_term = get_term($term['term_id']); |
| 232 |
} |
| 233 |
|
| 234 |
$tg_term = new TagGroups_Term($term['term_id']); |
| 235 |
if (! $tg_term->has_exactly_groups($term['term_group'])) { |
| 236 |
$tg_term->set_group($term['term_group'])->save(); |
| 237 |
$count_changed++; |
| 238 |
} |
| 239 |
|
| 240 |
|
| 241 |
if ( |
| 242 |
$term['name'] != $wp_term->name || |
| 243 |
$term['slug'] != $wp_term->slug || |
| 244 |
$term['taxonomy'] != $wp_term->taxonomy || |
| 245 |
$term['description'] != $wp_term->description || // might lead to "false positives" if description contains invisible HTML |
| 246 |
$term['parent'] != $wp_term->parent |
| 247 |
) { |
| 248 |
// We update the default term data, except for term_group |
| 249 |
unset($term['term_group']); |
| 250 |
remove_all_actions("edit_{$term['taxonomy']}"); |
| 251 |
remove_all_actions("edited_{$term['taxonomy']}"); |
| 252 |
$result = wp_update_term($term['term_id'], $term['taxonomy'], $term); |
| 253 |
if (is_array($result)) { |
| 254 |
$is_term_saved = true; |
| 255 |
} |
| 256 |
} |
| 257 |
|
| 258 |
if ($is_term_saved) { |
| 259 |
$count_saved++; |
| 260 |
} |
| 261 |
|
| 262 |
$count_processed++; |
| 263 |
} |
| 264 |
|
| 265 |
|
| 266 |
if (! isset($terms['date'])) { |
| 267 |
$terms['date'] = ' - ' . __('date unknown', 'tag-groups') . ' - '; |
| 268 |
} |
| 269 |
|
| 270 |
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotValidated -- File upload validation |
| 271 |
TagGroups_Admin_Notice::add('success', sprintf( |
| 272 |
/* translators: 1: Filename, 2: Plugin version, 3: Export file creation date. */ |
| 273 |
__('Your terms have been imported from the file %1$s (created with plugin version %2$s on %3$s).', 'tag-groups'), |
| 274 |
'<b>' . $_FILES['settings_file']['name'] . '</b>', //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.NonceVerification.Missing |
| 275 |
$terms['version'], |
| 276 |
$terms['date'] |
| 277 |
) . '</p><p>' . |
| 278 |
/* translators: %d is the number of terms processed */ |
| 279 |
sprintf(_n('We processed %d term.', 'We processed %d terms.', $count_processed, 'tag-groups'), $count_processed) . '</p><p>' . |
| 280 |
/* translators: %d is the number of terms saved */ |
| 281 |
sprintf(_n('We saved %d term.', 'We saved %d terms.', $count_saved, 'tag-groups'), $count_saved) . '</p><p>' . |
| 282 |
/* translators: %d is the number of terms updated */ |
| 283 |
sprintf(_n('The group info of %d term was updated.', 'The group info of %d terms was updated.', $count_changed, 'tag-groups'), $count_changed)); |
| 284 |
do_action('tag_groups_terms_imported'); |
| 285 |
} |
| 286 |
} |
| 287 |
|
| 288 |
|
| 289 |
} |
| 290 |
|