help.php
13 years ago
home.php
13 years ago
import.php
13 years ago
manage.php
13 years ago
settings.php
13 years ago
import.php
1306 lines
| 1 | <?php |
| 2 | /** |
| 3 | * Import configuration wizard |
| 4 | * |
| 5 | * @author Pavel Kulbakin <p.kulbakin@gmail.com> |
| 6 | */ |
| 7 | |
| 8 | class PMXI_Admin_Import extends PMXI_Controller_Admin { |
| 9 | protected $isWizard = true; // indicates whether controller is in wizard mode (otherwize it called to be deligated an edit action) |
| 10 | protected $isTemplateEdit = false; // indicates whether controlled is deligated by manage imports controller |
| 11 | protected $csv_mimes = array('text/comma-separated-values','text/csv','application/csv','application/excel','application/vnd.ms-excel','application/vnd.msexcel','text/anytext'); |
| 12 | |
| 13 | protected function init() { |
| 14 | parent::init(); |
| 15 | |
| 16 | // enable sessions |
| 17 | if ( ! session_id()) session_start(); |
| 18 | |
| 19 | if ('PMXI_Admin_Manage' == PMXI_Plugin::getInstance()->getAdminCurrentScreen()->base) { // prereqisites are not checked when flow control is deligated |
| 20 | $id = $this->input->get('id'); |
| 21 | $this->data['import'] = $import = new PMXI_Import_Record(); |
| 22 | if ( ! $id or $import->getById($id)->isEmpty()) { // specified import is not found |
| 23 | wp_redirect(add_query_arg('page', 'pmxi-admin-manage', admin_url('admin.php'))); die(); |
| 24 | } |
| 25 | $this->isWizard = false; |
| 26 | |
| 27 | } else { |
| 28 | $action = PMXI_Plugin::getInstance()->getAdminCurrentScreen()->action; |
| 29 | $this->_step_ready($action); |
| 30 | $this->isInline = 'process' == $action; |
| 31 | } |
| 32 | |
| 33 | XmlImportConfig::getInstance()->setCacheDirectory(sys_get_temp_dir()); |
| 34 | |
| 35 | // preserve id parameter as part of baseUrl |
| 36 | $id = $this->input->get('id') and $this->baseUrl = add_query_arg('id', $id, $this->baseUrl); |
| 37 | } |
| 38 | |
| 39 | public function set($var, $val) |
| 40 | { |
| 41 | $this->{$var} = $val; |
| 42 | } |
| 43 | public function get($var) |
| 44 | { |
| 45 | return $this->{$var}; |
| 46 | } |
| 47 | |
| 48 | /** |
| 49 | * Checks whether corresponding step of wizard is complete |
| 50 | * @param string $action |
| 51 | */ |
| 52 | protected function _step_ready($action) { |
| 53 | // step #1: xml selction - has no prerequisites |
| 54 | if ('index' == $action) return true; |
| 55 | |
| 56 | // step #2: element selection |
| 57 | $this->data['dom'] = $dom = new DOMDocument(); |
| 58 | $this->data['update_previous'] = $update_previous = new PMXI_Import_Record(); |
| 59 | $old = libxml_use_internal_errors(true); |
| 60 | if (empty($_SESSION['pmxi_import']) |
| 61 | or ! $dom->loadXML(preg_replace('%xmlns\s*=\s*([\'"]).*\1%sU', '', $_SESSION['pmxi_import']['xml'])) // FIX: libxml xpath doesn't handle default namespace properly, so remove it upon XML load |
| 62 | or empty($_SESSION['pmxi_import']['source']) |
| 63 | or ! empty($_SESSION['pmxi_import']['update_previous']) and $update_previous->getById($_SESSION['pmxi_import']['update_previous'])->isEmpty() |
| 64 | ) { |
| 65 | wp_redirect_or_javascript($this->baseUrl); die(); |
| 66 | } |
| 67 | libxml_use_internal_errors($old); |
| 68 | if ('element' == $action) return true; |
| 69 | if ('evaluate' == $action) return true; |
| 70 | |
| 71 | // step #3: template |
| 72 | $xpath = new DOMXPath($dom); |
| 73 | if (empty($_SESSION['pmxi_import']['xpath']) or ! ($this->data['elements'] = $elements = $xpath->query($_SESSION['pmxi_import']['xpath'])) or ! $elements->length) { |
| 74 | wp_redirect_or_javascript(add_query_arg('action', 'element', $this->baseUrl)); die(); |
| 75 | } |
| 76 | if ('template' == $action or 'preview' == $action or 'tag' == $action) return true; |
| 77 | |
| 78 | // step #4: options |
| 79 | if (empty($_SESSION['pmxi_import']['template']) or empty($_SESSION['pmxi_import']['template']['title']) or empty($_SESSION['pmxi_import']['template']['title'])) { |
| 80 | wp_redirect_or_javascript(add_query_arg('action', 'template', $this->baseUrl)); die(); |
| 81 | } |
| 82 | if ('options' == $action) return true; |
| 83 | |
| 84 | if (empty($_SESSION['pmxi_import']['options'])) { |
| 85 | wp_redirect(add_query_arg('action', 'options', $this->baseUrl)); die(); |
| 86 | } |
| 87 | } |
| 88 | |
| 89 | /** |
| 90 | * Step #1: Choose File |
| 91 | */ |
| 92 | public function index() { |
| 93 | |
| 94 | $import = new PMXI_Import_Record(); |
| 95 | $this->data['id'] = $id = $this->input->get('id'); |
| 96 | if ($id and $import->getById($id)->isEmpty()) { // update requested but corresponding import is not found |
| 97 | wp_redirect(remove_query_arg('id', $this->baseUrl)); die(); |
| 98 | } |
| 99 | |
| 100 | $this->data['post'] = $post = $this->input->post(array( |
| 101 | 'type' => 'upload', |
| 102 | 'url' => 'http://', |
| 103 | 'ftp' => array('url' => 'ftp://'), |
| 104 | 'file' => '', |
| 105 | 'reimport' => '', |
| 106 | 'is_update_previous' => $id ? 1 : 0, |
| 107 | 'update_previous' => $id, |
| 108 | )); |
| 109 | |
| 110 | $this->data['imports'] = $imports = new PMXI_Import_List(); |
| 111 | $imports->setColumns('id', 'name', 'registered_on', 'path')->getBy(NULL, 'name ASC, registered_on DESC'); |
| 112 | |
| 113 | $this->data['history'] = $history = new PMXI_File_List(); |
| 114 | $history->setColumns('id', 'name', 'registered_on', 'path')->getBy(NULL, 'id DESC'); |
| 115 | |
| 116 | if ($this->input->post('is_submitted_continue')) { |
| 117 | if ( ! empty($_SESSION['pmxi_import']['xml'])) { |
| 118 | wp_redirect(add_query_arg('action', 'element', $this->baseUrl)); die(); |
| 119 | } |
| 120 | } elseif ('upload' == $this->input->post('type')) { |
| 121 | if (empty($_FILES['upload']) or empty($_FILES['upload']['name'])) { |
| 122 | $this->errors->add('form-validation', __('XML/CSV file must be specified', 'pmxi_plugin')); |
| 123 | } elseif (empty($_FILES['upload']['size'])) { |
| 124 | $this->errors->add('form-validation', __('Uploaded file is empty', 'pmxi_plugin')); |
| 125 | } elseif ( ! preg_match('%\W(xml|gzip|csv)$%i', trim($_FILES['upload']['name'])) and !$this->detect_csv($_FILES['upload']['type'])) { |
| 126 | $this->errors->add('form-validation', __('Uploaded file must be XML, CSV or GZIP', 'pmxi_plugin')); |
| 127 | } elseif ( preg_match('%\W(csv)$%i', trim($_FILES['upload']['name'])) or $this->detect_csv($_FILES['upload']['type'])) { |
| 128 | $uploads = wp_upload_dir(); |
| 129 | if($uploads['error']){ |
| 130 | $this->errors->add('form-validation', __('Can not create upload folder. Permision denied', 'pmxi_plugin')); |
| 131 | } |
| 132 | // copy file in temporary folder |
| 133 | $fdata = file_get_contents($_FILES['upload']['tmp_name']); |
| 134 | $fdata = utf8_encode($fdata); |
| 135 | file_put_contents($uploads['path'] . '/' . trim(basename($_FILES['upload']['name'])), $fdata); |
| 136 | chmod($uploads['path'] . '/'. trim(basename($_FILES['upload']['name'])), "0777"); |
| 137 | // end file convertion |
| 138 | $xml = $this->csv_to_xml($uploads['path'] . '/' . trim(basename($_FILES['upload']['name']))); |
| 139 | if( is_array($xml) && isset($xml['error'])){ |
| 140 | $this->errors->add('form-validation', __($xml['error'], 'pmxi_plugin')); |
| 141 | } |
| 142 | else { |
| 143 | // delete file in temporary folder |
| 144 | unlink( $uploads['path'] .'/'. trim(basename($_FILES['upload']['name']) )); |
| 145 | $filename = $_FILES['upload']['tmp_name']; |
| 146 | |
| 147 | // Let's make sure the file exists and is writable first. |
| 148 | if (is_writable($filename)) { |
| 149 | |
| 150 | if (!$handle = fopen($filename, 'w')) { |
| 151 | $this->errors->add('form-validation', __('Cannot open file ' . $filename, 'pmxi_plugin')); |
| 152 | } |
| 153 | |
| 154 | // Write $somecontent to our opened file. |
| 155 | if (fwrite($handle, $xml) === FALSE) { |
| 156 | $this->errors->add('form-validation', __('Cannot write to file ' . $filename, 'pmxi_plugin')); |
| 157 | } |
| 158 | |
| 159 | fclose($handle); |
| 160 | |
| 161 | } else { |
| 162 | $this->errors->add('form-validation', __('The file' . $filename . 'is not writable', 'pmxi_plugin')); |
| 163 | } |
| 164 | $filePath = $_FILES['upload']['tmp_name']; |
| 165 | $source = array( |
| 166 | 'name' => $_FILES['upload']['name'], |
| 167 | 'type' => 'upload', |
| 168 | 'path' => '', |
| 169 | ); |
| 170 | } |
| 171 | } else { |
| 172 | $filePath = $_FILES['upload']['tmp_name']; |
| 173 | $source = array( |
| 174 | 'name' => $_FILES['upload']['name'], |
| 175 | 'type' => 'upload', |
| 176 | 'path' => '', |
| 177 | ); |
| 178 | } |
| 179 | } elseif ('url' == $this->input->post('type')) { |
| 180 | if (empty($post['url'])) { |
| 181 | $this->errors->add('form-validation', __('XML/CSV file must be specified', 'pmxi_plugin')); |
| 182 | } elseif ( ! preg_match('%^https?://%i', $post['url'])) { |
| 183 | $this->errors->add('form-validation', __('Specified URL has wrong format'), 'pmxi_plugin'); |
| 184 | } elseif ( preg_match('%\W(csv)$%i', trim($post['url'])) or strpos(file_get_contents($post['url']), '<?xml') === false) { |
| 185 | $uploads = wp_upload_dir(); |
| 186 | $fdata = file_get_contents($post['url']); |
| 187 | $fdata = utf8_encode($fdata); |
| 188 | $tmpname = md5(time()).'.csv'; |
| 189 | file_put_contents($uploads['path'] .'/'. $tmpname, $fdata); |
| 190 | $xml = $this->csv_to_xml($uploads['path'] .'/'. $tmpname); |
| 191 | if( is_array($xml) && isset($xml['error'])){ |
| 192 | $this->errors->add('form-validation', __($xml['error'], 'pmxi_plugin')); |
| 193 | } |
| 194 | else { |
| 195 | $filename = tempnam(XmlImportConfig::getInstance()->getCacheDirectory(), 'xim'); |
| 196 | |
| 197 | // Let's make sure the file exists and is writable first. |
| 198 | if (is_writable($filename)) { |
| 199 | |
| 200 | if (!$handle = fopen($filename, 'w')) { |
| 201 | $this->errors->add('form-validation', __('Cannot open file ' . $filename, 'pmxi_plugin')); |
| 202 | } |
| 203 | |
| 204 | // Write $somecontent to our opened file. |
| 205 | if (fwrite($handle, $xml) === FALSE) { |
| 206 | $this->errors->add('form-validation', __('Cannot write to file ' . $filename, 'pmxi_plugin')); |
| 207 | } |
| 208 | |
| 209 | fclose($handle); |
| 210 | |
| 211 | } else { |
| 212 | $this->errors->add('form-validation', __('The file' . $filename . 'is not writable', 'pmxi_plugin')); |
| 213 | } |
| 214 | $filePath = $filename; |
| 215 | $source = array( |
| 216 | 'name' => basename(parse_url($post['url'], PHP_URL_PATH)), |
| 217 | 'type' => 'url', |
| 218 | 'path' => $post['url'], |
| 219 | ); |
| 220 | } |
| 221 | }else { |
| 222 | $filePath = $post['url']; |
| 223 | $source = array( |
| 224 | 'name' => basename(parse_url($filePath, PHP_URL_PATH)), |
| 225 | 'type' => 'url', |
| 226 | 'path' => $filePath, |
| 227 | ); |
| 228 | |
| 229 | } |
| 230 | } elseif ('ftp' == $this->input->post('type')) { |
| 231 | if (empty($post['ftp']['url'])) { |
| 232 | $this->errors->add('form-validation', __('XML/CSV file must be specified', 'pmxi_plugin')); |
| 233 | } elseif ( ! preg_match('%^ftps?://%i', $post['ftp']['url'])) { |
| 234 | $this->errors->add('form-validation', __('Specified FTP resource has wrong format'), 'pmxi_plugin'); |
| 235 | } elseif ( preg_match('%\W(csv)$%i', trim($post['ftp']['url']))) { |
| 236 | // path to remote file |
| 237 | $remote_file = $post['ftp']['url']; |
| 238 | $local_file = tempnam(XmlImportConfig::getInstance()->getCacheDirectory(), 'xim'); |
| 239 | |
| 240 | // open some file to write to |
| 241 | $handle = fopen($local_file, 'w'); |
| 242 | |
| 243 | // set up basic connection |
| 244 | $ftp_url = $post['ftp']['url']; |
| 245 | $parsed_url = parse_url($ftp_url); |
| 246 | $ftp_server = $parsed_url['host'] ; |
| 247 | $conn_id = ftp_connect( $ftp_server ); |
| 248 | $is_ftp_ok = TRUE; |
| 249 | |
| 250 | // login with username and password |
| 251 | $ftp_user_name = $post['ftp']['user']; |
| 252 | $ftp_user_pass = $post['ftp']['pass']; |
| 253 | |
| 254 | // hide warning message |
| 255 | echo '<span style="display:none">'; |
| 256 | if ( !ftp_login($conn_id, $ftp_user_name, $ftp_user_pass) ){ |
| 257 | $this->errors->add('form-validation', __('Login authentication failed', 'pmxi_plugin')); |
| 258 | $is_ftp_ok = false; |
| 259 | } |
| 260 | echo '</span>'; |
| 261 | |
| 262 | |
| 263 | if ( $is_ftp_ok ){ |
| 264 | // try to download $remote_file and save it to $handle |
| 265 | if (!ftp_fget($conn_id, $handle, $parsed_url['path'], FTP_ASCII, 0)) { |
| 266 | $this->errors->add('form-validation', __('There was a problem while downloading' . $remote_file . 'to' . $local_file, 'pmxi_plugin')); |
| 267 | } |
| 268 | |
| 269 | // close the connection and the file handler |
| 270 | ftp_close($conn_id); |
| 271 | fclose($handle); |
| 272 | |
| 273 | // copy file in temporary folder |
| 274 | $uploads = wp_upload_dir(); |
| 275 | if($uploads['error']){ |
| 276 | $this->errors->add('form-validation', __('Can not create upload folder. Permision denied', 'pmxi_plugin')); |
| 277 | } |
| 278 | copy( $local_file, $uploads['path'] . basename($local_file)); |
| 279 | $url = $uploads['url'] . basename($local_file); |
| 280 | // convert file to utf8 |
| 281 | chmod($uploads['path'] . basename($local_file), '0755'); |
| 282 | $fdata = file_get_contents($url); |
| 283 | $fdata = utf8_encode($fdata); |
| 284 | file_put_contents($uploads['path'] . basename($local_file), $fdata); |
| 285 | // end file convertion |
| 286 | $xml = $this->csv_to_xml($uploads['path'] . basename($local_file)); |
| 287 | if( is_array($xml) && isset($xml['error'])){ |
| 288 | $this->errors->add('form-validation', __($xml['error'], 'pmxi_plugin')); |
| 289 | } |
| 290 | else { |
| 291 | unlink( $uploads['path'] . basename($local_file) ); |
| 292 | $filename = $local_file; |
| 293 | |
| 294 | // Let's make sure the file exists and is writable first. |
| 295 | if (is_writable($filename)) { |
| 296 | |
| 297 | if (!$handle = fopen($filename, 'w')) { |
| 298 | $this->errors->add('form-validation', __('Cannot open file ' . $filename, 'pmxi_plugin')); |
| 299 | } |
| 300 | |
| 301 | // Write $somecontent to our opened file. |
| 302 | if (fwrite($handle, $xml) === FALSE) { |
| 303 | $this->errors->add('form-validation', __('Cannot write to file ' . $filename, 'pmxi_plugin')); |
| 304 | } |
| 305 | |
| 306 | fclose($handle); |
| 307 | |
| 308 | } else { |
| 309 | $this->errors->add('form-validation', __('The file' . $filename . 'is not writable', 'pmxi_plugin')); |
| 310 | } |
| 311 | $filePath = $local_file; |
| 312 | $source = array( |
| 313 | 'name' => basename($local_file), |
| 314 | 'type' => 'ftp', |
| 315 | 'path' => $filePath, |
| 316 | ); |
| 317 | } |
| 318 | } |
| 319 | } else { |
| 320 | $filePath = $post['ftp']['url']; |
| 321 | if (isset($post['ftp']['user']) and $post['ftp']['user'] !== '') { |
| 322 | $filePath = preg_replace('%://([^@/]*@)?%', '://' . urlencode($post['ftp']['user']) . ':' . urlencode($post['ftp']['pass']) . '@', $filePath, 1); |
| 323 | } |
| 324 | $source = array( |
| 325 | 'name' => basename(parse_url($filePath, PHP_URL_PATH)), |
| 326 | 'type' => 'ftp', |
| 327 | 'path' => $filePath, |
| 328 | ); |
| 329 | } |
| 330 | } elseif ('file' == $this->input->post('type')) { |
| 331 | if (empty($post['file'])) { |
| 332 | $this->errors->add('form-validation', __('XML/CSV file must be specified', 'pmxi_plugin')); |
| 333 | } elseif (preg_match('%\W(csv)$%i', trim($post['file']))) { |
| 334 | $uploads = PMXI_Plugin::ROOT_DIR . '/upload/'; |
| 335 | $wp_uploads = wp_upload_dir(); |
| 336 | if($wp_uploads['error']){ |
| 337 | $this->errors->add('form-validation', __('Can not create upload folder. Permision denied', 'pmxi_plugin')); |
| 338 | } |
| 339 | // copy file in temporary folder |
| 340 | // hide warning message |
| 341 | echo '<span style="display:none">'; |
| 342 | copy( $uploads . $post['file'], $wp_uploads['path'] . basename($post['file'])); |
| 343 | echo '</span>'; |
| 344 | $url = $wp_uploads['url'] . basename($post['file']); |
| 345 | // convert file to utf8 |
| 346 | chmod($wp_uploads['path'] . basename($post['file']), '0755'); |
| 347 | $fdata = file_get_contents($url); |
| 348 | $fdata = utf8_encode($fdata); |
| 349 | file_put_contents($wp_uploads['path'] . basename($post['file']), $fdata); |
| 350 | // end file convertion |
| 351 | $xml = $this->csv_to_xml($wp_uploads['path'] . basename($post['file'])); |
| 352 | if( is_array($xml) && isset($xml['error'])){ |
| 353 | $this->errors->add('form-validation', __($xml['error'], 'pmxi_plugin')); |
| 354 | } |
| 355 | else { |
| 356 | $filename = $wp_uploads['path'] . basename($post['file']); |
| 357 | |
| 358 | // Let's make sure the file exists and is writable first. |
| 359 | if (is_writable($filename)) { |
| 360 | |
| 361 | if (!$handle = fopen($filename, 'w')) { |
| 362 | $this->errors->add('form-validation', __('Cannot open file ' . $filename, 'pmxi_plugin')); |
| 363 | } |
| 364 | |
| 365 | // Write $somecontent to our opened file. |
| 366 | if (fwrite($handle, $xml) === FALSE) { |
| 367 | $this->errors->add('form-validation', __('Cannot write to file ' . $filename, 'pmxi_plugin')); |
| 368 | } |
| 369 | |
| 370 | fclose($handle); |
| 371 | |
| 372 | } else { |
| 373 | $this->errors->add('form-validation', __('The file ' . $filename . ' is not writable or you use wildcard for CSV file', 'pmxi_plugin')); |
| 374 | } |
| 375 | $filePath = $wp_uploads['path'] . basename($post['file']); |
| 376 | $source = array( |
| 377 | 'name' => basename(parse_url($filePath, PHP_URL_PATH)), |
| 378 | 'type' => 'file', |
| 379 | 'path' => $filePath, |
| 380 | ); |
| 381 | } |
| 382 | } |
| 383 | else { |
| 384 | $filePath = PMXI_Plugin::ROOT_DIR . '/upload/' . $post['file']; |
| 385 | $source = array( |
| 386 | 'name' => basename(parse_url($filePath, PHP_URL_PATH)), |
| 387 | 'type' => 'file', |
| 388 | 'path' => $filePath, |
| 389 | ); |
| 390 | } |
| 391 | } elseif ('reimport' == $this->input->post('type')) { |
| 392 | if (empty($post['reimport'])) { |
| 393 | $this->errors->add('form-validation', __('XML/CSV file must be specified', 'pmxi_plugin')); |
| 394 | } |
| 395 | } |
| 396 | |
| 397 | if ($post['is_update_previous'] and empty($post['update_previous'])) { |
| 398 | $this->errors->add('form-validation', __('Previous import for update must be selected or uncheck `Update Previous Import` option to proceed with a new one', 'pmxi_plugin')); |
| 399 | } |
| 400 | |
| 401 | if ($this->input->post('is_submitted') and ! $this->errors->get_error_codes()) { |
| 402 | |
| 403 | check_admin_referer('choose-file', '_wpnonce_choose-file'); |
| 404 | |
| 405 | if ('reimport' == $this->input->post('type')) { // get file content from database |
| 406 | preg_match('%^#(\d+):%', $post['reimport'], $mtch) and $reimport_id = $mtch[1] or $reimport_id = 0; |
| 407 | $file = new PMXI_File_Record(); |
| 408 | if ( ! $reimport_id or $file->getById($reimport_id)->isEmpty()) { |
| 409 | $xml = FALSE; |
| 410 | } else { |
| 411 | $xml = $file->contents; |
| 412 | $source = array( |
| 413 | 'name' => $file->name, |
| 414 | 'type' => 'reimport', |
| 415 | 'path' => $file->path, |
| 416 | ); |
| 417 | } |
| 418 | } else { |
| 419 | if (in_array($this->input->post('type'), array('ftp', 'file'))) { // file may be specified by pattern |
| 420 | $file_path_array = @PMXI_Helper::safe_glob($filePath, PMXI_Helper::GLOB_NODIR | PMXI_Helper::GLOB_PATH); |
| 421 | if ($file_path_array) { |
| 422 | $filePath = array_shift($file_path_array); // take only 1st matching one |
| 423 | } else { |
| 424 | $filePath = FALSE; |
| 425 | } |
| 426 | } |
| 427 | |
| 428 | ob_start(); |
| 429 | $filePath && @readgzfile($filePath); |
| 430 | |
| 431 | $xml = ob_get_clean(); |
| 432 | |
| 433 | } |
| 434 | |
| 435 | if (PMXI_Import_Record::validateXml($xml, $this->errors)) { |
| 436 | // xml is valid |
| 437 | |
| 438 | $_SESSION['pmxi_import'] = array( |
| 439 | 'xml' => $xml, |
| 440 | 'source' => $source, |
| 441 | ); |
| 442 | |
| 443 | $update_previous = new PMXI_Import_Record(); |
| 444 | if ($post['is_update_previous'] and ! $update_previous->getById($post['update_previous'])->isEmpty()) { |
| 445 | $_SESSION['pmxi_import'] += array( |
| 446 | 'update_previous' => $update_previous->id, |
| 447 | 'xpath' => $update_previous->xpath, |
| 448 | 'template' => $update_previous->template, |
| 449 | 'options' => $update_previous->options, |
| 450 | ); |
| 451 | } else { |
| 452 | $_SESSION['pmxi_import']['update_previous'] = ''; |
| 453 | } |
| 454 | |
| 455 | wp_redirect(add_query_arg('action', 'element', $this->baseUrl)); die(); |
| 456 | } |
| 457 | } |
| 458 | |
| 459 | $this->render(); |
| 460 | } |
| 461 | |
| 462 | /** |
| 463 | * Step #2: Choose elements |
| 464 | */ |
| 465 | public function element() |
| 466 | { |
| 467 | |
| 468 | |
| 469 | $xpath = new DOMXPath($this->data['dom']); |
| 470 | $post = $this->input->post(array('xpath' => '')); |
| 471 | $this->data['post'] =& $post; |
| 472 | |
| 473 | if ($this->input->post('is_submitted')) { |
| 474 | check_admin_referer('choose-elements', '_wpnonce_choose-elements'); |
| 475 | if ('' == $post['xpath']) { |
| 476 | $this->errors->add('form-validation', __('No elements selected', 'pmxi_plugin')); |
| 477 | } else { |
| 478 | $node_list = @ $xpath->query($post['xpath']); // make sure only element selection is allowed; prevent parsing warning to be displayed |
| 479 | |
| 480 | if (FALSE === $node_list) { |
| 481 | $this->errors->add('form-validation', __('Invalid XPath expression', 'pmxi_plugin')); |
| 482 | } elseif ( ! $node_list->length) { |
| 483 | $this->errors->add('form-validation', __('No matching elements found for XPath expression specified', 'pmxi_plugin')); |
| 484 | } else { |
| 485 | foreach ($node_list as $el) { |
| 486 | if ( ! $el instanceof DOMElement) { |
| 487 | $this->errors->add('form-validation', __('XPath must match only elements', 'pmxi_plugin')); |
| 488 | break; |
| 489 | }; |
| 490 | } |
| 491 | } |
| 492 | } |
| 493 | if ( ! $this->errors->get_error_codes()) { |
| 494 | $_SESSION['pmxi_import']['xpath'] = $post['xpath']; |
| 495 | wp_redirect(add_query_arg('action', 'template', $this->baseUrl)); die(); |
| 496 | } |
| 497 | } else { |
| 498 | |
| 499 | $this->shrink_xml_element($this->data['dom']->documentElement); |
| 500 | |
| 501 | if (isset($_SESSION['pmxi_import']['xpath'])) { |
| 502 | $post['xpath'] = $_SESSION['pmxi_import']['xpath']; |
| 503 | if ( ! $xpath->query($post['xpath'])->length and ! empty($_SESSION['pmxi_import']['update_previous'])) { |
| 504 | $_GET['pmxi_nt'] = __('<b>Warning</b>: No matching elements found for XPath expression from the import being updated. It probably means that new XML file has different format. Though you can update XPath, procceed only if you sure about update operation being valid.', 'pmxi_plugin'); |
| 505 | } |
| 506 | } else { |
| 507 | // suggest 1st repeating element as default selection |
| 508 | $post['xpath'] = $this->xml_find_repeating($this->data['dom']->documentElement); |
| 509 | } |
| 510 | } |
| 511 | |
| 512 | // workaround to prevent rendered XML representation to eat memory since it has to be stored in momory when output is bufferred |
| 513 | $this->render(); |
| 514 | add_action('pmxi_action_after', array($this, 'element_after')); |
| 515 | } |
| 516 | public function element_after() |
| 517 | { |
| 518 | $this->render(); |
| 519 | } |
| 520 | |
| 521 | /** |
| 522 | * Helper to evaluate xpath and return matching elements as direct paths for javascript side to highlight them |
| 523 | */ |
| 524 | public function evaluate() |
| 525 | { |
| 526 | if ( ! PMXI_Plugin::getInstance()->getAdminCurrentScreen()->is_ajax) { // call is only valid when send with ajax |
| 527 | wp_redirect(add_query_arg('action', 'element', $this->baseUrl)); die(); |
| 528 | } |
| 529 | |
| 530 | $xpath = new DOMXPath($this->data['dom']); |
| 531 | $post = $this->input->post(array('xpath' => '')); |
| 532 | if ('' == $post['xpath']) { |
| 533 | $this->errors->add('form-validation', __('No elements selected', 'pmxi_plugin')); |
| 534 | } else { |
| 535 | $node_list = @ $xpath->query($post['xpath']); // prevent parsing warning to be displayed |
| 536 | $this->data['node_list_count'] = $node_list->length; |
| 537 | if (FALSE === $node_list) { |
| 538 | $this->errors->add('form-validation', __('Invalid XPath expression', 'pmxi_plugin')); |
| 539 | } elseif ( ! $node_list->length) { |
| 540 | $this->errors->add('form-validation', __('No matching elements found for XPath expression specified', 'pmxi_plugin')); |
| 541 | } else { |
| 542 | foreach ($node_list as $el) { |
| 543 | if ( ! $el instanceof DOMElement) { |
| 544 | $this->errors->add('form-validation', __('XPath must match only elements', 'pmxi_plugin')); |
| 545 | break; |
| 546 | }; |
| 547 | } |
| 548 | } |
| 549 | } |
| 550 | if ( ! $this->errors->get_error_codes()) { |
| 551 | $this->shrink_xml_element($this->data['dom']->documentElement); |
| 552 | $xpath = new DOMXPath($this->data['dom']); |
| 553 | $this->data['node_list'] = $node_list = @ $xpath->query($post['xpath']); // prevent parsing warning to be displayed |
| 554 | |
| 555 | $paths = array(); $this->data['paths'] =& $paths; |
| 556 | if (PMXI_Plugin::getInstance()->getOption('highlight_limit') and $node_list->length <= PMXI_Plugin::getInstance()->getOption('highlight_limit')) { |
| 557 | foreach ($node_list as $el) { |
| 558 | if ( ! $el instanceof DOMElement) continue; |
| 559 | |
| 560 | $p = $this->get_xml_path($el, $xpath) and $paths[] = $p; |
| 561 | } |
| 562 | } |
| 563 | $this->render(); |
| 564 | } else { |
| 565 | $this->error(); |
| 566 | } |
| 567 | } |
| 568 | |
| 569 | /** |
| 570 | * Step #3: Choose template |
| 571 | */ |
| 572 | public function template() |
| 573 | { |
| 574 | |
| 575 | $template = new PMXI_Template_Record(); |
| 576 | $default = array( |
| 577 | 'title' => '', |
| 578 | 'content' => '', |
| 579 | 'name' => '', |
| 580 | 'is_keep_linebreaks' => 0, |
| 581 | ); |
| 582 | if ($this->isWizard) { |
| 583 | $this->data['post'] = $post = $this->input->post( |
| 584 | (isset($_SESSION['pmxi_import']['template']) ? $_SESSION['pmxi_import']['template'] : array()) |
| 585 | + $default |
| 586 | ); |
| 587 | } else { |
| 588 | $this->data['post'] = $post = $this->input->post( |
| 589 | $this->data['import']->template |
| 590 | + $default |
| 591 | ); |
| 592 | } |
| 593 | |
| 594 | if (($load_template = $this->input->post('load_template'))) { // init form with template selected |
| 595 | if ( ! $template->getById($load_template)->isEmpty()) { |
| 596 | $this->data['post'] = array( |
| 597 | 'title' => $template->title, |
| 598 | 'content' => $template->content, |
| 599 | 'is_keep_linebreaks' => $template->is_keep_linebreaks, |
| 600 | 'name' => '', // template is always empty |
| 601 | ); |
| 602 | $_SESSION['pmxi_import']['is_loaded_template'] = $load_template; |
| 603 | } |
| 604 | |
| 605 | } elseif ($this->input->post('is_submitted')) { // save template submission |
| 606 | check_admin_referer('template', '_wpnonce_template'); |
| 607 | |
| 608 | if (empty($post['title'])) { |
| 609 | $this->errors->add('form-validation', __('Post title is empty', 'pmxi_plugin')); |
| 610 | } else { |
| 611 | $this->_validate_template($post['title'], 'Post title'); |
| 612 | } |
| 613 | if (empty($post['content'])) { |
| 614 | $this->errors->add('form-validation', __('Post content is empty', 'pmxi_plugin')); |
| 615 | } else { |
| 616 | $this->_validate_template($post['content'], 'Post content'); |
| 617 | } |
| 618 | |
| 619 | |
| 620 | if ( ! $this->errors->get_error_codes()) { |
| 621 | if ( ! empty($post['name'])) { // save template in database |
| 622 | $template->getByName($post['name'])->set($post)->save(); |
| 623 | $_SESSION['pmxi_import']['saved_template'] = $template->id; |
| 624 | } |
| 625 | if ($this->isWizard) { |
| 626 | $_SESSION['pmxi_import']['template'] = $post; |
| 627 | wp_redirect(add_query_arg('action', 'options', $this->baseUrl)); die(); |
| 628 | } else { |
| 629 | $this->data['import']->set('template', $post)->save(); |
| 630 | wp_redirect(add_query_arg(array('page' => 'pmxi-admin-manage', 'pmlc_nt' => urlencode(__('Template updated', 'pmxi_plugin'))) + array_intersect_key($_GET, array_flip($this->baseUrlParamNames)), admin_url('admin.php'))); die(); |
| 631 | } |
| 632 | } |
| 633 | } |
| 634 | |
| 635 | if (user_can_richedit()) { |
| 636 | wp_enqueue_script('editor'); |
| 637 | } |
| 638 | wp_enqueue_script('word-count'); |
| 639 | add_thickbox(); |
| 640 | wp_enqueue_script('media-upload'); |
| 641 | add_action('admin_print_footer_scripts', 'wp_tiny_mce', 25); |
| 642 | wp_enqueue_script('quicktags'); |
| 643 | $this->render(); |
| 644 | } |
| 645 | protected function _validate_template($text, $field_title) |
| 646 | { |
| 647 | try { |
| 648 | $scanner = new XmlImportTemplateScanner(); |
| 649 | $tokens = $scanner->scan(new XmlImportStringReader($text)); |
| 650 | $parser = new XmlImportTemplateParser($tokens); |
| 651 | $tree = $parser->parse(); |
| 652 | } catch (XmlImportException $e) { |
| 653 | $this->errors->add('form-validation', sprintf(__('%s template is invalid: %s', 'pmxi_plugin'), $field_title, $e->getMessage())); |
| 654 | } |
| 655 | } |
| 656 | |
| 657 | /** |
| 658 | * Preview selected xml tag (called with ajax from `template` step) |
| 659 | */ |
| 660 | public function tag() |
| 661 | { |
| 662 | if (empty($this->data['elements'])) |
| 663 | { |
| 664 | |
| 665 | $update_previous = new PMXI_Import_Record(); |
| 666 | if ($update_previous->getById($this->input->get('id'))) { |
| 667 | $_SESSION['pmxi_import'] = array( |
| 668 | 'update_previous' => $update_previous->id, |
| 669 | 'xpath' => $update_previous->xpath, |
| 670 | 'template' => $update_previous->template, |
| 671 | 'options' => $update_previous->options, |
| 672 | ); |
| 673 | $history_file = new PMXI_File_Record(); |
| 674 | $history_file->getBy('import_id', $update_previous->id); |
| 675 | $history_file->__get('contents'); |
| 676 | $_SESSION['pmxi_import']['xml'] = $history_file->contents; |
| 677 | } else { |
| 678 | $_SESSION['pmxi_import']['update_previous'] = ''; |
| 679 | } |
| 680 | if (!empty($_SESSION['pmxi_import']['xml'])) |
| 681 | { |
| 682 | $dom = new DOMDocument(); |
| 683 | $dom->loadXML(preg_replace('%xmlns\s*=\s*([\'"]).*\1%sU', '', $_SESSION['pmxi_import']['xml'])); |
| 684 | $xpath = new DOMXPath($dom); |
| 685 | |
| 686 | $this->data['elements'] = $elements = $xpath->query($_SESSION['pmxi_import']['xpath']); |
| 687 | } |
| 688 | } |
| 689 | |
| 690 | $this->data['tagno'] = min(max(intval($this->input->getpost('tagno', 1)), 1), $this->data['elements']->length); |
| 691 | $this->render(); |
| 692 | } |
| 693 | |
| 694 | /** |
| 695 | * Preview future post based on current template and tag (called with ajax from `template` step) |
| 696 | */ |
| 697 | public function preview() |
| 698 | { |
| 699 | $post = $this->input->post(array( |
| 700 | 'title' => '', |
| 701 | 'content' => '', |
| 702 | 'is_keep_linebreaks' => 0, |
| 703 | )); |
| 704 | $tagno = min(max(intval($this->input->getpost('tagno', 1)), 1), $this->data['elements']->length); |
| 705 | $xpath = "(" . $_SESSION['pmxi_import']['xpath'] . ")[$tagno]"; |
| 706 | // validate |
| 707 | try { |
| 708 | |
| 709 | if (empty($post['title'])) { |
| 710 | $this->errors->add('form-validation', __('Post title is empty', 'pmxi_plugin')); |
| 711 | } else { |
| 712 | list($this->data['title']) = XmlImportParser::factory($_SESSION['pmxi_import']['xml'], $xpath, $post['title'], $file)->parse(); unlink($file); |
| 713 | if ( ! isset($this->data['title']) or '' == strval(trim(strip_tags($this->data['title'], '<img><input><textarea><iframe><object><embed>')))) { |
| 714 | $this->errors->add('xml-parsing', __('<strong>Warning</strong>: resulting post title is empty', 'pmxi_plugin')); |
| 715 | } |
| 716 | } |
| 717 | } catch (XmlImportException $e) { |
| 718 | $this->errors->add('form-validation', sprintf(__('Error parsing title: %s', 'pmxi_plugin'), $e->getMessage())); |
| 719 | } |
| 720 | try { |
| 721 | if (empty($post['content'])) { |
| 722 | $this->errors->add('form-validation', __('Post content is empty', 'pmxi_plugin')); |
| 723 | } else { |
| 724 | list($this->data['content']) = XmlImportParser::factory($post['is_keep_linebreaks'] ? $_SESSION['pmxi_import']['xml'] : preg_replace('%\r\n?|\n%', ' ', $_SESSION['pmxi_import']['xml']), $xpath, $post['content'], $file)->parse(); unlink($file); |
| 725 | if ( ! isset($this->data['content']) or '' == strval(trim(strip_tags($this->data['content'], '<img><input><textarea><iframe><object><embed>')))) { |
| 726 | $this->errors->add('xml-parsing', __('<strong>Warning</strong>: resulting post content is empty', 'pmxi_plugin')); |
| 727 | } |
| 728 | } |
| 729 | } catch (XmlImportException $e) { |
| 730 | $this->errors->add('form-validation', sprintf(__('Error parsing content: %s', 'pmxi_plugin'), $e->getMessage())); |
| 731 | } |
| 732 | |
| 733 | $this->render(); |
| 734 | } |
| 735 | |
| 736 | /** |
| 737 | * Step #4: Options |
| 738 | */ |
| 739 | public function options() |
| 740 | { |
| 741 | include_once(PMXI_Plugin::ROOT_DIR.'/libraries/XmlImportCsvParse.php'); |
| 742 | |
| 743 | $default = PMXI_Plugin::get_default_import_options(); |
| 744 | |
| 745 | if ($this->isWizard) { |
| 746 | $this->data['source_type'] = $_SESSION['pmxi_import']['source']['type']; |
| 747 | $default['unique_key'] = $_SESSION['pmxi_import']['template']['title']; |
| 748 | $post = $this->input->post( |
| 749 | (isset($_SESSION['pmxi_import']['options']) ? $_SESSION['pmxi_import']['options'] : array()) |
| 750 | + $default |
| 751 | ); |
| 752 | |
| 753 | $scheduled = $this->input->post(array( |
| 754 | 'is_scheduled' => ! empty($post['scheduled']), |
| 755 | 'scheduled_period' => ! empty($post['scheduled']) ? $post['scheduled'] : '0 0 * * *', // daily by default |
| 756 | )); |
| 757 | |
| 758 | } else { |
| 759 | $this->data['source_type'] = $this->data['import']->type; |
| 760 | $post = $this->input->post( |
| 761 | $this->data['import']->options |
| 762 | + $default |
| 763 | ); |
| 764 | $scheduled = $this->input->post(array( |
| 765 | 'is_scheduled' => ! empty($this->data['import']->scheduled), |
| 766 | 'scheduled_period' => ! empty($this->data['import']->scheduled) ? $this->data['import']->scheduled : '0 0 * * *', // daily by default |
| 767 | )); |
| 768 | } |
| 769 | $this->data['post'] =& $post; |
| 770 | $this->data['scheduled'] =& $scheduled; |
| 771 | $this->data['is_loaded_template'] = $_SESSION['pmxi_import']['is_loaded_template']; |
| 772 | |
| 773 | if (($load_options = $this->input->post('load_options'))) { // init form with template selected |
| 774 | $this->data['load_options'] = true; |
| 775 | $template = new PMXI_Template_Record(); |
| 776 | if ( ! $template->getById($this->data['is_loaded_template'])->isEmpty()) { |
| 777 | $post = $template->options + $default; |
| 778 | $scheduled = array( |
| 779 | 'is_scheduled' => ! empty($template->scheduled), |
| 780 | 'scheduled_period' => ! empty($template->scheduled) ? $template->scheduled : '0 0 * * *', // daily by default |
| 781 | ); |
| 782 | } |
| 783 | |
| 784 | } elseif (($reset_options = $this->input->post('reset_options'))){ |
| 785 | $post = $default; |
| 786 | $scheduled = $this->input->post(array( |
| 787 | 'is_scheduled' => ! empty($post['scheduled']), |
| 788 | 'scheduled_period' => ! empty($post['scheduled']) ? $post['scheduled'] : '0 0 * * *', // daily by default |
| 789 | )); |
| 790 | } elseif ($this->input->post('is_submitted')) { |
| 791 | check_admin_referer('options', '_wpnonce_options'); |
| 792 | // remove entires where both custom_name and custom_value are empty |
| 793 | $not_empty = array_flip(array_values(array_merge(array_keys(array_filter($post['custom_name'])), array_keys(array_filter($post['custom_value']))))); |
| 794 | $post['custom_name'] = array_intersect_key($post['custom_name'], $not_empty); |
| 795 | $post['custom_value'] = array_intersect_key($post['custom_value'], $not_empty); |
| 796 | // validate |
| 797 | if (array_keys(array_filter($post['custom_name'])) != array_keys(array_filter($post['custom_value']))) { |
| 798 | $this->errors->add('form-validation', __('Both name and value must be set for all custom parameters', 'pmxi_plugin')); |
| 799 | } else { |
| 800 | foreach ($post['custom_name'] as $custom_name) { |
| 801 | $this->_validate_template($custom_name, __('Custom Field Name', 'pmxi_plugin')); |
| 802 | } |
| 803 | foreach ($post['custom_value'] as $custom_value) { |
| 804 | $this->_validate_template($custom_value, __('Custom Field Value', 'pmxi_plugin')); |
| 805 | } |
| 806 | } |
| 807 | if ('page' == $post['type'] and ! preg_match('%^(-?\d+)?$%', $post['order'])) { |
| 808 | $this->errors->add('form-validation', __('Order must be an integer number', 'pmxi_plugin')); |
| 809 | } |
| 810 | if ('post' == $post['type']) { |
| 811 | '' == $post['categories'] or $this->_validate_template($post['categories'], __('Categories', 'pmxi_plugin')); |
| 812 | '' == $post['tags'] or $this->_validate_template($post['tags'], __('Tags', 'pmxi_plugin')); |
| 813 | } |
| 814 | if ('specific' == $post['date_type']) { |
| 815 | '' == $post['date'] or $this->_validate_template($post['date'], __('Date', 'pmxi_plugin')); |
| 816 | } else { |
| 817 | '' == $post['date_start'] or $this->_validate_template($post['date_start'], __('Start Date', 'pmxi_plugin')); |
| 818 | '' == $post['date_end'] or $this->_validate_template($post['date_end'], __('Start Date', 'pmxi_plugin')); |
| 819 | } |
| 820 | if ('' == $post['categories_delim']) { |
| 821 | $this->errors->add('form-validation', __('Category list delimiter cannot be empty', 'pmxi_plugin')); |
| 822 | } |
| 823 | if ('' == $post['tags_delim']) { |
| 824 | $this->errors->add('form-validation', __('Tag list delimiter must cannot be empty', 'pmxi_plugin')); |
| 825 | } |
| 826 | if ($post['is_import_specified']) { |
| 827 | if (empty($post['import_specified'])) { |
| 828 | $this->errors->add('form-validation', __('Records to import must be specified or uncheck `Import only specified records` option to process all records', 'pmxi_plugin')); |
| 829 | } else { |
| 830 | $chanks = preg_split('% *, *%', $post['import_specified']); |
| 831 | foreach ($chanks as $chank) { |
| 832 | if ( ! preg_match('%^([1-9]\d*)( *- *([1-9]\d*))?$%', $chank, $mtch)) { |
| 833 | $this->errors->add('form-validation', __('Wrong format of `Import only specified records` value', 'pmxi_plugin')); |
| 834 | break; |
| 835 | } |
| 836 | } |
| 837 | } |
| 838 | } |
| 839 | if ('' == $post['unique_key']) { |
| 840 | $this->errors->add('form-validation', __('Expression for `Post Unique Key` must be set, use the same expression as specified for post title if you are not sure what to put there', 'pmxi_plugin')); |
| 841 | } else { |
| 842 | $this->_validate_template($post['unique_key'], __('Post Unique Key', 'pmxi_plugin')); |
| 843 | } |
| 844 | |
| 845 | if ( ! $this->errors->get_error_codes()) { // no validation errors found |
| 846 | // assign some defaults |
| 847 | '' !== $post['date'] or $post['date'] = 'now'; |
| 848 | '' !== $post['date_start'] or $post['date_start'] = 'now'; |
| 849 | '' !== $post['date_end'] or $post['date_end'] = 'now'; |
| 850 | |
| 851 | if ($this->isWizard) { |
| 852 | $_SESSION['pmxi_import']['options'] = $post; |
| 853 | $_SESSION['pmxi_import']['scheduled'] = $scheduled['is_scheduled'] ? $scheduled['scheduled_period'] : ''; |
| 854 | |
| 855 | // Update template options |
| 856 | if (!empty($_SESSION['pmxi_import']['saved_template'])) { |
| 857 | $template = new PMXI_Template_Record(); |
| 858 | $template->getById($_SESSION['pmxi_import']['saved_template'])->set(array( |
| 859 | 'options' => $_SESSION['pmxi_import']['options'], |
| 860 | 'scheduled' => $_SESSION['pmxi_import']['scheduled']))->update(); |
| 861 | } |
| 862 | elseif (!empty($_SESSION['pmxi_import']['is_loaded_template'])) |
| 863 | { |
| 864 | $template = new PMXI_Template_Record(); |
| 865 | $template->getById($_SESSION['pmxi_import']['is_loaded_template'])->set(array( |
| 866 | 'options' => $_SESSION['pmxi_import']['options'], |
| 867 | 'scheduled' => $_SESSION['pmxi_import']['scheduled']))->update(); |
| 868 | } |
| 869 | |
| 870 | if ( ! $this->input->post('save_only')) { |
| 871 | $this->process();die(); |
| 872 | } else { |
| 873 | $import = $this->data['update_previous']; |
| 874 | $is_update = ! $import->isEmpty(); |
| 875 | $import->set( |
| 876 | $_SESSION['pmxi_import']['source'] |
| 877 | + array( |
| 878 | 'xpath' => $_SESSION['pmxi_import']['xpath'], |
| 879 | 'template' => $_SESSION['pmxi_import']['template'], |
| 880 | 'options' => $_SESSION['pmxi_import']['options'], |
| 881 | 'scheduled' => $_SESSION['pmxi_import']['scheduled'], |
| 882 | ) |
| 883 | )->save(); |
| 884 | |
| 885 | $history_file = new PMXI_File_Record(); |
| 886 | $history_file->set(array( |
| 887 | 'name' => $import->name, |
| 888 | 'import_id' => $import->id, |
| 889 | 'path' => $import->path, |
| 890 | 'contents' => $_SESSION['pmxi_import']['xml'], |
| 891 | 'registered_on' => date('Y-m-d H:i:s'), |
| 892 | ))->save(); |
| 893 | unset($_SESSION['pmxi_import']); // clear session data |
| 894 | wp_redirect(add_query_arg(array('page' => 'pmxi-admin-manage', 'pmlc_nt' => urlencode($is_update ? __('Import updated', 'pmxi_plugin') : __('Import created', 'pmxi_plugin'))), admin_url('admin.php'))); die(); |
| 895 | } |
| 896 | } else { |
| 897 | $this->data['import']->set('options', $post)->set('scheduled', $scheduled['is_scheduled'] ? $scheduled['scheduled_period'] : '')->save(); |
| 898 | wp_redirect(add_query_arg(array('page' => 'pmxi-admin-manage', 'pmlc_nt' => urlencode(__('Options updated', 'pmxi_plugin'))) + array_intersect_key($_GET, array_flip($this->baseUrlParamNames)), admin_url('admin.php'))); die(); |
| 899 | } |
| 900 | } |
| 901 | } |
| 902 | |
| 903 | ! empty($post['custom_name']) or $post['custom_name'] = array('') and $post['custom_value'] = array(''); |
| 904 | |
| 905 | $this->render(); |
| 906 | } |
| 907 | |
| 908 | /** |
| 909 | * Import processing step (status console) |
| 910 | */ |
| 911 | public function process() |
| 912 | { |
| 913 | wp_ob_end_flush_all(); flush(); |
| 914 | |
| 915 | // store import info in database |
| 916 | $import = $this->data['update_previous']; |
| 917 | |
| 918 | if ($_SESSION['pmxi_import']['options']['is_first_chank'] or !$_SESSION['pmxi_import']['options']['is_update_previous']) |
| 919 | { |
| 920 | $_SESSION['pmxi_import']['step'] = 0; |
| 921 | $import->set( |
| 922 | $_SESSION['pmxi_import']['source'] |
| 923 | + array( |
| 924 | 'xpath' => $_SESSION['pmxi_import']['xpath'], |
| 925 | 'template' => $_SESSION['pmxi_import']['template'], |
| 926 | 'options' => $_SESSION['pmxi_import']['options'], |
| 927 | 'scheduled' => $_SESSION['pmxi_import']['scheduled'] |
| 928 | ) |
| 929 | )->save(); |
| 930 | |
| 931 | $history_file = new PMXI_File_Record(); |
| 932 | $history_file->set(array( |
| 933 | 'name' => $import->name, |
| 934 | 'import_id' => $import->id, |
| 935 | 'path' => $import->path, |
| 936 | 'contents' => $_SESSION['pmxi_import']['xml'], |
| 937 | 'registered_on' => date('Y-m-d H:i:s'), |
| 938 | ))->save(); |
| 939 | |
| 940 | $_SESSION['pmxi_import']['import_start'] = date('H:i:s'); |
| 941 | } |
| 942 | else |
| 943 | { |
| 944 | $_SESSION['pmxi_import']['step']++; |
| 945 | $import = new PMXI_Import_Record(); |
| 946 | $import->getById($_SESSION['pmxi_import']['options']['is_update_previous']); |
| 947 | } |
| 948 | |
| 949 | $logger = create_function('$m', 'echo "<div class=\\"progress-msg\\">$m</div>\\n"; flush();'); |
| 950 | if (in_array($import->type, array('ftp', 'file'))) { // process files by patten |
| 951 | $import->execute($logger); |
| 952 | } else { // directly process XML |
| 953 | set_time_limit(0); |
| 954 | $xml = new SimpleXMLElement($_SESSION['pmxi_import']['xml']); |
| 955 | $rootNodes = $xml->xpath($_SESSION['pmxi_import']['xpath']); |
| 956 | $this->removeNode($xml, $_SESSION['pmxi_import']['xpath'].'[position() >= 10]'); |
| 957 | |
| 958 | if (count($xml->xpath($_SESSION['pmxi_import']['xpath']))) |
| 959 | { |
| 960 | ob_start(); |
| 961 | $import->process($xml->asXML(), $logger, ((count($rootNodes) < 10) ? true : false)); |
| 962 | |
| 963 | if ((count($rootNodes) < 10)) |
| 964 | { |
| 965 | // [indicate in header process is complete] |
| 966 | $msg = addcslashes(__('Complete', 'pmxi_plugin'), "'\n\r"); |
| 967 | echo <<<COMPLETE |
| 968 | <script type="text/javascript"> |
| 969 | //<![CDATA[ |
| 970 | (function($){ |
| 971 | $('#status').html('$msg'); |
| 972 | window.onbeforeunload = false; |
| 973 | })(jQuery); |
| 974 | //]]> |
| 975 | </script> |
| 976 | COMPLETE; |
| 977 | // [/indicate in header process is complete] |
| 978 | } |
| 979 | $log = ob_get_clean(); |
| 980 | } |
| 981 | unset($xml); |
| 982 | |
| 983 | $xml_chank = new SimpleXMLElement($_SESSION['pmxi_import']['xml']); |
| 984 | $this->removeNode($xml_chank, $_SESSION['pmxi_import']['xpath'].'[position() < 10]'); |
| 985 | |
| 986 | $_SESSION['pmxi_import']['xml'] = (count($rootNodes) >= 10) ? $xml_chank->asXML() : NULL; |
| 987 | unset($xml_chank); |
| 988 | } |
| 989 | |
| 990 | if (!empty($_SESSION['pmxi_import']['xml'])) |
| 991 | exit(json_encode(array('status' => 'process','update_previous' => $import->__get('id'), 'count' => count($rootNodes), 'is_last_chank' => ((count($rootNodes) < 10) ? true : false), 'is_first_chank' => $_SESSION['pmxi_import']['options']['is_first_chank'], 'log' => $log))); |
| 992 | else { |
| 993 | $start_time = $_SESSION['pmxi_import']['import_start']; |
| 994 | $end_time = date('H:i:s'); |
| 995 | unset($_SESSION['pmxi_import']); // clear session data (prevent from reimporting the same data on page refresh) |
| 996 | exit(json_encode(array('status' => 'stop', 'log' => $log, 'start_time' => $start_time, 'end_time' => $end_time, 'import_time' => date('H:i:s', strtotime($end_time) - strtotime($start_time))))); |
| 997 | }; |
| 998 | |
| 999 | |
| 1000 | } |
| 1001 | /** |
| 1002 | * Remove xml document nodes by xpath expression |
| 1003 | */ |
| 1004 | function removeNode($xml, $path) |
| 1005 | { |
| 1006 | $result = $xml->xpath($path); |
| 1007 | |
| 1008 | if (empty($result)) return false; |
| 1009 | $errlevel = error_reporting(E_ALL & ~E_WARNING); |
| 1010 | foreach ($result as $r) unset ($r[0]); |
| 1011 | error_reporting($errlevel); |
| 1012 | |
| 1013 | return true; |
| 1014 | } |
| 1015 | |
| 1016 | /* |
| 1017 | * |
| 1018 | * Get SimpleXML object by xpath extension |
| 1019 | * |
| 1020 | */ |
| 1021 | function get_chank(& $xml, $path){ |
| 1022 | |
| 1023 | $result = $xml->xpath($path); |
| 1024 | |
| 1025 | $array = array(); |
| 1026 | foreach ($result as $el) { |
| 1027 | array_push($array, $this->simplexml2array($el)); |
| 1028 | } |
| 1029 | |
| 1030 | if (empty($array)) return false; |
| 1031 | |
| 1032 | return new SimpleXMLElement(ArrayToXML::toXml($array)); |
| 1033 | } |
| 1034 | |
| 1035 | /* |
| 1036 | * |
| 1037 | * Convert SimpleXML object to array |
| 1038 | * |
| 1039 | */ |
| 1040 | function simplexml2array($xml) { |
| 1041 | if (@get_class($xml) == 'SimpleXMLElement') { |
| 1042 | $attributes = $xml->attributes(); |
| 1043 | foreach($attributes as $k=>$v) { |
| 1044 | if ($v) $a[$k] = (string) $v; |
| 1045 | } |
| 1046 | $x = $xml; |
| 1047 | $xml = get_object_vars($xml); |
| 1048 | } |
| 1049 | if (is_array($xml)) { |
| 1050 | if (count($xml) == 0) return (string) $x; // for CDATA |
| 1051 | foreach($xml as $key=>$value) { |
| 1052 | $r[$key] = $this->simplexml2array($value); |
| 1053 | } |
| 1054 | if (isset($a)) $r['@attributes'] = $a; // Attributes |
| 1055 | return $r; |
| 1056 | } |
| 1057 | return (string) $xml; |
| 1058 | } |
| 1059 | |
| 1060 | protected $_sibling_limit = 20; |
| 1061 | protected function get_xml_path(DOMElement $el, DOMXPath $xpath) |
| 1062 | { |
| 1063 | for($p = '', $doc = $el; $doc and ! $doc instanceof DOMDocument; $doc = $doc->parentNode) { |
| 1064 | if (($ind = $xpath->query('preceding-sibling::' . $doc->nodeName, $doc)->length)) { |
| 1065 | $p = '[' . ++$ind . ']' . $p; |
| 1066 | } elseif ( ! $doc->parentNode instanceof DOMDocument) { |
| 1067 | $p = '[' . ($ind = 1) . ']' . $p; |
| 1068 | } |
| 1069 | $p = '/' . $doc->nodeName . $p; |
| 1070 | } |
| 1071 | return $p; |
| 1072 | } |
| 1073 | |
| 1074 | protected function shrink_xml_element(DOMElement $el) |
| 1075 | { |
| 1076 | $prev = null; $sub_ind = null; |
| 1077 | for ($i = 0; $i < $el->childNodes->length; $i++) { |
| 1078 | $child = $el->childNodes->item($i); |
| 1079 | if ($child instanceof DOMText) { |
| 1080 | if ('' == trim($child->wholeText)) { |
| 1081 | $el->removeChild($child); |
| 1082 | $i--; |
| 1083 | continue; |
| 1084 | } |
| 1085 | } |
| 1086 | if ($child instanceof DOMComment) { |
| 1087 | continue; |
| 1088 | } |
| 1089 | if ($prev instanceof $child and $prev->nodeName == $child->nodeName) { |
| 1090 | $sub_ind++; |
| 1091 | } else { |
| 1092 | if ($sub_ind > $this->_sibling_limit) { |
| 1093 | $el->insertBefore(new DOMComment('[pmxi_more:' . ($sub_ind - $this->_sibling_limit) . ']'), $child); |
| 1094 | $i++; |
| 1095 | } |
| 1096 | $sub_ind = 1; |
| 1097 | $prev = null; |
| 1098 | } |
| 1099 | if ($child instanceof DOMElement) { |
| 1100 | $prev = $child; |
| 1101 | if ($sub_ind <= $this->_sibling_limit) { |
| 1102 | $this->shrink_xml_element($child); |
| 1103 | } else { |
| 1104 | $el->removeChild($child); |
| 1105 | $i--; |
| 1106 | } |
| 1107 | } |
| 1108 | } |
| 1109 | if ($sub_ind > $this->_sibling_limit) { |
| 1110 | $el->appendChild(new DOMComment('[pmxi_more:' . ($sub_ind - $this->_sibling_limit) . ']')); |
| 1111 | } |
| 1112 | return $el; |
| 1113 | } |
| 1114 | protected function render_xml_element(DOMElement $el, $shorten = false, $path = '/', $ind = 1, $lvl = 0) |
| 1115 | { |
| 1116 | $path .= $el->nodeName; |
| 1117 | if ( ! $el->parentNode instanceof DOMDocument and $ind > 0) { |
| 1118 | $path .= "[$ind]"; |
| 1119 | } |
| 1120 | |
| 1121 | echo '<div class="xml-element lvl-' . $lvl . ' lvl-mod4-' . ($lvl % 4) . '" title="' . $path . '">'; |
| 1122 | if ($el->hasChildNodes()) { |
| 1123 | $is_render_collapsed = $ind > 1; |
| 1124 | if ($el->childNodes->length > 1 or ! $el->childNodes->item(0) instanceof DOMText or strlen(trim($el->childNodes->item(0)->wholeText)) > 40) { |
| 1125 | echo '<div class="xml-expander">' . ($is_render_collapsed ? '+' : '-') . '</div>'; |
| 1126 | } |
| 1127 | echo '<div class="xml-tag opening"><<span class="xml-tag-name">' . $el->nodeName . '</span>'; $this->render_xml_attributes($el, $path . '/'); echo '></div>'; |
| 1128 | if (1 == $el->childNodes->length and $el->childNodes->item(0) instanceof DOMText) { |
| 1129 | $this->render_xml_text(trim($el->childNodes->item(0)->wholeText), $shorten, $is_render_collapsed); |
| 1130 | } else { |
| 1131 | echo '<div class="xml-content' . ($is_render_collapsed ? ' collapsed' : '') . '">'; |
| 1132 | $indexes = array(); |
| 1133 | foreach ($el->childNodes as $child) { |
| 1134 | if ($child instanceof DOMElement) { |
| 1135 | empty($indexes[$child->nodeName]) and $indexes[$child->nodeName] = 0; $indexes[$child->nodeName]++; |
| 1136 | $this->render_xml_element($child, $shorten, $path . '/', $indexes[$child->nodeName], $lvl + 1); |
| 1137 | } elseif ($child instanceof DOMText) { |
| 1138 | $this->render_xml_text(trim($child->wholeText), $shorten); |
| 1139 | } elseif ($child instanceof DOMComment) { |
| 1140 | if (preg_match('%\[pmxi_more:(\d+)\]%', $child->nodeValue, $mtch)) { |
| 1141 | $no = intval($mtch[1]); |
| 1142 | echo '<div class="xml-more">[ ⇓ ' . sprintf(__('<strong>%s</strong> %s more', 'pmxi_plugin'), $no, _n('element', 'elements', $no, 'pmxi_plugin')) . ' ⇓ ]</div>'; |
| 1143 | } |
| 1144 | } |
| 1145 | } |
| 1146 | echo '</div>'; |
| 1147 | } |
| 1148 | echo '<div class="xml-tag closing"></<span class="xml-tag-name">' . $el->nodeName . '</span>></div>'; |
| 1149 | } else { |
| 1150 | echo '<div class="xml-tag opening empty"><<span class="xml-tag-name">' . $el->nodeName . '</span>'; $this->render_xml_attributes($el); echo '/></div>'; |
| 1151 | } |
| 1152 | echo '</div>'; |
| 1153 | } |
| 1154 | protected function render_xml_text($text, $shorten = false, $is_render_collapsed = false) |
| 1155 | { |
| 1156 | if (empty($text)) { |
| 1157 | return; // do not display empty text nodes |
| 1158 | } |
| 1159 | if (preg_match('%\[more:(\d+)\]%', $text, $mtch)) { |
| 1160 | $no = intval($mtch[1]); |
| 1161 | echo '<div class="xml-more">[ ⇓ ' . sprintf(__('<strong>%s</strong> %s more', 'pmxi_plugin'), $no, _n('element', 'elements', $no, 'pmxi_plugin')) . ' ⇓ ]</div>'; |
| 1162 | return; |
| 1163 | } |
| 1164 | $more = ''; |
| 1165 | if ($shorten and preg_match('%^(.*?\s+){20}(?=\S)%', $text, $mtch)) { |
| 1166 | $text = $mtch[0]; |
| 1167 | $more = '<span class="xml-more">[' . __('more', 'pmxi_plugin') . ']</span>'; |
| 1168 | } |
| 1169 | $is_short = strlen($text) <= 40; |
| 1170 | $text = esc_html($text); |
| 1171 | $text = preg_replace('%(?<!\s)\b(?!\s|\W[\w\s])|\w{20}%', '$0​', $text); // put explicit breaks for xml content to wrap |
| 1172 | echo '<div class="xml-content textonly' . ($is_short ? ' short' : '') . ($is_render_collapsed ? ' collapsed' : '') . '">' . $text . $more . '</div>'; |
| 1173 | } |
| 1174 | protected function render_xml_attributes(DOMElement $el, $path = '/') |
| 1175 | { |
| 1176 | foreach ($el->attributes as $attr) { |
| 1177 | echo ' <span class="xml-attr" title="' . $path . '@' . $attr->nodeName . '"><span class="xml-attr-name">' . $attr->nodeName . '</span>=<span class="xml-attr-value">"' . esc_attr($attr->value) . '"</span></span>'; |
| 1178 | } |
| 1179 | } |
| 1180 | |
| 1181 | protected function render_xml_element_table(DOMElement $el, $shorten = false, $path = '/', $ind = 0, $lvl = 0) |
| 1182 | { |
| 1183 | $path .= $el->nodeName; |
| 1184 | if ($ind > 0) { |
| 1185 | $path .= "[$ind]"; |
| 1186 | } |
| 1187 | |
| 1188 | $is_render_collapsed = $ind > 1; |
| 1189 | echo '<tr class="xml-element lvl-' . $lvl . ($is_render_collapsed ? ' collapsed' : '') . '" title="' . $path . '">'; |
| 1190 | echo '<td style="padding-left:' . ($lvl + 1) * 15 . 'px">'; |
| 1191 | $is_inline = true; |
| 1192 | if ( ! (0 == $el->attributes->length and 1 == $el->childNodes->length and $el->childNodes->item(0) instanceof DOMText and strlen($el->childNodes->item(0)->wholeText) <= 40)) { |
| 1193 | $is_inline = false; |
| 1194 | echo '<div class="xml-expander">' . ($is_render_collapsed ? '+' : '-') . '</div>'; |
| 1195 | } |
| 1196 | echo '<div class="xml-tag opening"><span class="xml-tag-name">' . $el->nodeName . '</span></div>'; |
| 1197 | echo '</td>'; |
| 1198 | echo '<td>'; |
| 1199 | $is_inline and $this->render_xml_text_table(trim($el->childNodes->item(0)->wholeText), $shorten, NULL, NULL, $is_inline = true); |
| 1200 | echo '</td>'; |
| 1201 | echo '</tr>'; |
| 1202 | if ( ! $is_inline) { |
| 1203 | echo '<tr class="xml-content' . ($is_render_collapsed ? ' collapsed' : '') . '">'; |
| 1204 | echo '<td colspan="2">'; |
| 1205 | echo '<table>'; |
| 1206 | $this->render_xml_attributes_table($el, $path . '/', $lvl + 1); |
| 1207 | $indexes = array(); |
| 1208 | foreach ($el->childNodes as $child) { |
| 1209 | if ($child instanceof DOMElement) { |
| 1210 | empty($indexes[$child->nodeName]) and $indexes[$child->nodeName] = 1; |
| 1211 | $this->render_xml_element_table($child, $shorten, $path . '/', $indexes[$child->nodeName]++, $lvl + 1); |
| 1212 | } elseif ($child instanceof DOMText) { |
| 1213 | $this->render_xml_text_table(trim($child->wholeText), $shorten, $path . '/', $lvl + 1); |
| 1214 | } |
| 1215 | } |
| 1216 | echo '</table>'; |
| 1217 | echo '</td>'; |
| 1218 | echo '</tr>'; |
| 1219 | } |
| 1220 | } |
| 1221 | protected function render_xml_text_table($text, $shorten = false, $path = '/', $lvl = 0, $is_inline = false) |
| 1222 | { |
| 1223 | if (empty($text)) { |
| 1224 | return; // do not display empty text nodes |
| 1225 | } |
| 1226 | $more = ''; |
| 1227 | if ($shorten and preg_match('%^(.*?\s+){20}(?=\S)%', $text, $mtch)) { |
| 1228 | $text = $mtch[0]; |
| 1229 | $more = '<span class="xml-more">[' . __('more', 'pmxi_plugin') . ']</span>'; |
| 1230 | } |
| 1231 | $is_short = strlen($text) <= 40; |
| 1232 | $text = esc_html($text); |
| 1233 | $text = preg_replace('%(?<!\s)\b(?!\s|\W[\w\s])|\w{20}%', '$0​', $text); // put explicit breaks for xml content to wrap |
| 1234 | if ($is_inline) { |
| 1235 | echo $text . $more; |
| 1236 | } else { |
| 1237 | echo '<tr class="xml-content-tr textonly lvl-' . $lvl . ($is_short ? ' short' : '') . '" title="' . $path . 'text()">'; |
| 1238 | echo '<td style="padding-left:' . ($lvl + 1) * 15 . 'px"><span class="xml-attr-name">text</span></td>'; |
| 1239 | echo '<td>' . $text . $more . '</td>'; |
| 1240 | echo '</tr>'; |
| 1241 | } |
| 1242 | } |
| 1243 | protected function render_xml_attributes_table(DOMElement $el, $path = '/', $lvl = 0) |
| 1244 | { |
| 1245 | foreach ($el->attributes as $attr) { |
| 1246 | echo '<tr class="xml-attr lvl-' . $lvl . '" title="' . $path . '@' . $attr->nodeName . '">'; |
| 1247 | echo '<td style="padding-left:' . ($lvl + 1) * 15 . 'px"><span class="xml-attr-name">@' . $attr->nodeName . '</span></td>'; |
| 1248 | echo '<td><span class="xml-attr-value">' . esc_attr($attr->value) . '</span></td>'; |
| 1249 | echo '</tr>'; |
| 1250 | } |
| 1251 | } |
| 1252 | |
| 1253 | protected function xml_find_repeating(DOMElement $el, $path = '/') |
| 1254 | { |
| 1255 | $path .= $el->nodeName; |
| 1256 | if ( ! $el->parentNode instanceof DOMDocument) { |
| 1257 | $path .= '[1]'; |
| 1258 | } |
| 1259 | $children = array(); |
| 1260 | foreach ($el->childNodes as $child) { |
| 1261 | if ($child instanceof DOMElement) { |
| 1262 | if ( ! empty($children[$child->nodeName])) { |
| 1263 | return $path . '/' . $child->nodeName; |
| 1264 | } else { |
| 1265 | $children[$child->nodeName] = true; |
| 1266 | } |
| 1267 | } |
| 1268 | } |
| 1269 | // reaching this point means we didn't find anything among current element children, so recursively ask children to find something in them |
| 1270 | foreach ($el->childNodes as $child) { |
| 1271 | if ($child instanceof DOMElement) { |
| 1272 | $result = $this->xml_find_repeating($child, $path . '/'); |
| 1273 | if ($result) { |
| 1274 | return $result; |
| 1275 | } |
| 1276 | } |
| 1277 | } |
| 1278 | // reaching this point means we didn't find anything, so return element itself if the function was called for it |
| 1279 | if ('/' . $el->nodeName == $path) { |
| 1280 | return $path; |
| 1281 | } |
| 1282 | |
| 1283 | return NULL; |
| 1284 | } |
| 1285 | /* |
| 1286 | * Convert csv to xml using yahoo API |
| 1287 | */ |
| 1288 | protected function csv_to_xml($csv_url){ |
| 1289 | |
| 1290 | include_once(PMXI_Plugin::ROOT_DIR.'/libraries/XmlImportCsvParse.php'); |
| 1291 | |
| 1292 | $csv = new PMXI_CsvParser($csv_url); |
| 1293 | |
| 1294 | return $csv->toXML(); |
| 1295 | |
| 1296 | } |
| 1297 | /* |
| 1298 | * |
| 1299 | * Detect CSV file |
| 1300 | * |
| 1301 | */ |
| 1302 | protected function detect_csv($type){ |
| 1303 | return in_array($type, $this->csv_mimes); |
| 1304 | } |
| 1305 | } |
| 1306 |