Changeset 2491116
- Timestamp:
- 03/10/2021 12:04:14 AM (5 years ago)
- Location:
- wpsitesynccontent
- Files:
-
- 59 added
- 21 edited
-
tags/1.6.1 (added)
-
tags/1.6.1/.htaccess (added)
-
tags/1.6.1/assets (added)
-
tags/1.6.1/assets/css (added)
-
tags/1.6.1/assets/css/sync-admin.css (added)
-
tags/1.6.1/assets/imgs (added)
-
tags/1.6.1/assets/imgs/ajax-loader.gif (added)
-
tags/1.6.1/assets/imgs/wpsitesync-logo-blue.png (added)
-
tags/1.6.1/assets/imgs/wpsitesync-logo.svg (added)
-
tags/1.6.1/assets/js (added)
-
tags/1.6.1/assets/js/settings.js (added)
-
tags/1.6.1/assets/js/sync.js (added)
-
tags/1.6.1/classes (added)
-
tags/1.6.1/classes/admin.php (added)
-
tags/1.6.1/classes/admindashboard.php (added)
-
tags/1.6.1/classes/ajax.php (added)
-
tags/1.6.1/classes/apicontroller.php (added)
-
tags/1.6.1/classes/apiheaders.php (added)
-
tags/1.6.1/classes/apimodel.php (added)
-
tags/1.6.1/classes/apirequest.php (added)
-
tags/1.6.1/classes/apiresponse.php (added)
-
tags/1.6.1/classes/attachmodel.php (added)
-
tags/1.6.1/classes/auth.php (added)
-
tags/1.6.1/classes/autherror.php (added)
-
tags/1.6.1/classes/debug.php (added)
-
tags/1.6.1/classes/extensionmodel.php (added)
-
tags/1.6.1/classes/extensionsettings.php (added)
-
tags/1.6.1/classes/gutenbergentry.php (added)
-
tags/1.6.1/classes/input.php (added)
-
tags/1.6.1/classes/licensesettings.php (added)
-
tags/1.6.1/classes/licensing.php (added)
-
tags/1.6.1/classes/logmodel.php (added)
-
tags/1.6.1/classes/mediamodel.php (added)
-
tags/1.6.1/classes/model.php (added)
-
tags/1.6.1/classes/options.php (added)
-
tags/1.6.1/classes/postmodel.php (added)
-
tags/1.6.1/classes/serialize.php (added)
-
tags/1.6.1/classes/settings.php (added)
-
tags/1.6.1/classes/shortcodeentry.php (added)
-
tags/1.6.1/classes/shortcodes.php (added)
-
tags/1.6.1/classes/sourcesmodel.php (added)
-
tags/1.6.1/classes/usage.php (added)
-
tags/1.6.1/classes/view.php (added)
-
tags/1.6.1/index.php (added)
-
tags/1.6.1/install (added)
-
tags/1.6.1/install/activate.php (added)
-
tags/1.6.1/install/deactivate.php (added)
-
tags/1.6.1/install/pluginupdater.php (added)
-
tags/1.6.1/languages (added)
-
tags/1.6.1/languages/placeholder.txt (added)
-
tags/1.6.1/readme.txt (added)
-
tags/1.6.1/views (added)
-
tags/1.6.1/views/content_details.php (added)
-
tags/1.6.1/views/syncextensions.php (added)
-
tags/1.6.1/wpsitesynccontent.php (added)
-
trunk/assets/imgs/wpsitesync-logo-cropped-208x286.png (added)
-
trunk/assets/imgs/wpsitesync-logo-cropped.png (added)
-
trunk/assets/js/sync-common.js (added)
-
trunk/assets/js/sync.js (modified) (3 diffs)
-
trunk/classes/admin.php (modified) (6 diffs)
-
trunk/classes/admindashboard.php (modified) (1 diff)
-
trunk/classes/ajax.php (modified) (3 diffs)
-
trunk/classes/apicontroller.php (modified) (23 diffs)
-
trunk/classes/apiheaders.php (modified) (1 diff)
-
trunk/classes/apimodel.php (modified) (3 diffs)
-
trunk/classes/apirequest.php (modified) (73 diffs)
-
trunk/classes/apiresponse.php (modified) (2 diffs)
-
trunk/classes/auth.php (modified) (5 diffs)
-
trunk/classes/autherror.php (modified) (1 diff)
-
trunk/classes/gutenbergentry.php (modified) (1 diff)
-
trunk/classes/input.php (modified) (2 diffs)
-
trunk/classes/licensesettings.php (modified) (1 diff)
-
trunk/classes/model.php (modified) (10 diffs)
-
trunk/classes/options.php (modified) (1 diff)
-
trunk/classes/postmetamodel.php (added)
-
trunk/classes/settings.php (modified) (16 diffs)
-
trunk/classes/shortcodeentry.php (modified) (1 diff)
-
trunk/install/pluginupdater.php (modified) (2 diffs)
-
trunk/readme.txt (modified) (9 diffs)
-
trunk/wpsitesynccontent.php (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
wpsitesynccontent/trunk/assets/js/sync.js
r2247839 r2491116 97 97 WPSiteSyncContent.prototype.is_gutenberg = function() 98 98 { 99 if ('undefined' !== typeof(wp.blocks) && 'undefined' !== typeof(wp.blocks.registerBlockType)) { 99 if ('undefined' !== typeof(wp.blocks) && 'undefined' !== typeof(wp.blocks.registerBlockType) && 100 'undefined' !== typeof(wp.data) && null !== wp.data.select('core/editor')) { 100 101 //console.log('.is_gutenberg() returning true'); 102 101 103 return true; 102 104 } … … 536 538 } 537 539 }, 538 error: function(response ) {540 error: function(response, status) { 539 541 //console.log('push() failure response:'); 540 542 //console.log(response); … … 542 544 if ('undefined' !== typeof(response.error_message)) 543 545 wpsitesynccontent.set_message('<span class="error">' + response.error_message + '</span>', false, true); 544 else 546 else if (response.status >= 500) { 547 wpsitesynccontent.set_message('<span class="error">' + 548 jQuery('#sync-server-err-msg').html().replace('%err%', response.status) + 549 '</span>', false, true); 550 } else 545 551 wpsitesynccontent.set_message('<span class="error">' + jQuery('#sync-runtime-err-msg').html() + '</span>', false, true); 546 552 // jQuery('#sync-content-anim').hide(); -
wpsitesynccontent/trunk/classes/admin.php
r2321211 r2491116 20 20 add_action('admin_enqueue_scripts', array($this, 'admin_enqueue_scripts')); 21 21 add_action('wp_dashboard_setup', array($this, 'dashboard_init'), 50); 22 // TODO: use a 'spectrom_sync_allow_sync', post_id, post_type filter and the post ID to allow add-ons to control whether or not the metabox is displayed for specific posts 22 23 add_action('add_meta_boxes', array($this, 'add_sync_metabox')); 23 24 add_filter('plugin_action_links_wpsitesynccontent/wpsitesynccontent.php', array($this, 'plugin_action_links')); … … 29 30 30 31 // TODO: only init if running settings page 32 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' getting SyncSettings instance action=' . current_action()); 31 33 SyncSettings::get_instance(); 32 34 } … … 83 85 return; 84 86 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' registering "sync"'); 85 wp_register_script('sync', WPSiteSyncContent::get_asset('js/sync.js'), array('jquery'), WPSiteSyncContent::PLUGIN_VERSION, TRUE); 86 wp_register_script('sync-settings', WPSiteSyncContent::get_asset('js/settings.js'), array('jquery'), WPSiteSyncContent::PLUGIN_VERSION, TRUE); 87 wp_register_script('sync', WPSiteSyncContent::get_asset('js/sync.js'), array('jquery'), 88 WPSiteSyncContent::PLUGIN_VERSION, TRUE); 89 wp_register_script('sync-settings', WPSiteSyncContent::get_asset('js/settings.js'), 90 array('jquery'), WPSiteSyncContent::PLUGIN_VERSION, TRUE); 91 wp_register_script('sync-common', WPSiteSyncContent::get_asset('js/sync-common.js'), 92 array('jquery'), WPSiteSyncContent::PLUGIN_VERSION, TRUE); 87 93 88 94 wp_register_style('sync-admin', WPSiteSyncContent::get_asset('css/sync-admin.css'), array(), WPSiteSyncContent::PLUGIN_VERSION, 'all'); … … 104 110 } 105 111 wp_enqueue_script('sync-settings'); 106 107 $option_data = array('site_key' => SyncOptions::get('site_key'));108 wp_localize_script('sync-settings', 'syncdata', $option_data);109 110 112 wp_enqueue_style('sync-admin'); 111 113 } 114 115 $option_data = array('site_key' => SyncOptions::get('site_key')); 116 // allow extensions to modify the data 117 $option_data = apply_filters('spectrom_sync_option_data', $option_data); 118 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' syncdata=' . var_export($option_data, TRUE)); 119 wp_localize_script('sync-common', 'syncdata', $option_data); 120 wp_enqueue_script('sync-common'); 112 121 } 113 122 … … 142 151 __('WPSiteSync logo', 'wpsitesynccontent') . '" title="' . __('WPSiteSync for Content', 'wpsitesynccontent') . '" />™'; 143 152 add_meta_box( 144 'spectrom_sync', // TODO: update name 153 // TODO: update name 154 'spectrom_sync', 145 155 $img, // __('WPSiteSync for Content', 'wpsitesynccontent'), 146 156 array($this, 'render_sync_metabox'), … … 364 374 if ($pull_disabled) 365 375 echo '<div id="sync-pull-msg"><div style="color: #0085ba;">', __('Please activate the Pull extension.', 'wpsitesynccontent'), '</div></div>'; 366 echo '<div id="sync-runtime-err-msg">', __('A PHP runtime error occurred while processing your request. Examine Target log files for more information.', 'wpsitesynccontent'), '</div>'; 376 echo '<div id="sync-runtime-err-msg">', __('A PHP runtime error occurred while processing your request. Examine log files for more information.', 'wpsitesynccontent'), '</div>'; 377 echo '<div id="sync-server-err-msg">', __('An HTTP %err% error occured in sending the request to the Source site.', 'wpsitesynccontent'), '</div>'; 367 378 echo '<div id="sync-error-msg">', __('Error: error encountered during request.', 'wpsitesynccontent'), '</div>'; 368 379 echo '<span id="sync-msg-working">', __('Pushing Content to Target...', 'wpsitesynccontent'), '</span>'; -
wpsitesynccontent/trunk/classes/admindashboard.php
r2158145 r2491116 64 64 $msg = sprintf(__('Thank you for using <a href="%1$s" target="_blank">WPSiteSync for Content</a>. Please consider <a href="%2$s" target="_blank">rating us</a> on <a href="%2$s" target="_blank">WordPress.org</a>!', 'wpsitesynccontent'), 65 65 esc_url('https://wpsitesync.com'), 66 esc_url('https://wordpress.org/support/ view/plugin-reviews/wpsitesynccontent?filter=5#postform')66 esc_url('https://wordpress.org/support/plugin/wpsitesynccontent/reviews?rate=5#new-post') 67 67 ); 68 68 break; -
wpsitesynccontent/trunk/classes/ajax.php
r2321211 r2491116 38 38 $operation = $this->post('operation'); 39 39 $response = new SyncApiResponse(TRUE); 40 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' op=' . var_export($operation, TRUE)); 40 41 41 42 // set headers … … 49 50 $response->send(); 50 51 } 51 $operation = sanitize_key($operation);52 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' op=' . var_export($operation, TRUE)); 52 53 53 54 // perform authentication checking: must be logged in … … 103 104 break; 104 105 default: 106 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' filter "spectrom_sync_ajax_operation" op=' . var_export($operation, TRUE)); 105 107 // allow add-ons a chance to handle their own AJAX request operation types 106 108 if (FALSE === apply_filters('spectrom_sync_ajax_operation', FALSE, $operation, $response)) { -
wpsitesynccontent/trunk/classes/apicontroller.php
r2321211 r2491116 20 20 private $_target_urls = NULL; // list of Target URLs for domain transposition 21 21 private $_action = NULL; // API action being performed 22 p rivate$_parent_action = NULL; // the parent action for the current API call22 public $_parent_action = NULL; // the parent action for the current API call 23 23 private $_sync_model = NULL; // class property for SyncModel- used in push_complete API calls 24 24 … … 101 101 if ($this->_response->has_errors()) { 102 102 $this->_response->send(NULL === $this->args['parent_action']); // calls die() if there is a parent action 103 return; 104 } 105 } 103 return; // for Pull requests we want to return and continue processing 104 } 105 } 106 107 // signal add-ons before processing API request 108 do_action('spectrom_sync_before_api', $this->_action); 106 109 107 110 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' check action: ' . $action); … … 136 139 137 140 default: 141 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' checking for dependent add-ons'); 142 // TODO: move before switch so this is done for all API requests 143 $required = $this->get_header(SyncApiHeaders::HEADER_SYNC_REQUIRED_ADDON); 144 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' header: ' . $required); 145 if (!empty($required)) { 146 // we know there's a dependency. check that it's active 147 $ext = apply_filters('spectrom_sync_active_extensions', array(), FALSE); // get active extensions 148 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' extensions: ' . var_export($ext, TRUE)); 149 $found = FALSE; 150 foreach ($ext as $extension) { 151 if ($required === $extension['name']) { 152 $found = TRUE; 153 break; 154 } 155 } 156 if (!$found) { 157 $this->_response->error_code(SyncApiRequest::ERROR_EXTENSION_MISSING, $required); 158 $this->_response->send(); 159 return; 160 } 161 } 162 138 163 SyncDebug::log(__METHOD__."() sending action '{$this->_action}' to filter 'spectrom_sync_api'"); 139 164 // let add-ons have a chance to process the request … … 166 191 $inst = self::get_instance($args); 167 192 if (NULL === $inst) 168 SyncDebug::log(__MET hOD__.'():' . __LINE__ . ' instance is NULL');193 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' instance is NULL'); 169 194 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' dispatching...'); 170 195 $inst->dispatch(); … … 269 294 $this->_headers[strtolower($key)] = $value; 270 295 } 271 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' read request headers: ' . var_export($this->_headers, TRUE)); 296 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' read request headers: ' . var_export($this->_headers, TRUE)); ## 272 297 } 273 298 … … 318 343 SyncDebug::log(__METHOD__.'():'.__LINE__); 319 344 //SyncDebug::log(' post data: ' . SyncDebug::arr_sanitize($_POST)); 320 //SyncDebug::log(' request data: ' . var_export($_REQUEST, TRUE));345 //SyncDebug::log(' request data: ' . SyncDebug::arr_sanitize($_REQUEST)); 321 346 // TODO: need to assume failure, not success - then set to success when successful 322 347 $response->success(TRUE); … … 420 445 if (0 !== SyncOptions::get_user_id()) 421 446 wp_set_current_user(SyncOptions::get_user_id()); 447 448 // adjust post_time and post_modified based on Time Zone settings for Source and Target #278 449 $source_offset = (float) $this->post_raw('gmt_offset', ''); 450 $target_offset = (float) get_option('gmt_offset', ''); 451 // check for empty, not a 0 hour adjustment #278 452 if (!empty($this->post_raw('gmt_offset')) && $source_offset !== $target_offset) { 453 $post_date_gmt = DateTime::createFromFormat('Y-m-d H:i:s', $post_data['post_date_gmt']); 454 $post_date_gmt->add(new DateInterval('PT' . ($target_offset - $source_offset) . 'H')); 455 $new_post_date = $post_date_gmt->format('Y-m-d H:i:s'); 456 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' post_date_gmt=' . $post_data['post_date_gmt'] . ' new post_date=' . $new_post_date); 457 ## $post_data['post_date'] = $new_post_date; 458 // same process for post_modified 459 $post_modified_gmt = DateTime::createFromFormat('Y-m-d H:i:s', $post_data['post_modified_gmt']); 460 $post_modified_gmt->add(new DateInterval('PT' . ($target_offset - $source_offset) . 'H')); 461 $new_post_modified = $post_modified_gmt->format('Y-m-d H:i:s'); 462 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' post_modified_gmt=' . $post_data['post_modified_gmt'] . ' new post_modified=' . $new_post_modified); 463 ## $post_data['post_modified'] = $new_post_date; 464 } 422 465 423 466 // add/update post … … 451 494 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' content: ' . $post_data['post_content']); 452 495 $target_post_id = wp_insert_post($new_post_data); // ;here; 496 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' created post ID #' . $target_post_id); 453 497 } else { 454 498 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' user does not have permission to update content'); … … 473 517 $model->save_sync_data($save_sync); 474 518 475 // log the Push operation in the ‘spectrom_sync_push’table519 // log the Push operation in the 'spectrom_sync_push' table 476 520 $logger = new SyncLogModel(); 477 521 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' starting logger'); … … 500 544 unstick_post($target_post_id); 501 545 502 // sync metadata 546 // handle metadata 547 $this->_process_postmeta($target_post_id); 548 549 // handle taxonomy information 550 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' handling taxonomies'); 551 $this->_process_taxonomies($target_post_id); 552 553 // check post thumbnail 554 $thumbnail = $this->post('thumbnail', ''); 555 if ('' === $thumbnail) { 556 // remove the thumbnail -- it's no longer attached on the Source 557 delete_post_thumbnail($target_post_id); 558 } 559 560 // this lets add-ons know that the Push operation is complete. Ex: CPT add-on can handle additional taxonomies to update 561 SyncDebug::log(__METHOD__.'():'.__LINE__ . " calling action 'spectrom_sync_push_content'"); 562 do_action('spectrom_sync_push_content', $target_post_id, $post_data, $response); 563 564 //$temp_post = get_post($target_post_id); 565 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' push complete, content=' . $temp_post->post_content); 566 } 567 568 /** 569 * Handles processing of postmeta data 570 * @param int $post_id The Target's post ID where postmeta data will be written 571 */ 572 private function _process_postmeta($post_id) 573 { 503 574 // TODO: note, this is in $_POST['post_data']['post_meta'] 504 575 $post_meta = $this->post_raw('post_meta', array()); 576 $model = new SyncModel(); 577 505 578 // TODO: need to handle deletes - postmeta that doesn't exist in Source any more but does on Target 506 579 // TOOD: probably better to remove all postmeta, then add_post_meta() for each item found 507 580 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' handling meta data'); 581 582 // initialize this. if needed and NULL, a new SyncSerialize() instance will be instantiated 508 583 $ser = NULL; 509 // TODO: move postmeta processing into _process_postmeta() method 584 585 $pmmodel = new SyncPostMetaModel(); 586 510 587 foreach ($post_meta as $meta_key => $meta_value) { 511 foreach ($meta_value as $value) // loop through meta_value array 588 // handle multiple postmeta entries #287 589 $current_entries = $pmmodel->get_post_meta($post_id, $meta_key); 590 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' current postmeta=' . var_export($current_entries, TRUE)); 591 592 // find the lower of Source vs. Target postmeta entries for this meta_key 593 $entries = min(count($meta_value), count($current_entries)); 594 // loop through all the entries. adds and deletes are handled after this 595 for ($idx = 0; $idx < $entries; ++$idx) { // loop through meta_value array #286 596 // foreach ($meta_value as $value) { // loop through meta_value array #286 597 $value = $meta_value[$idx]; 512 598 //$_v = $value; 513 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' key=' . $meta_key . ' (' . gettype($value) . ') value=' . var_export($_v, TRUE));599 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' key=' . $meta_key . ' (' . gettype($value) . ') value=' . SyncDebug::arr_sanitize($_v)); 514 600 //$_v = stripslashes($_v); 515 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' key=' . $meta_key . ' (' . gettype($value) . ') value=' . var_export($_v, TRUE));601 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' key=' . $meta_key . ' (' . gettype($value) . ') value=' . SyncDebug::arr_sanitize($_v)); 516 602 //$_v = maybe_unserialize($_v); 517 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' new value=' . var_export($_v, TRUE));603 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' new value=' . SyncDebug::arr_sanitize($_v)); 518 604 // change Source URL references to Target URL references in meta data 519 605 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' meta key: "' . $meta_key . '" meta data: ' . $value); 520 606 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' key start=' . $meta_key . ' value=' . $value); 521 607 $value = stripslashes($value); // removes slashes added via HTTP POST operation 608 609 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' stripped slashes=' . $value); 610 $is_json = $model->is_json($value); 611 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' is_json=' . ($is_json ? 'TRUE' : 'FALSE')); 522 612 // replace token with *double* escaped quote because update_post_meta() calls wp_unslash() #257 523 $value = str_replace('~syncescquote~', '\\\"', $value);613 $value = str_replace('~syncescquote~', $is_json ? '\"' : '\\\"', $value); 524 614 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' key before=' . $meta_key . ' value=' . $value); 525 615 if (FALSE !== strpos($value, '~syncuni~')) { 526 $value = str_replace('~syncuni~', '\\\\u', $value); // need to double escape unicode for update_post_meta()527 } 528 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' key=' . $meta_key . ' value=' . $value);616 $value = str_replace('~syncuni~', $is_json ? '\\u' : '\\\\u', $value); // need to double escape unicode for update_post_meta() 617 } 618 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' key=' . $meta_key . ' value=' . $value); 529 619 if (is_serialized($value)) { 530 620 if (NULL === $ser) … … 536 626 $value = $ser->parse_data($value, array($this, 'fixup_url_references')); 537 627 } else { 538 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' not fixing serialized data');628 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' not fixing non-serialized data'); 539 629 $value = str_replace($this->source, site_url(), $value); 540 630 } 631 632 // adjust serialized JSON data containing escaped content #275 633 if ($is_json) { 634 // if it's JSON data, need to add escaping 635 // this preserves the escaped data because update_post_meta() calls wp_unslash() 636 $value = wp_slash($value); 637 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' add wp_slash() to JSON data ' . var_export($value, TRUE)); 638 } 639 541 640 # if ('_wp_page_template' === $meta_key && class_exists('Elementor\Plugin', FALSE)) { 542 641 # // #184: bug in Elementor- modules/page-templates/module.php:345 $common is not initialized … … 547 646 # $elementor->init_common(); 548 647 # } 549 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' writing key=' . $meta_key . ' value=' . $value); 550 update_post_meta($target_post_id, $meta_key, maybe_unserialize($value)); 551 } 552 553 // handle taxonomy information 554 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' handling taxonomies'); 555 $this->_process_taxonomies($target_post_id); 556 557 // check post thumbnail 558 $thumbnail = $this->post('thumbnail', ''); 559 if ('' === $thumbnail) { 560 // remove the thumbnail -- it's no longer attached on the Source 561 delete_post_thumbnail($target_post_id); 562 } 563 564 // this lets add-ons know that the Push operation is complete. Ex: CPT add-on can handle additional taxonomies to update 565 SyncDebug::log(__METHOD__.'():'.__LINE__ . " calling action 'spectrom_sync_push_content'"); 566 do_action('spectrom_sync_push_content', $target_post_id, $post_data, $response); 567 568 $temp_post = get_post($target_post_id); 569 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' push complete, content=' . $temp_post->post_content); 570 } 648 649 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' writing key=' . $meta_key . ' value=' . var_export($value, TRUE)); 650 // update_post_meta($post_id, $meta_key, maybe_unserialize($value)); 651 $pmmodel->update_post_meta($current_entries[$idx], $value); // #287 652 } // for $idx < $entries 653 654 if (count($current_entries) > count($meta_value)) { 655 // more entries on Target than on Source. Remove extra entries on Target #287 656 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' removing extra entries'); 657 for (; $idx < count($current_entries); ++$idx) 658 $pmmodel->remove_post_meta($current_entries[$idx]); 659 } else if (count($current_entries) < count($meta_value)) { 660 // more entries on Source than on Target. Add extra entries on Target #287 661 for (; $idx < count($meta_value); ++$idx) { 662 // TODO: move postmeta data fixup into a helper method 663 $value = $meta_value[$idx]; 664 $value = stripslashes($value); 665 $is_json = $model->is_json($value); 666 667 $value = str_replace('~syncescquote~', $is_json ? '\"' : '\\\"', $value); 668 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' key before=' . $meta_key . ' value=' . $value); 669 if (FALSE !== strpos($value, '~syncuni~')) { 670 $value = str_replace('~syncuni~', $is_json ? '\\u' : '\\\\u', $value); // need to double escape unicode for update_post_meta() 671 } 672 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' key=' . $meta_key . ' value=' . $value); 673 if (is_serialized($value)) { 674 if (NULL === $ser) 675 $ser = new SyncSerialize(); 676 /// $fix_data = str_replace($this->source, site_url(), $value); 677 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' fix data: ' . $fix_data); 678 /// $fix_data = $ser->fix_serialized_data($fix_data); 679 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' fixing serialized data: ' . $fix_data); 680 $value = $ser->parse_data($value, array($this, 'fixup_url_references')); 681 } else { 682 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' not fixing non-serialized data'); 683 $value = str_replace($this->source, site_url(), $value); 684 } 685 686 // adjust serialized JSON data containing escaped content #275 687 if ($is_json) { 688 // if it's JSON data, need to add escaping 689 // this preserves the escaped data because update_post_meta() calls wp_unslash() to remove it 690 $value = wp_slash($value); 691 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' add wp_slash() to JSON data ' . var_export($value, TRUE)); 692 } 693 694 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' writing key=' . $meta_key . ' value=' . var_export($value, TRUE)); 695 add_post_meta($post_id, $meta_key, maybe_unserialize($value)); 696 } 697 } 698 } // foreach $post_meta 699 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' done processing postmeta'); 700 } 701 571 702 572 703 /** … … 576 707 private function _process_gutenberg($response) 577 708 { 578 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' post=' . var_export($_POST, TRUE));709 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' post=' . SyncDebug::arr_sanitize($_POST)); 579 710 // https://premium.wpmudev.org/blog/a-tour-of-the-gutenberg-editor-for-wordpress/ 580 711 … … 774 905 if (NULL === $sync_data) { 775 906 // no id found 776 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' cannot find Target ID for Source Content ID ' . $source_ref_id); 907 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' cannot find Target ID for Source Content id ' . $source_ref_id); 908 $response->error_code(SyncApiRequest::ERROR_GB_ATTACHMENT_ID, $block_name); 777 909 } else { 778 910 $target_ref_id = abs($sync_data->target_content_id); … … 795 927 case 'wp:video': // Video Block- resource reference 796 928 case 'wp:image': // Image Block- resource reference 797 $source_ref_id = abs($obj->id);929 $source_ref_id = isset($obj->id) ? abs($obj->id) : 0; 798 930 if (0 !== $source_ref_id) { 799 931 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' found reference to post id ' . $source_ref_id); … … 802 934 if (NULL === $sync_data) { 803 935 // no id found 804 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' ERROR: cannot find Target ID for Source Content ID' . $source_ref_id);805 // TODO: error recovery936 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' cannot find Target ID for Source Content id ' . $source_ref_id); 937 $response->error_code(SyncApiRequest::ERROR_GB_ATTACHMENT_ID, $block_name); 806 938 } else { 807 939 $target_ref_id = abs($sync_data->target_content_id); … … 875 1007 case 'wp:gallery': // Gallery Block- multiple image references 876 1008 $source_ids = $obj->ids; // array of Gellery Image IDs 877 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' source ids=' . implode(',', $source_ids) );1009 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' source ids=' . implode(',', $source_ids) . ' = ' . var_export($source_ids, TRUE)); 878 1010 $new_ids = array(); // initialize array of new ids 879 1011 foreach ($source_ids as $source_ref_id) { 880 $source_ref_id = abs($source_ref_id); 881 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' source ref id=' . $source_ref_id); 882 if (0 !== $source_ref_id) { 883 // look up source post ID to see what the Target ID is 884 $sync_data = $sync_model->get_sync_data($source_ref_id, $this->source_site_key); 885 if (NULL === $sync_data) { 886 // no id found 1012 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' source ref id=' . var_export($source_ref_id, TRUE)); 1013 // check for NULLs and maintain array size, just skip attachment ID update process #280 1014 if (NULL === $source_ref_id) { 1015 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' skipping NULL ref id'); 1016 $new_ids[] = NULL; 1017 } else { 1018 $source_ref_id = abs($source_ref_id); 1019 1020 if (0 !== $source_ref_id) { 1021 // look up source post ID to see what the Target ID is 1022 $sync_data = $sync_model->get_sync_data($source_ref_id, $this->source_site_key); 1023 if (NULL === $sync_data) { 1024 // no id found 887 1025 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' ERROR: cannot find Target ID for Source Content ID ' . $source_ref_id); 888 } else {889 $target_ref_id = abs($sync_data->target_content_id);1026 } else { 1027 $target_ref_id = abs($sync_data->target_content_id); 890 1028 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' target ref id: ' . $target_ref_id); 891 $att_post = get_post($target_ref_id); 892 // $obj->url = $att_post->guid; // no need to update url, that was fixed in 'push' operation 893 $new_ids[] = $target_ref_id; // add to list of new ids 894 } 895 } 1029 $att_post = get_post($target_ref_id); 1030 // $obj->url = $att_post->guid; // no need to update url, that was fixed in 'push' operation 1031 $new_ids[] = $target_ref_id; // add to list of new ids 1032 } 1033 } // 0 !== $source_ref_id 1034 } // NULL check 896 1035 // TODO: error recovery 897 1036 } 1037 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' updated gallery ids: ' . var_export($new_ids, TRUE)); 898 1038 $from = array(); 899 1039 $to = array(); … … 1083 1223 private function _process_shortcodes(SyncApiResponse $apiresponse) 1084 1224 { 1085 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' POST=' . var_export($_POST, TRUE));1225 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' POST=' . SyncDebug::arr_sanitize($_POST)); 1086 1226 /* 1087 1227 [caption id="attachment_1995" align="aligncenter" width="808"] … … 1276 1416 public function get_info(SyncApiResponse $response) 1277 1417 { 1278 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' - post=' . var_export($_POST, TRUE));1418 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' - post=' . SyncDebug::arr_sanitize($_POST)); 1279 1419 $input = new SyncInput(); 1280 1420 $target_post_id = $input->post_int('post_id', 0); … … 1514 1654 } 1515 1655 // check to see if $post_term was included in $taxonomies data provided via the API call 1516 if ($found) { 1517 //SyncDebug::log(__METHOD__.'() post term found in taxonomies list- not removing it'); 1518 } else { 1656 if (!$found) { 1519 1657 // if the $post_term assigned to the post is NOT in the $taxonomies list, it needs to be removed 1520 1658 //SyncDebug::log(__METHOD__.'() ** removing term #' . $post_term->term_id . ' ' . $post_term->slug . ' [' . $post_term->taxonomy . ']'); 1521 1659 wp_remove_object_terms($post_id, abs($post_term->term_id), $post_term->taxonomy); 1660 } else { 1661 // if the $post_term is present we don't need to do anything 1662 $found = TRUE; 1663 //SyncDebug::log(__METHOD__.'() post term found in taxonomies list- not removing it'); 1522 1664 } 1523 1665 1524 1666 // Note: no need to remove term info saved via SyncModel- term IDs don't change, only the post IDs referring to them 1525 } 1667 } // foreach ($assigned_terms) 1526 1668 } 1527 1669 … … 1543 1685 require_once ABSPATH . 'wp-admin/includes/file.php'; 1544 1686 require_once ABSPATH . 'wp-admin/includes/media.php'; 1545 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' FILES=' . var_export($_FILES, TRUE));1546 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' POST=' . var_export($_POST, TRUE));1687 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' FILES=' . SyncDebug::arr_sanitize($_FILES)); 1688 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' POST=' . SyncDebug::arr_sanitize($_POST)); 1547 1689 1548 1690 if (!isset($_FILES['sync_file_upload'])) { … … 1760 1902 $this->_process_shortcodes($response); #102 1761 1903 1904 $target_post_id = 0; 1762 1905 $source_post_id = $this->post_int('post_id'); 1763 1906 $sync_data = $this->_sync_model->get_sync_data($source_post_id, $this->source_site_key); … … 1812 1955 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' cannot find Target post from Source ID #' . $source_post_id); 1813 1956 } 1957 1958 // signal add-ons that Push is complete and all Source IDs have been updated to Target IDs 1959 do_action('spectrom_sync_push_complete', $source_post_id, $target_post_id, $response); 1814 1960 } 1815 1961 -
wpsitesynccontent/trunk/classes/apiheaders.php
r2321211 r2491116 7 7 { 8 8 // TODO: x- headers are deprecated (rfc6648). change to spectrom-sync- headers 9 const HEADER_SYNC_VERSION = 'x-sync-version'; // SYNC version number; used in requests and responses 10 const HEADER_WP_VERSION = 'x-wp-version'; // WP version number; used in requests and responses 11 const HEADER_SOURCE = 'x-sync-source'; // Source site's URL; used in requests 12 const HEADER_SITE_KEY = 'x-sync-site-key'; // Source site's site_key; used in requests 13 const HEADER_MATCH_MODE = 'x-sync-match-mode'; // How to match Content on Target 14 const HEADER_SYNC_REQUEST_ID = 'spectrom-sync-api-request'; // unique API request identifier 9 const HEADER_SYNC_VERSION = 'x-sync-version'; // SYNC version number; used in requests and responses 10 const HEADER_WP_VERSION = 'x-wp-version'; // WP version number; used in requests and responses 11 const HEADER_SOURCE = 'x-sync-source'; // Source site's URL; used in requests 12 const HEADER_SITE_KEY = 'x-sync-site-key'; // Source site's site_key; used in requests 13 const HEADER_MATCH_MODE = 'x-sync-match-mode'; // How to match Content on Target 14 const HEADER_SYNC_REQUEST_ID = 'spectrom-sync-api-request'; // unique API request identifier 15 const HEADER_SYNC_REQUIRED_ADDON = 'spectrom-sync-required-addon'; // API request requires the specified add-on 15 16 } 16 17 -
wpsitesynccontent/trunk/classes/apimodel.php
r2321211 r2491116 212 212 return; 213 213 } 214 // look for 'Comming Soon' v6+ plugin 215 if (defined('SEEDPROD_VERSION')) { 216 add_filter('option_seedprod_settings', array($this, 'filter_coming_soon_6_setting'), 10, 1); 217 return; 218 } 219 // disable the Membership plugin's redirect to login for "Private Sites" #281 220 add_filter('members_is_private_blog', '__return_false', 100); 214 221 // look for iThemes Security 215 222 /* if (class_exists('ITSEC_Core', FALSE)) { … … 258 265 259 266 /** 260 * Filter for the Coming Soon plugin's configuration settings. Used to turn off it's features 267 * Filter for the Coming Soon plugin's configuration settings. Used to turn off it's features during API requests. 261 268 * @param array $settings The Coming Soon plugins settings 262 269 * @return array The modified settings, with the 'status' value set to 0 to disable it … … 266 273 $settings['status'] = '0'; 267 274 return $settings; 275 } 276 /** 277 * Filter for Comming Soon v6+ plugin's configuration settings. Used to turn off it's features during API requests. 278 * @param string $option The Coming Soon plugin's settings 279 * @return string The modified settings, with the 'enable_coming_soon_mode' turned off. 280 */ 281 public function filter_coming_soon_6_setting($option) 282 { 283 return '{"enable_maintenance_mode":false,"enable_coming_soon_mode":false}'; 268 284 } 269 285 -
wpsitesynccontent/trunk/classes/apirequest.php
r2325466 r2491116 11 11 const ERROR_BAD_CREDENTIALS = 4; 12 12 const ERROR_SESSION_EXPIRED = 5; 13 const ERROR_CONTENT_EDITING = 6; // deprecated13 const ERROR_CONTENT_EDITING = 6; // deprecated 14 14 const ERROR_CONTENT_LOCKED = 7; 15 15 const ERROR_POST_DATA_INCOMPLETE = 8; … … 42 42 const ERROR_CLOUDFLARE_BLOCKED = 35; 43 43 const ERROR_CLOUDFRONT_BLOCKED = 36; 44 const ERROR_MISSING_TOKEN = 37; 45 const ERROR_MISSING_USER = 38; 46 const ERROR_GB_ATTACHMENT_ID = 39; 47 const ERROR_GB_TAX_ID = 40; 44 48 45 49 const NOTICE_FILE_EXISTS = 1; … … 48 52 49 53 // TODO: rename to $target 50 public $host = NULL; // URL of the host site we're pushing to 51 public $id_refs = array(); // list if image/content references that need to be adjusted 52 public $post_children = array(); // list of post children to be adjusted during push_complete API handling 53 public $gutenberg_queue = array(); // list of Gutenberg post IDs that need to be parsed for references 54 public $gutenberg_processed = array(); // list of Gutenberg post IDs that have been processed (used to skip duplicate references) 55 public $post_id = 0; // post ID being processed 56 public $thumbnail_id = 0; // the post's thumbnail ID 57 private $_post_data = NULL; // reference to the $post_data array being constructed 58 private $_source_domain = NULL; // domain sending the post information 59 private $_response = NULL; // the SyncApiResponse instance for the current request 54 public $host = NULL; // URL of the host site we're pushing to 55 public $id_refs = array(); // list if image/content references that need to be adjusted 56 public $post_children = array(); // list of post children to be adjusted during push_complete API handling 57 public $gutenberg_queue = array(); // list of Gutenberg post IDs that need to be parsed for references 58 public $gutenberg_processed = array(); // list of Gutenberg post IDs that have been processed (used to skip duplicate references) 59 public $gutenberg_obj = NULL; // the current Gutenberg block data as an object 60 public $post_id = 0; // post ID being processed 61 public $thumbnail_id = 0; // the post's thumbnail ID 62 private $_post_data = NULL; // reference to the $post_data array being constructed 63 private $_source_domain = NULL; // domain sending the post information 64 private $_response = NULL; // the SyncApiResponse instance for the current request 60 65 private $_user_id = 0; 61 66 private $_target_data = array(); 62 67 private $_auth_cookie = ''; 63 68 private $_queue = array(); 64 private $_processing = FALSE; // set to TRUE when processing the $_queue65 private $_sent_images = array(); // list of image attachments/references within post66 private $_trigger_push_complete = FALSE; // set to TRUE when 'spectrom_sync_push_queue_complete' needs to be triggered67 private $_triggered_push_complete = FALSE; // set to TRUE when 'spectrom_sync_push_queue_complete' action has been triggered69 private $_processing = FALSE; // set to TRUE when processing the $_queue 70 private $_sent_images = array(); // list of image attachments/references within post 71 private $_trigger_push_complete = FALSE; // set to TRUE when 'spectrom_sync_push_queue_complete' needs to be triggered 72 private $_triggered_push_complete = FALSE; // set to TRUE when 'spectrom_sync_push_queue_complete' action has been triggered 68 73 69 74 /** … … 104 109 if (is_array($data)) { 105 110 $ret = $this->_auth($data); 106 // check $re sfor WP_Error111 // check $ret for WP_Error 107 112 if (is_wp_error($ret)) { 108 113 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' error authenticating: ' . var_export($ret, TRUE)); … … 110 115 return $response; 111 116 } 112 SyncDebug::log(__METHOD__ .'():' . __LINE__ . ' current auth data: ' . SyncDebug::arr_sanitize($data));117 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' current auth data: ' . SyncDebug::arr_sanitize($data)); 113 118 } 114 119 115 120 // TODO: do some sanity checking on $data contents 116 SyncDebug::log(__METHOD__ .'():' . __LINE__ . ' checking action="' . $action . '"');121 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' checking action="' . $action . '"'); 117 122 switch ($action) { 118 123 case 'auth': … … 128 133 case 'upload_media': 129 134 $data = apply_filters('spectrom_sync_api_request_media', $data, $action, $remote_args); 130 $data = $this->_media($data, $remote_args); // converts $data to a string135 $data = $this->_media($data, $remote_args); // converts $data to a string 131 136 break; 132 137 case 'getinfo': … … 141 146 } 142 147 // reduced logging #!# 143 if (is_string($data)) #!# 144 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' action="' . $action . '" data=' . SyncDebug::post_sanitize($data)); #!# 145 else #!# 146 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' action="' . $action . '" data=' . SyncDebug::arr_sanitize($data)); #!# 147 148 if (is_string($data)) #!# 149 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' action="' . $action . '" data=' . SyncDebug::post_sanitize($data));#!# 150 else #!# 151 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' action="' . $action . '" data=' . SyncDebug::arr_sanitize($data));#!# 148 152 // check value returned from API call 149 153 // check for filter returning a WP_Error instance … … 164 168 // if (NULL !== ($api_controller = SyncApiController::get_instance())) { 165 169 // if ('pull' === $api_controller->get_parent_action()) { 166 SyncDebug::log(__METHOD__ .'():' . __LINE__ . ' adding id ref data to post data: ' . var_export($this->id_refs, TRUE));167 $data['id_refs'] = $this->id_refs;170 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' adding id ref data to post data: ' . var_export($this->id_refs, TRUE)); 171 $data['id_refs'] = $this->id_refs; 168 172 // } 169 173 } … … 183 187 184 188 if (!isset($remote_args['timeout'])) 185 $remote_args['timeout'] = 30; // default to a 30 second timeout if not already specified 186 187 // send data where it's going 189 $remote_args['timeout'] = 30; // default to a 30 second timeout if not already specified 190 191 192 // send data where it's going 188 193 $url = $this->host . '?pagename=' . WPSiteSyncContent::API_ENDPOINT . '&action=' . $action; 189 SyncDebug::log(__METHOD__ .'():' . __LINE__ . ' sending API request to ' . $url, TRUE);194 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' sending API request to ' . $url, TRUE); 190 195 191 196 $remote_args = apply_filters('spectrom_sync_api_arguments', $remote_args, $action); 192 197 $remote_args['headers'][self::HEADER_SYNC_REQUEST_ID] = substr(SyncOptions::get('site_key'), -16) . '-' . date('z.His') . '-' . rand(100, 999); 193 SyncDebug::log(__METHOD__ .'():' . __LINE__ . ' req id=' . $remote_args['headers'][self::HEADER_SYNC_REQUEST_ID]);198 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' req id=' . $remote_args['headers'][self::HEADER_SYNC_REQUEST_ID]); 194 199 195 200 $request = wp_remote_post($url, $remote_args); … … 208 213 209 214 // validate the host and credentials 210 if (abs($request['response']['code']) >= 400 && 211 (FALSE !== stripos($request['body'], 'Wordfence') && FALSE !== stripos($request['body'], 'A potentially unsafe operation has been detected'))) { 215 $http_code = abs($request['response']['code']); 216 $http_server = isset($request['headers']->data) && isset($request['headers']->data['server']) ? $request['headers']->data['server'] : ''; 217 $http_body = str_replace('\\', '', $request['body']); 218 if ($http_code >= 400 && 219 (FALSE !== stripos($http_body, 'Wordfence') && FALSE !== stripos($http_body, 'A potentially unsafe operation has been detected'))) { 212 220 $response->error_code(self::ERROR_WORDFENCE_BLOCKED); 213 } else if (abs($request['response']['code']) >= 400 && 214 (FALSE !== stripos($request['body'], 'Attention Required! | Cloudflare') && FALSE !== stripos($request['body'], 'Please complete the security check to access'))) { 215 } else if (abs($request['response']['code']) >= 400 && 216 (isset($request['headers']->data) && 217 (isset($request['headers']->data['server']) && 'cloudflare' === $request['headers']->data['server']) && 218 ($request['headers']->data['cf-request-id']))) { 219 $response->error_code(self::ERROR_CLOUDFLARE_BLOCKED); 220 } else if (abs($request['response']['code']) > 200 && 221 (FALSE !== stripos($request['body'], 'CloudFront attempted to establish a connection') && FALSE !== stripos($request['body'], 'Generated by cloudfront'))) { 221 } else if ($http_code >= 400 && 222 (FALSE !== stripos($http_body, 'Attention Required! | Cloudflare') && FALSE !== stripos($http_body, 'Please complete the security check to access'))) { 223 $response->error_code(self::ERROR_CLOUDFLARE_BLOCKED, __('Complete security check.', 'wpsitesynccontent')); 224 } else if ($http_code >= 400 && 'cloudflare' === $http_server && 225 FALSE !== stripos($http_body, 'You don\'t have permission to access')) { 226 $response->error_code(self::ERROR_CLOUDFLARE_BLOCKED, __('No permission', 'wpsitesynccontent')); 227 } else if ($http_code >= 400 && 'cloudflare' === $http_server) { 228 $response->error_code(self::ERROR_CLOUDFLARE_BLOCKED, sprintf(__('HTTP Response: %1$d.', 'wpsitesync'), $http_code)); 229 } else if ($http_code > 200 && 230 (FALSE !== stripos($http_body, 'CloudFront attempted to establish a connection') && 231 FALSE !== stripos($http_body, 'Generated by cloudfront'))) { 222 232 $response->error_code(self::ERROR_CLOUDFRONT_BLOCKED); 223 } else if (406 === abs($request['response']['code']) && FALSE !== stripos($request['body'], 'This error was generated by Mod_Security.')) {233 } else if (406 === $http_code && FALSE !== stripos($http_body, 'This error was generated by Mod_Security.')) { 224 234 $response->error_code(self::ERROR_MODSECURITY_BLOCKED); 225 } else if (!($ request['response']['code'] >= 200 && $request['response']['code']< 300)) {226 $response->error_code(self::ERROR_BAD_POST_RESPONSE, abs($request['response']['code']));235 } else if (!($http_code >= 200 && $http_code < 300)) { 236 $response->error_code(self::ERROR_BAD_POST_RESPONSE, $http_code); 227 237 } else if (!isset($request['headers'][self::HEADER_SYNC_VERSION])) { 228 238 $response->error_code(self::ERROR_NOT_INSTALLED); … … 252 262 // examine the Target response's error codes and assign them to the local system's response object 253 263 // TODO: Use SyncResponse::copy() method 254 if (isset($response->response->error_code) ) {264 if (isset($response->response->error_code) && 0 !== abs($response->response->error_code)) { 255 265 $msg = NULL; 256 266 if (!empty($response->response->error_data)) 257 267 $msg = $response->response->error_data; 258 SyncDebug::log(__METHOD__ .'():' . __LINE__ . ' error code=' . $response->response->error_code . ' msg=' . var_export($msg, TRUE));268 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' error code=' . $response->response->error_code . ' msg=' . var_export($msg, TRUE)); 259 269 $response->error_code($response->response->error_code, $msg); 260 } else if (isset($response->response->has_errors) && $response->response->has_errors) {261 SyncDebug::log(__METHOD__ .'():' . __LINE__ . ' error code=' . $response->response->error_code);270 } else if (isset($response->response->has_errors) && 0 !== abs($response->response->has_errors)) { 271 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' error code=' . $response->response->error_code); 262 272 $response->error_code($response->response->error_code); 263 273 } … … 292 302 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' perform after action logging action="' . $action . '"'); 293 303 switch ($action) { 294 case 'auth': // no logging, but add to source table and set target site_key304 case 'auth': // no logging, but add to source table and set target site_key 295 305 if (isset($response->response->data)) { 296 306 // TODO: deprecated … … 341 351 'target_site_key' => SyncOptions::get('target_site_key'), 342 352 ); 343 // TODO: should be 'post' since media are stored in posts table 344 // if ('upload_media' === $action) 345 // $sync_data['content_type'] = 'media'; 353 //SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' logging data ' . var_export($sync_data, TRUE)); 346 354 347 355 $model = new SyncModel(); … … 356 364 357 365 default: 358 SyncDebug::log(__METHOD__ .'():' . __LINE__ . ' - triggering "spectrom_sync_action_success" on action="' . $action . '" data=' . SyncDebug::arr_sanitize($data));366 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' - triggering "spectrom_sync_action_success" on action="' . $action . '" data=' . SyncDebug::arr_sanitize($data)); 359 367 if (isset($data['post_id'])) 360 368 do_action('spectrom_sync_action_success', $action, abs($data['post_id']), $data, $response); 361 369 } 362 370 } 363 else SyncDebug::log(__METHOD__ .'():' . __LINE__ . ' error code=' . $response->get_error_code());#!#371 else SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' error code=' . $response->get_error_code());#!# 364 372 } 365 373 … … 449 457 $this->_queue[] = array('action' => $action, 'data' => $data); 450 458 } 459 451 460 public function add_queue($action, $data) 452 461 { … … 540 549 // serialize the data into a JSON string. 541 550 // $push_data = json_encode($push_data); 542 // use the wp_remote_post() API to perform a connection/authenticate operation on the Target site using the Target site ’s configured credentials and send the JSON data.543 $target = new SyncApiRequest();551 // use the wp_remote_post() API to perform a connection/authenticate operation on the Target site using the Target site's configured credentials and send the JSON data. 552 // $target = new SyncApiRequest(); 544 553 545 554 $result = $this->api('push', $push_data); // $target->api('push', $push_data); // send data … … 552 561 if (!is_wp_error($result)) { 553 562 // PARSE IMAGES FROM SOURCE ONLY 554 $this->_parse_media($result->data->post_id, $push_data['post_data']['post_content'] , $target, $response);563 $this->_parse_media($result->data->post_id, $push_data['post_data']['post_content']); //, $target, $response); 555 564 $response->success(TRUE); 556 565 $response->notice_code(SyncApiRequest::NOTICE_CONTENT_SYNCD); … … 586 595 private function _auth(&$data) 587 596 { 588 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' data: ' . var_export($data, TRUE));597 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' data: ' . SyncDebug::arr_sanitize($data)); 589 598 // TODO: indicate error if target system not set up 590 599 // if no Target credentials provided, get them from the config … … 592 601 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' using credentials from config'); 593 602 $source_model = new SyncSourcesModel(); 594 $opts = new SyncOptions(); 595 $row = $source_model->find_target($opts->get('host')); 603 $row = $source_model->find_target(SyncOptions::get('host')); 596 604 if (NULL === $row) { 597 605 $this->_response->error_code(self::ERROR_NO_AUTH_TOKEN); 606 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' target not found'); 598 607 return new WP_Error($this->error_code_to_string(self::ERROR_NO_AUTH_TOKEN)); 599 608 } … … 602 611 603 612 if (!isset($data['username'])) 604 $data['username'] = $opts->get('username');613 $data['username'] = SyncOptions::get('username'); 605 614 if (!isset($data['token'])) 606 615 $data['token'] = $row->token; 607 616 // TODO: change name to ['target'] to be more consistent 608 617 if (!isset($data['host'])) 609 $data['host'] = $opts->get('host');618 $data['host'] = SyncOptions::get('host'); 610 619 } else if (!empty($data['username']) && !empty($data['password']) && !empty($data['host'])) { 611 620 // using passed-in credentials. add $data[] values into $this->_target_data 621 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' using credentials provided'); 612 622 $this->_target_data['host'] = $data['host']; 613 623 $this->_target_data['username'] = $data['username']; … … 641 651 } 642 652 643 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' -adding authentication data to array');653 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' adding authentication data to array'); 644 654 // add authentication to the data array 645 655 $data['auth'] = array( … … 660 670 } 661 671 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' data: ' . SyncDebug::arr_sanitize($data)); 662 663 672 return NULL; 664 673 } … … 668 677 $this->_post_data = array(); 669 678 } 679 670 680 public function set_post_data($data) 671 681 { … … 685 695 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' post id=' . $post_id); 686 696 $post_data = $model->build_sync_data($post_id); 687 $this->_post_data = &$post_data; // save a copy for the gutenberg_taxonomy_block() method697 $this->_post_data = &$post_data; // save a copy for the gutenberg_taxonomy_block() method 688 698 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' post data: ' . var_export($post_data, TRUE)); 689 699 if (0 === count($post_data)) … … 711 721 if (isset($post_data['thumbnail'])) 712 722 $data['thumbnail'] = $post_data['thumbnail']; 713 $this->_post_data = &$data; // reset reference to new data 723 $data['gmt_offset'] = get_option('gmt_offset', ''); // include GMT offset in API data #278 724 $this->_post_data = &$data; // reset reference to new data 714 725 $this->post_id = $post_id; 715 $this->id_refs = array(); // init list of reference IDs 716 726 $this->id_refs = array(); // init list of reference IDs 717 727 // parse images from source only 718 728 $res = $this->_parse_media($post_id, $post_data['post_data']['post_content']); … … 722 732 723 733 // parse for Shortcodes #102 724 $res = $this-> _parse_shortcodes($post_id, $post_data['post_data']['post_content'], $data);725 SyncDebug::log(__METHOD__ .'():' . __LINE__ . ' after _parse_shortcodes() res=' . var_export($res, TRUE));734 $res = $this->parse_shortcodes($post_id, $post_data['post_data']['post_content'], $data); 735 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' after parse_shortcodes() res=' . var_export($res, TRUE)); 726 736 if (is_wp_error($res)) 727 737 return $res; … … 733 743 734 744 $data = apply_filters('spectrom_sync_api_push_content', $data, $this); 735 $this->_post_data = &$data; // reset reference to new data 736 745 $this->_post_data = &$data; // reset reference to new data 737 746 // after all processing, check for performing 'push_complete' API call 738 SyncDebug::log(__METHOD__ .'():' . __LINE__ . ' all content processed; id refs=' . count($this->id_refs) . ' sent images=' . count($this->_sent_images) . ' trigger=' . ($this->_trigger_push_complete ? 'TRUE' : 'FALSE'));747 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' all content processed; id refs=' . count($this->id_refs) . ' sent images=' . count($this->_sent_images) . ' trigger=' . ($this->_trigger_push_complete ? 'TRUE' : 'FALSE')); 739 748 // there are IDs to update, use the 'push_complete' API to indicate completion and update IDs on Target 740 749 // TODO: check ApiResponse instance for has_errors() 741 750 if ($this->_trigger_push_complete || 742 751 (0 !== count($this->id_refs) || 0 !== count($this->_sent_images))) { 743 $this->trigger_push_complete(); // indicate that 'push_complete' API is requred752 $this->trigger_push_complete(); // indicate that 'push_complete' API is requred 744 753 } 745 754 … … 775 784 unset($data['contents']); 776 785 /** 777 array (778 'username' =>779 'password' =>780 'host' =>781 'auth' =>782 array (783 'cookie' =>784 'nonce' =>785 'site_key' =>786 )786 array ( 787 'username' => 788 'password' => 789 'host' => 790 'auth' => 791 array ( 792 'cookie' => 793 'nonce' => 794 'site_key' => 795 ) 787 796 */ 788 797 $headers = array( … … 842 851 // TODO: we'll need to add the media sizes on the Source to the data being sent so the Target can generate image sizes 843 852 844 if (empty($content)) { // need to continue even with empty content. otherwise featured image doesn't get processed845 $xml = NULL; // use this to denote empty content and skip looping through <img> and <a> tags #180853 if (empty($content)) { // need to continue even with empty content. otherwise featured image doesn't get processed 854 $xml = NULL; // use this to denote empty content and skip looping through <img> and <a> tags #180 846 855 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' content is empty, not searching <img> and <a> tags'); 847 856 } else { … … 852 861 // TODO: can we use get_media_embedded_in_content()? 853 862 libxml_clear_errors(); 854 libxml_use_internal_errors(TRUE); // disable the error messages generated from unrecognized DOM elements863 libxml_use_internal_errors(TRUE); // disable the error messages generated from unrecognized DOM elements 855 864 $xml = new DOMDocument(); 856 865 … … 861 870 $xml->loadHTML($content); 862 871 } catch (Exception $ex) { 863 $xml = NULL; // any errors in parsing; mark it as empty content so processing continues #180872 $xml = NULL; // any errors in parsing; mark it as empty content so processing continues #180 864 873 } 865 874 } … … 868 877 $post_thumbnail_id = abs(get_post_thumbnail_id($post_id)); 869 878 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' post thumb id=' . $post_thumbnail_id); 870 $this->_sent_images = array(); // list of images already sent. Used by _send_image() to not send the same image twice879 $this->_sent_images = array(); // list of images already sent. Used by _send_image() to not send the same image twice 871 880 // set source domain; used to detect media elements to be added to push queue 872 $this->set_source_domain( site_url('url'));881 $this->set_source_domain(); 873 882 874 883 // used in processing <a> tags. Don't need to do this if content is empty #180 … … 884 893 $post_children = NULL; 885 894 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' children=' . var_export($post_children, TRUE)); 886 887 895 // only used in processing <img> tags. Don't need to do this if content is empty #180 888 896 $attach_model = new SyncAttachModel(); … … 890 898 891 899 // search for <img> tags within content 892 if (NULL !== $xml) { // don't look for <img> elements if content is empty #180900 if (NULL !== $xml) { // don't look for <img> elements if content is empty #180 893 901 $tags = $xml->getElementsByTagName('img'); 894 902 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' found ' . $tags->length . ' <img> tags'); … … 918 926 $src_attr = NULL; 919 927 } else { 920 $img_id = 0; // if not valid, clear id to indicate use of fallback method below #162928 $img_id = 0; // if not valid, clear id to indicate use of fallback method below #162 921 929 } 922 930 break; … … 960 968 961 969 // search through <a> tags within content 962 if (NULL !== $xml) { // don't look for <img> elements if content is empty #180970 if (NULL !== $xml) { // don't look for <img> elements if content is empty #180 963 971 $tags = $xml->getElementsByTagName('a'); 964 972 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' found ' . $tags->length . ' <a> tags'); … … 983 991 } 984 992 } 985 if (0 !== $attach_id) // https://wordpress.org/support/topic/bugs-68/993 if (0 !== $attach_id) // https://wordpress.org/support/topic/bugs-68/ 986 994 $this->send_media($href_attr, $post_id, $post_thumbnail_id, $attach_id); 987 995 } else { … … 997 1005 $this->post_children[] = $attachment->ID; 998 1006 } 999 $this->trigger_push_complete(); // need to force push_complete API to handle attaching post children1007 $this->trigger_push_complete(); // need to force push_complete API to handle attaching post children 1000 1008 } 1001 1009 … … 1004 1012 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' featured image: ' . $post_thumbnail_id); 1005 1013 $img = wp_get_attachment_image_src($post_thumbnail_id, 'full'); 1006 if (FALSE === $img) SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' wp_get_attachment_image_src() failed'); #!#1014 if (FALSE === $img) SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' wp_get_attachment_image_src() failed');#!# 1007 1015 // check image URL and see if it doesn't match Source domain. #131 1008 1016 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' source domain: ' . $this->_source_domain); … … 1016 1024 } 1017 1025 } 1018 SyncDebug::log(__METHOD__ .'():' . __LINE__ . ' src=' . var_export($img, TRUE));1026 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' src=' . var_export($img, TRUE)); 1019 1027 // convert site url to relative path 1020 1028 if (FALSE !== $img) { … … 1059 1067 } // 0 !== count 1060 1068 } // isset 1061 SyncDebug::log(__METHOD__ .'():' . __LINE__ . ' done processing media');1069 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' done processing media'); 1062 1070 return TRUE; 1063 1071 } … … 1070 1078 * @return boolean|WP_Error return boolean TRUE or WP_Error instance with more information 1071 1079 */ 1072 p rivate function _parse_shortcodes($post_id, $content, &$data)1080 public function parse_shortcodes($post_id, $content, &$data) 1073 1081 { 1074 1082 // get a list of all shortcodes to be processed … … 1079 1087 1080 1088 $num = preg_match_all('/' . $pattern . '/s', $content, $matches); 1081 SyncDebug::log(__METHOD__ .'():' . __LINE__ . ' matches=' . var_export($matches, TRUE));1089 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' matches=' . var_export($matches, TRUE)); 1082 1090 if ($num > 0 && array_key_exists(2, $matches)) { 1083 1091 // one or more of the shortcodes is found within the content 1084 1092 $idx = 0; 1085 foreach ($matches[2] as $shortcode) { // examine each shortcode1093 foreach ($matches[2] as $shortcode) { // examine each shortcode 1086 1094 $sce = new SyncShortcodeEntry($shortcode, $matches[0][$idx], $matches[3][$idx]); 1087 1095 1088 1096 // check all known attributes to see if they need processing 1089 1097 foreach ($sce->parse_attributes($shortcodes[$shortcode]) as $type_name => $type_code) { 1090 if ($sce->has_attribute($type_name)) { // if the current shortcode has the mentioned attribute1098 if ($sce->has_attribute($type_name)) { // if the current shortcode has the mentioned attribute 1091 1099 // TODO: the [gallery include= exclude=] list to do nothing on Source, update IDs on Target 1092 1100 switch ($type_code) { 1093 1101 case SyncShortcodeEntry::TYPE_IMAGE_ID: 1094 1102 case SyncShortcodeEntry::TYPE_POST_ID: 1095 // TODO: push all attachments of this post ID1103 // TODO: push all attachments of this post ID 1096 1104 case SyncShortcodeEntry::TYPE_IMAGE_LIST: 1097 1105 case SyncShortcodeEntry::TYPE_POST_LIST: … … 1099 1107 $ids = trim($ids, '"\''); 1100 1108 $id_list = explode(',', $ids); 1101 foreach ($id_list as $id) { // look up all IDs referenced by the attribute1109 foreach ($id_list as $id) { // look up all IDs referenced by the attribute 1102 1110 $id = abs($id); 1103 SyncDebug::log(__METHOD__ .'():' . __LINE__ . ' found post reference: ' . $id);1104 if (0 !== $id ) {1105 SyncDebug::log(__METHOD__ .'():' . __LINE__ . ' handling post ID ' . $id);1111 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' found post reference: ' . $id); 1112 if (0 !== $id) { 1113 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' handling post ID ' . $id); 1106 1114 if (SyncShortcodeEntry::TYPE_IMAGE_ID === $type_code || SyncShortcodeEntry::TYPE_IMAGE_LIST === $type_code) { 1107 1115 // id refers to an image- make sure image gets pushed 1108 SyncDebug::log(__METHOD__ .'():' . __LINE__ . ' calling send_media_by_id(' . $id . ')');1116 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' calling send_media_by_id(' . $id . ')'); 1109 1117 $this->send_media_by_id($id); 1110 1118 } else if (SyncShortcodeEntry::TYPE_POST_ID === $type_code || SyncShortcodeEntry::TYPE_POST_LIST === $type_code) { 1111 1119 // id refers to a post- give Target site a change to update them 1112 SyncDebug::log(__METHOD__ .'():' . __LINE__ . ' signaling "push_complete" is required');1113 $this->_trigger_push_complete = TRUE; // signal 'push_complete' is required1120 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' signaling "push_complete" is required'); 1121 $this->_trigger_push_complete = TRUE; // signal 'push_complete' is required 1114 1122 } else { 1115 SyncDebug::log(__METHOD__ .'():' . __LINE__ . ' type code=' . $type_code);1123 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' type code=' . $type_code); 1116 1124 } 1117 1125 } … … 1120 1128 1121 1129 case SyncShortcodeEntry::TYPE_POST_ATTACH: 1122 $id = abs($sce->get_attribute($type_name)); // post ID to use for gallery1123 SyncDebug::log(__METHOD__ .'():' . __LINE__ . ' check gallery id reference ' . $id);1130 $id = abs($sce->get_attribute($type_name)); // post ID to use for gallery 1131 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' check gallery id reference ' . $id); 1124 1132 if (0 !== $id) { 1125 1133 $model = new SyncModel(); 1126 1134 $sync_data = $model->get_sync_data($id); 1127 1135 if (NULL === $sync_data) { 1128 SyncDebug::log(__METHOD__ .'():' . __LINE__ . ' dependent post id ' . $id . ' has not been pushed');1136 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' dependent post id ' . $id . ' has not been pushed'); 1129 1137 $this->_response->error_code(self::ERROR_UNRESOLVED_PARENT, $id); 1130 1138 } else { … … 1132 1140 // are sent to the Target 1133 1141 $attachments = get_children($id, OBJECT); 1134 SyncDebug::log(__METHOD__ .'():' . __LINE__ . ' attachments: ' . var_export($attachments, TRUE));1142 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' attachments: ' . var_export($attachments, TRUE)); 1135 1143 foreach ($attachments as $attachment) { 1136 1144 $attach_id = $attachment->ID; 1137 SyncDebug::log(__METHOD__ .'():' . __LINE__ . ' adding attachment id #'. $attach_id . ': ' . var_export($attachment, TRUE));1145 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' adding attachment id #' . $attach_id . ': ' . var_export($attachment, TRUE)); 1138 1146 $this->send_media_by_id($attach_id); 1139 1147 } … … 1159 1167 1160 1168 default: 1161 SyncDebug::log(__METHOD__ .'():' . __LINE__ . ' unrecognized attribute type code: ' . $type_code);1169 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' unrecognized attribute type code: ' . $type_code); 1162 1170 break; 1163 1171 } // switch ($type_code) … … 1196 1204 // <!-- wp:file {"id":{post_id},"href":"{file-uri}"} --> 1197 1205 // <!-- wp:media-text {"mediaId":{post_id},"mediaType":"video"} --> 1198 1199 1206 // don't bother processing if there's no Gutenberg content 1200 1207 if (function_exists('has_blocks') && !has_blocks($content)) … … 1203 1210 return; 1204 1211 1212 if (NULL === $this->_source_domain) 1213 $this->set_source_domain(); 1214 1205 1215 $post_thumbnail_id = abs(get_post_thumbnail_id($post_id)); // needed for send_media() calls 1206 $attach_model = new SyncAttachModel(); // used for regex image search #2111207 1208 $this->gutenberg_queue = array(); // init work queueu1209 $this->gutenberg_processed = array(); // init processed queue1216 $attach_model = new SyncAttachModel(); // used for regex image search #211 1217 1218 $this->gutenberg_queue = array(); // init work queueu 1219 $this->gutenberg_processed = array(); // init processed queue 1210 1220 $this->gutenberg_add_queue($post_id); 1211 $error = FALSE; // set initial error condition1221 $error = FALSE; // set initial error condition 1212 1222 // Process all items in queue. During processing Shared Blocks are added to queue 1213 1223 // so that image references within those Blocks can also be checked. … … 1217 1227 $content = $work_post->post_content; 1218 1228 } 1219 $len = strlen($content); // length of content1220 $offset = 0; // pointer into string for where search currently is1229 $len = strlen($content); // length of content 1230 $offset = 0; // pointer into string for where search currently is 1221 1231 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' starting work on ID ' . $work_post_id . ' with ' . $len . ' bytes of content'); 1222 1232 do { … … 1225 1235 if (FALSE !== $pos) { 1226 1236 // found a beginning marker: "<!-- wp:" 1227 $ref_id = 0; // initialize reference ID value1237 $ref_id = 0; // initialize reference ID value 1228 1238 1229 1239 $pos_space = strpos($content, ' ', $pos + 5); // space after block name … … 1242 1252 --$end; 1243 1253 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' end=' . $end . ' [' . substr($content, $end - 3, 10) . ']'); 1244 $json = NULL; // initialize decoded json object1254 $json = NULL; // initialize decoded json object 1245 1255 if (FALSE !== $end) { 1246 1256 $end -= 2; 1247 1257 $json = substr($content, $start, $end - $start + 1); 1248 1258 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' json=[' . $json . ']'); 1249 if (empty($json)) // if json string is empty1250 $json = NULL; // reset to NULL1259 if (empty($json)) // if json string is empty 1260 $json = NULL; // reset to NULL 1251 1261 } else { 1252 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' could not find end of block marker. off=' . $offset . ' data=' . substr($content, $start, 30));1262 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' could not find end of block marker. off=' . $offset . ' data=' . substr($content, $start, 30)); 1253 1263 } 1254 1264 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' json=[' . $json . ']'); 1255 1265 // if there is json data, decode and process it 1256 1266 if (NULL !== $json) { 1257 $obj = json_decode($json);1267 $obj = $this->gutenberg_obj = json_decode($json); 1258 1268 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' found json string: "' . $json . '"'); 1259 1269 1260 // TODO: need to add error checking when referenced IDs are missing, etc. 1270 $process = TRUE; 1271 // check .url property and skip if it references s.w.org #279 1272 if (property_exists($obj, 'url')) { 1273 $url_host = parse_url($obj->url, PHP_URL_HOST); 1274 // TODO: check if host !== Source host 1275 if ($url_host !== $this->_source_domain) 1276 // the URL refers to s.w.org or some other non-Source domain 1277 // skip these so we don't update Patterns, etc. 1278 $process = FALSE; 1279 // TODO: check for additional references that need to be skipped 1280 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' process block=' . ($process ? 'TRUE' : 'FALSE')); 1281 } 1282 //$process = TRUE; 1261 1283 // TODO: how does Gutenberg deal with these ID references if they've been deleted after the ID is written to the Block Marker? 1262 // handle each Block Marker individually 1263 switch ($block_name) { 1264 case 'wp:block': // Shared Block reference - post reference 1265 $ref_id = abs($obj->ref); 1266 $this->gutenberg_add_queue($ref_id); // add Shared Block post ID to the work queue 1284 // check to see if this block should be processed 1285 if ($process) { 1286 // handle each Block Marker individually 1287 switch ($block_name) { 1288 case 'wp:block': // Shared Block reference - post reference 1289 $ref_id = isset($obj->ref) ? abs($obj->ref) : 0; 1290 if (0 !== $ref_id) { 1291 $this->gutenberg_add_queue($ref_id); // add Shared Block post ID to the work queue 1267 1292 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' found shared block reference: ' . $ref_id . ' at pos ' . ($pos + 21)); 1268 if (0 !== $ref_id && !isset($this->id_refs[$ref_id])) {1269 // ID has not already been processed (no duplicates)1270 $ref_post = get_post($ref_id, ARRAY_A);1271 $this->id_refs[$ref_id] = array($block_name, $ref_post);1293 if (0 !== $ref_id && !isset($this->id_refs[$ref_id])) { 1294 // ID has not already been processed (no duplicates) 1295 $ref_post = get_post($ref_id, ARRAY_A); 1296 $this->id_refs[$ref_id] = array($block_name, $ref_post); 1272 1297 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' found reference to post id ' . $ref_id . ' "' . $ref_post['post_title'] . '"'); 1273 } 1274 // TODO: error recovery 1275 break; 1276 1277 case 'wp:cover': // Cover Block - image reference 1278 if (FALSE === $this->gutenberg_attachment_block($obj->id, $work_post_id, $post_thumbnail_id, $block_name)) { 1279 // TODO: handle error recovery 1280 } 1281 break; 1282 1283 case 'wp:audio': // Audio Block- resource reference 1284 case 'wp:video': // Video Block- resource reference 1285 case 'wp:image': // Image Block- resource reference 1286 if (FALSE === $this->gutenberg_attachment_block($obj->id, $work_post_id, $post_thumbnail_id, $block_name)) { 1287 // TODO: handle error recovery 1288 } 1289 break; 1290 1291 case 'wp:media-text': 1298 } 1299 } 1300 // TODO: error recovery 1301 break; 1302 1303 case 'wp:cover': // Cover Block - image reference 1304 // check for NULL references in "Pattern" blocks. these can be skipped #280 1305 if (isset($obj->id) && NULL !== $obj->id) { 1306 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' wp:cover id=' . $obj->id . ' post id=' . $word_post_id); 1307 if (FALSE === $this->gutenberg_attachment_block($obj->id, $work_post_id, $post_thumbnail_id, $block_name)) { 1308 // attachment not found, report error 1309 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' error handling cover block id ' . $obj->id . ' in block "' . $block_name . '"'); 1310 $this->_response->error_code(self::ERROR_GB_ATTACHMENT_ID, $block_name); 1311 } 1312 } 1313 break; 1314 1315 case 'wp:audio': // Audio Block- resource reference 1316 case 'wp:video': // Video Block- resource reference 1317 case 'wp:image': // Image Block- resource reference 1318 // check for NULL references in "Pattern" blocks. these can be skipped #280 1319 $obj_id = isset($obj->id) ? abs($obj->id) : 0; 1320 if (0 !== $obj_id) { 1321 if (FALSE === $this->gutenberg_attachment_block($obj_id, $work_post_id, $post_thumbnail_id, $block_name)) { 1322 // attachment not found, report error 1323 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' error handling attachment block id ' . $obj_id . ' in block "' . $block_name . '"'); 1324 $this->_response->error_code(self::ERROR_GB_ATTACHMENT_ID, $block_name); 1325 } 1326 } 1327 break; 1328 1329 case 'wp:media-text': 1330 // check for NULL references in "Pattern" blocks. these can be skipped #280 1331 if (isset($obj->mediaId) && NULL !== $obj->mediaId) { 1292 1332 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' mediaID=' . $obj->mediaId); 1293 if (FALSE === $this->gutenberg_attachment_block($obj->mediaId, $work_post_id, $post_thumbnail_id, $block_name)) { 1294 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' error handling attachment block id=' . $obj->mediaID); 1295 // TODO: handle error recovery 1296 } 1297 break; 1298 1299 case 'wp:gallery': // Gallery Block- multiple image references 1300 $ref_ids = $obj->ids; 1301 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' found reference ids=' . implode(', ', $ref_ids)); 1302 foreach ($ref_ids as $ref_id) { 1303 if (FALSE === $this->gutenberg_attachment_block($ref_id, $work_post_id, $post_thumbnail_id, $block_name)) { 1304 // TODO: handle error 1333 if (FALSE === $this->gutenberg_attachment_block($obj->mediaId, $work_post_id, $post_thumbnail_id, $block_name)) { 1334 // attachment not found, report error 1335 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' error handling attachment block id ' . $obj->mediaID . ' in block "' . $block_name . '"'); 1336 $this->_response->error_code(self::ERROR_GB_ATTACHMENT_ID, $block_name); 1337 } 1305 1338 } 1306 } 1307 break; 1308 1309 case 'wp:file': // File Block- resource reference 1310 $ref_id = abs($obj->id); 1311 if ($this->gutenberg_attachment_block($ref_id, $work_post_id, $post_thumbnail_id, $block_name)) { 1312 $ref_post = get_post($ref_id); 1313 if (NULL !== $ref_post) { 1339 break; 1340 1341 case 'wp:gallery': // Gallery Block- multiple image references 1342 $ref_ids = isset($obj->ids) ? $obj->ids : array(); 1343 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' found reference ids=' . var_export($ref_ids, TRUE)); // implode(', ', $ref_ids)); 1344 foreach ($ref_ids as $ref_id) { 1345 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' checking id=' . var_export($ref_id, TRUE)); 1346 // check for NULL references in "Pattern" blocks. these can be skipped #280 1347 if (NULL !== $ref_id) { 1348 if (FALSE === $this->gutenberg_attachment_block($ref_id, $work_post_id, $post_thumbnail_id, $block_name)) { 1349 // attachment not found, report error 1350 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' error findling attachment block id ' . $ref_id . ' in block "' . $block_name . '"'); 1351 $this->_response->error_code(self::ERROR_GB_ATTACHMENT_ID, $block_name); 1352 } 1353 } 1354 } 1355 break; 1356 1357 case 'wp:file': // File Block- resource reference 1358 $ref_id = isset($obj->id) ? abs($obj->id) : 0; 1359 if (0 !== $ref_id && $this->gutenberg_attachment_block($ref_id, $work_post_id, $post_thumbnail_id, $block_name)) { 1360 $ref_post = get_post($ref_id); 1361 if (NULL !== $ref_post) { 1314 1362 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' adding reference id ' . $ref_id . ' post ' . var_export($ref_post, TRUE)); 1315 $this->id_refs[$ref_id] = array($block_name, $ref_post); 1363 $this->id_refs[$ref_id] = array($block_name, $ref_post); 1364 } 1365 } else { 1366 // attachment not found, report error 1367 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' error finding attachment block id ' . $ref_id . ' in block "' . $block_name . '"'); 1368 $this->_response->error_code(self::ERROR_GB_ATTACHMENT_ID, $block_name); 1316 1369 } 1317 } else { 1318 // TODO: handle error recovery 1319 } 1320 break; 1321 1322 case 'wp:latest-posts': 1323 $cat_id = abs($obj->categories); 1324 if (0 !== $cat_id) { 1325 if (FALSE === $this->gutenberg_taxonomy_block($cat_id, $post_id, $block_name)) { 1326 // TODO: error recovery 1370 break; 1371 1372 case 'wp:latest-posts': 1373 $cat_id = isset($obj->categories) ? abs($obj->categories) : 0; 1374 if (0 !== $cat_id) { 1375 if (FALSE === $this->gutenberg_taxonomy_block($cat_id, $post_id, $block_name)) { 1376 // taxonomy not found, report error 1377 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' error finding taxonomy id ' . $cat_id . ' in "' . $block_name . '"'); 1378 $this->_response->error_code(self::ERROR_GB_TAX_ID, $block_name); 1379 } 1380 $this->trigger_push_complete(); // indicate that 'push_complete' API is requred 1327 1381 } 1328 $this->trigger_push_complete(); // indicate that 'push_complete' API is requred 1329 } 1330 break; 1331 1332 default: 1333 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' calling do_action "spectrom_sync_parse_gutenberg_block"'); 1334 // give other add-ons a chance to process this block 1335 do_action('spectrom_sync_parse_gutenberg_block', $block_name, $json, $post_id, $data, $pos, $this); 1336 break; 1337 } // switch ($block_name) 1382 break; 1383 1384 default: 1385 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' calling do_action "spectrom_sync_parse_gutenberg_block"'); 1386 // give other add-ons a chance to process this block 1387 do_action('spectrom_sync_parse_gutenberg_block', $block_name, $json, $post_id, $data, $pos, $this); 1388 break; 1389 } // switch ($block_name) 1390 } // $process 1338 1391 } else { 1339 1392 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' could not parse json object'); 1340 1393 } // NULL !== $json 1341 $offset = $end + 3; // move offset to the end of the Block Marker comment1394 $offset = $end + 3; // move offset to the end of the Block Marker comment 1342 1395 } else { 1343 1396 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' no json object in block'); … … 1424 1477 public function gutenberg_taxonomy_block($term_id, $post_id, $block_name) 1425 1478 { 1426 SyncDebug::log(__METHOD__ ."({$term_id}, {$post_id}, '{$block_name}')");1479 SyncDebug::log(__METHOD__ . "({$term_id}, {$post_id}, '{$block_name}')"); 1427 1480 $term = get_term($term_id); 1428 SyncDebug::log(__METHOD__ .'():' . __LINE__ . ' term=' . var_export($term, TRUE));1481 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' term=' . var_export($term, TRUE)); 1429 1482 if (NULL !== $term && !is_wp_error($term)) { 1430 1483 $tax = get_taxonomy($term->taxonomy); 1431 SyncDebug::log(__METHOD__ .'():' . __LINE__ . ' tax=' . var_export($tax, TRUE));1484 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' tax=' . var_export($tax, TRUE)); 1432 1485 if (FALSE !== $tax) { 1433 SyncDebug::log(__METHOD__ .'():'.__LINE__ . ' term=' . var_export($term, TRUE));1486 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' term=' . var_export($term, TRUE)); 1434 1487 if ($tax->hierarchical) { 1435 SyncDebug::log(__METHOD__ .'():' . __LINE__ . ' hierarchical taxonomy');1488 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' hierarchical taxonomy'); 1436 1489 // handle the hierarchical taxonomy stuff 1437 $term->ref_only = 1; // signifies it's a term reference, not to be assigned to the post via wp_add_object_terms()1490 $term->ref_only = 1; // signifies it's a term reference, not to be assigned to the post via wp_add_object_terms() 1438 1491 $this->_post_data['taxonomies']['hierarchical'][] = $term; 1439 1492 … … 1445 1498 $parent = $term->parent; 1446 1499 } 1447 SyncDebug::log(__METHOD__ .'():' . __LINE__ . ' added term to [taxonomy][hierarchical] list: ' . var_export($this->_post_data['taxonomies']['hierarchical'], TRUE));1500 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' added term to [taxonomy][hierarchical] list: ' . var_export($this->_post_data['taxonomies']['hierarchical'], TRUE)); 1448 1501 } else { 1449 SyncDebug::log(__METHOD__ .'():' . __LINE__ . ' non-hierarchical taxonomy');1450 $term->ref_only = 1; // signifies it's a term reference, not to be assigned to the post via wp_add_object_terms()1502 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' non-hierarchical taxonomy'); 1503 $term->ref_only = 1; // signifies it's a term reference, not to be assigned to the post via wp_add_object_terms() 1451 1504 $this->_post_data['taxonomies']['flat'][] = $term; 1452 SyncDebug::log(__METHOD__ .'():' . __LINE__ . ' add term to [taxonomy][flat] list: ' . var_export($this->_post_data['taxonomies']['flat'], TRUE));1505 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' add term to [taxonomy][flat] list: ' . var_export($this->_post_data['taxonomies']['flat'], TRUE)); 1453 1506 } 1454 1507 return TRUE; 1455 1508 } 1456 SyncDebug::log(__METHOD__ .'():'.__LINE__ . ' tax data=' . var_export($this->_post_data['taxonomies'], TRUE));1509 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' tax data=' . var_export($this->_post_data['taxonomies'], TRUE)); 1457 1510 } 1458 1511 return FALSE; … … 1481 1534 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' adding "push_complete" callback'); 1482 1535 add_action('spectrom_sync_push_queue_complete', array($this, 'push_complete_api'), 10, 1); 1483 $this->_triggered_push_complete = TRUE; // indicate this has been triggered1536 $this->_triggered_push_complete = TRUE; // indicate this has been triggered 1484 1537 } 1485 1538 } … … 1494 1547 // we've sent image/content references to the Target- need to send process compelte API call to Target 1495 1548 $api_data = array( 1496 'post_id' => $this->post_id, // the Source Post ID to be updated1497 'id_refs' => $this->id_refs, // data and Source IDs to update1498 'children' => $this->post_children, // list of attachments as post children1549 'post_id' => $this->post_id, // the Source Post ID to be updated 1550 'id_refs' => $this->id_refs, // data and Source IDs to update 1551 'children' => $this->post_children, // list of attachments as post children 1499 1552 ); 1500 1553 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' data=' . var_export($api_data, TRUE)); … … 1507 1560 * @param string $domain The domain name to set. Use domain only, no protocol or slashes 1508 1561 */ 1509 public function set_source_domain($domain) 1510 { 1562 public function set_source_domain($domain = NULL) 1563 { 1564 if (NULL === $domain) 1565 $domain = site_url('url'); // assume current site's domain name 1566 1511 1567 //SyncDebug::log(__METHOD__.'() domain=' . $domain); 1512 1568 // sanitize value to remove protocol and slashes … … 1527 1583 { 1528 1584 $image_id = abs($image_id); 1529 SyncDebug::log(__METHOD__ .'():' . __LINE__ . ' image reference ' . $image_id);1585 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' image reference ' . $image_id); 1530 1586 if (0 !== $image_id) { 1531 1587 $post = get_post($image_id); 1532 if ('attachment' !== $post->post_type) {1533 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' post id ' . $image_id . ' is not an image (' . $post->post_type . ')');1534 }1588 if ('attachment' !== $post->post_type) { 1589 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' post id ' . $image_id . ' is not an image (' . $post->post_type . ')'); 1590 } 1535 1591 if (NULL !== $post) { 1536 1592 $attach = $this->url_to_path($post->guid); … … 1544 1600 return TRUE; 1545 1601 } else { 1546 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' ERROR adding media file "' . $ ref_att. '" to queue');1602 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' ERROR adding media file "' . $attach . '" to queue'); 1547 1603 // return FALSE; 1548 1604 } … … 1578 1634 1579 1635 $src_parts = parse_url($url); 1580 if (empty($src_parts['host'])) // allow for empty host with relative urls #2501636 if (empty($src_parts['host'])) // allow for empty host with relative urls #250 1581 1637 $src_parts['host'] = ''; 1582 1638 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' url=' . $url . ' parts=' . var_export($src_parts, TRUE)); … … 1657 1713 'attach_id' => $attach_id, 1658 1714 'featured' => abs($featured), 1659 'boundary' => wp_generate_password(24), // TODO: remove and generate when formatting POST content in _media() 1715 // TODO: remove and generate when formatting POST content in _media() 1716 'boundary' => wp_generate_password(24), 1660 1717 'img_path' => dirname($file_path), 1661 1718 'img_name' => basename($file_path), … … 1693 1750 private function _get_image_contents($file_path) 1694 1751 { 1695 SyncDebug::log(__METHOD__ .'():' . __LINE__ . ' path=' . $file_path);1752 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' path=' . $file_path); 1696 1753 1697 1754 // adjust file path if running within multisite #167 … … 1699 1756 $to_dir = '/wp-content/blogs.dir/' . get_current_blog_id() . '/'; 1700 1757 $file_path = str_replace('/wp-content/files/', $to_dir, $file_path); 1701 SyncDebug::log(__METHOD__ .'():' . __LINE__ . ' adjusted multisite file path to ' . $file_path);1702 } 1703 1704 SyncDebug::log(__METHOD__ .'():' . __LINE__ . ' reading file contents: ' . $file_path);1758 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' adjusted multisite file path to ' . $file_path); 1759 } 1760 1761 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' reading file contents: ' . $file_path); 1705 1762 // TODO: rework to use CURLFile class and @filename specifier (for PHP < 5.5) to save memory on large files #165 1706 1763 // first, try file_get_contents() … … 1719 1776 } 1720 1777 1721 if (FALSE === $contents) #!# 1722 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' ERROR: unable to obtain image contents'); #!# 1723 1778 if (FALSE === $contents) #!# 1779 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' ERROR: unable to obtain image contents');#!# 1724 1780 // TODO: try using filesystem 1725 1781 … … 1734 1790 public function url_to_path($url) 1735 1791 { 1736 SyncDebug::log(__METHOD__ ."('{$url}'):" . __LINE__);1792 SyncDebug::log(__METHOD__ . "('{$url}'):" . __LINE__); 1737 1793 // $domain = parse_url(site_url(), PHP_URL_HOST); // local install's domain name 1738 1794 $parts = parse_url($url); 1739 SyncDebug::log(__METHOD__ .'():' . __LINE__ . ' url parts=' . var_export($parts, TRUE));1795 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' url parts=' . var_export($parts, TRUE)); 1740 1796 $site_url = site_url(); 1741 1797 // check for empty elements when referencing relative urls #250 … … 1745 1801 $parts['scheme'] = parse_url($site_url, PHP_URL_SCHEME); 1746 1802 $domain = $parts['host']; 1747 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' url parts=' . var_export($parts, TRUE));1803 //SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' url parts=' . var_export($parts, TRUE)); 1748 1804 1749 1805 // adjust path to account for subdirectory installs #223 1750 1806 if (0 !== ($pos = stripos($parts['path'], '/wp-content'))) { 1751 $parts['path'] = substr($parts['path'], $pos);1807 #226911 $parts['path'] = substr($parts['path'], $pos); 1752 1808 } 1753 1809 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' domain=' . $domain . ' site=' . $site_url . ' parts=' . var_export($parts, TRUE)); … … 1755 1811 // if it's a URL reference and on the same host, convert to filesystem path 1756 1812 if (('http' === $parts['scheme'] || 'https' === $parts['scheme']) && 1757 0 === strcasecmp($domain, $parts['host'])) { // compare case insignificant #1701813 0 === strcasecmp($domain, $parts['host'])) { // compare case insignificant #170 1758 1814 // if (!function_exists('get_home_path')) { 1759 1815 // require_once ABSPATH . '/wp-admin/file.php'; 1760 1816 // } 1761 $home = $this->get_home_path(); // get directory of WP install #1871817 $home = $this->get_home_path(); // get directory of WP install #187 1762 1818 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' home=' . $home); 1763 1819 SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' path=' . $parts['path']); … … 1813 1869 $error = ''; 1814 1870 switch ($code) { 1815 case self::ERROR_CANNOT_CONNECT: $error = __('Unable to connect to Target site.', 'wpsitesynccontent'); break;1816 case self::ERROR_UNRECOGNIZED_REQUEST: $error = __('The requested action is not recognized. Is plugin activated on Target?', 'wpsitesynccontent'); break;1817 case self::ERROR_NOT_INSTALLED: $error = __('WPSiteSync for Content is not installed and activated on Target site.', 'wpsitesynccontent'); break;1818 case self::ERROR_BAD_CREDENTIALS: $error = __('Unable to authenticate on Target site.', 'wpsitesynccontent'); break;1819 case self::ERROR_SESSION_EXPIRED: $error = __('User session has expired.', 'wpsitesynccontent'); break;1820 case self::ERROR_CONTENT_EDITING: $error = __('A user is currently editing this Content on the Target site.', 'wpsitesynccontent'); break;1871 case self::ERROR_CANNOT_CONNECT: $error = __('Unable to connect to Target site.', 'wpsitesynccontent'); break; 1872 case self::ERROR_UNRECOGNIZED_REQUEST: $error = __('The requested action is not recognized. Is plugin activated on Target?', 'wpsitesynccontent'); break; 1873 case self::ERROR_NOT_INSTALLED: $error = __('WPSiteSync for Content is not installed and activated on Target site.', 'wpsitesynccontent'); break; 1874 case self::ERROR_BAD_CREDENTIALS: $error = __('Unable to authenticate on Target site.', 'wpsitesynccontent'); break; 1875 case self::ERROR_SESSION_EXPIRED: $error = __('User session has expired.', 'wpsitesynccontent'); break; 1876 case self::ERROR_CONTENT_EDITING: $error = __('A user is currently editing this Content on the Target site.', 'wpsitesynccontent'); break; 1821 1877 case self::ERROR_CONTENT_LOCKED: 1822 1878 if (NULL !== $data) … … 1825 1881 $error - __('A user is currently editing this Content on the Target site.', 'wpsitesynccontent'); 1826 1882 break; 1827 case self::ERROR_POST_DATA_INCOMPLETE: $error = __('Some or all of the data for the request is missing.', 'wpsitesynccontent'); break; 1828 case self::ERROR_USER_NOT_FOUND: $error = __('The username does not exist on the Target site.', 'wpsitesynccontent'); break; 1829 case self::ERROR_FILE_UPLOAD: $error = __('Error while handling file upload.', 'wpsitesynccontent'); break; 1830 case self::ERROR_PERMALINK_MISMATCH: $error = __('The Permalink settings are different on the Target site.', 'wpsitesynccontent'); break; 1831 case self::ERROR_WP_VERSION_MISMATCH: $error = __('The WordPress versions are different on the Source and Target sites.', 'wpsitesynccontent'); break; 1832 case self::ERROR_SYNC_VERSION_MISMATCH: $error = __('The WPSiteSync versions are different on the Source and Target sites.', 'wpsitesynccontent'); break; 1833 case self::ERROR_EXTENSION_MISSING: $error = __('The required WPSiteSync extension is not active on the Target site.', 'wpsitesynccontent'); break; 1834 case self::ERROR_INVALID_POST_TYPE: $error = __('The post type is not allowed.', 'wpsitesynccontent'); break; 1835 case self::ERROR_REMOTE_REQUEST_FAILED: $error = __('Unable to make API request to Target system.', 'wpsitesynccontent'); break; 1883 case self::ERROR_POST_DATA_INCOMPLETE: $error = __('Some or all of the data for the request is missing.', 'wpsitesynccontent'); break; 1884 case self::ERROR_USER_NOT_FOUND: $error = __('The username does not exist on the Target site.', 'wpsitesynccontent'); break; 1885 case self::ERROR_FILE_UPLOAD: $error = __('Error while handling file upload.', 'wpsitesynccontent'); break; 1886 case self::ERROR_PERMALINK_MISMATCH: $error = __('The Permalink settings are different on the Target site.', 'wpsitesynccontent'); break; 1887 case self::ERROR_WP_VERSION_MISMATCH: $error = __('The WordPress versions are different on the Source and Target sites.', 'wpsitesynccontent'); break; 1888 case self::ERROR_SYNC_VERSION_MISMATCH: $error = __('The WPSiteSync versions are different on the Source and Target sites.', 'wpsitesynccontent'); break; 1889 case self::ERROR_EXTENSION_MISSING: 1890 if (NULL === $data && !empty($data)) 1891 $error = __('The required WPSiteSync extension is not active on the Target site.', 'wpsitesynccontent'); 1892 else 1893 $error = sprintf(__('The required WPSiteSync extension, %1$s, is not active on the Target site.', 'wpsitesynccontent'), $data); 1894 break; 1895 case self::ERROR_INVALID_POST_TYPE: $error = __('The post type is not allowed.', 'wpsitesynccontent'); break; 1896 case self::ERROR_REMOTE_REQUEST_FAILED: $error = __('Unable to make API request to Target system.', 'wpsitesynccontent'); break; 1836 1897 case self::ERROR_BAD_POST_RESPONSE: 1837 1898 if (NULL !== $data) … … 1840 1901 $error = __('Target system did not respond with success code.', 'wpsitesynccontent'); 1841 1902 break; 1842 case self::ERROR_MISSING_SITE_KEY: $error = __('Site Key for Target system has not been obtained.', 'wpsitesynccontent'); break;1843 case self::ERROR_POST_CONTENT_NOT_FOUND: $error = __('Unable to determine post content.', 'wpsitesynccontent'); break;1844 case self::ERROR_BAD_NONCE: $error = __('Unable to validate AJAX request.', 'wpsitesynccontent'); break;1903 case self::ERROR_MISSING_SITE_KEY: $error = __('Site Key for Target system has not been obtained.', 'wpsitesynccontent'); break; 1904 case self::ERROR_POST_CONTENT_NOT_FOUND: $error = __('Unable to determine post content.', 'wpsitesynccontent'); break; 1905 case self::ERROR_BAD_NONCE: $error = __('Unable to validate AJAX request.', 'wpsitesynccontent'); break; 1845 1906 case self::ERROR_UNRESOLVED_PARENT: 1846 if (NULL === $data)1847 $error = __('Content has a dependent Page that has not been Sync\'d. Please Push the Parent page.', 'wpsitesynccontent');1848 else {1849 $post_id = abs($data);1850 $post = get_post($post_id, OBJECT);1851 $error = sprintf(__('Content has a dependent Page that has not been Sync\'d. Please push the #%1$d "%2$s" Content first.', 'wpsitesynccontent'),1852 $post_id, (NULL !== $post ? $post->post_title : ''));1853 }1854 break;1855 case self::ERROR_NO_AUTH_TOKEN: $error = __('Unable to authenticate with Target site. Please re-enter credentials for this site.', 'wpsitesynccontent'); break;1856 case self::ERROR_NO_PERMISSION: $error = __('User does not have permission to perform Sync. Check configured user on Target.', 'wpsitesynccontent'); break;1857 case self::ERROR_INVALID_IMG_TYPE: $error = __('The image uploaded is not a valid image type.', 'wpsitesynccontent'); break;1858 case self::ERROR_POST_NOT_FOUND: $error = __('Requested post cannot be found on Target.', 'wpsitesynccontent'); break;1907 if (NULL === $data) 1908 $error = __('Content has a dependent Page that has not been Sync\'d. Please Push the Parent page.', 'wpsitesynccontent'); 1909 else { 1910 $post_id = abs($data); 1911 $post = get_post($post_id, OBJECT); 1912 $error = sprintf(__('Content has a dependent Page that has not been Sync\'d. Please push the #%1$d "%2$s" Content first.', 'wpsitesynccontent'), 1913 $post_id, (NULL !== $post ? $post->post_title : '')); 1914 } 1915 break; 1916 case self::ERROR_NO_AUTH_TOKEN: $error = __('Unable to authenticate with Target site. Please re-enter credentials for this site.', 'wpsitesynccontent'); break; 1917 case self::ERROR_NO_PERMISSION: $error = __('User does not have permission to perform Sync. Check configured user on Target.', 'wpsitesynccontent'); break; 1918 case self::ERROR_INVALID_IMG_TYPE: $error = __('The image uploaded is not a valid image type.', 'wpsitesynccontent'); break; 1919 case self::ERROR_POST_NOT_FOUND: $error = __('Requested post cannot be found on Target.', 'wpsitesynccontent'); break; 1859 1920 case self::ERROR_CONTENT_UPDATE_FAILED: 1860 1921 if (NULL === $data) … … 1863 1924 $error = sprintf(__('Content update on Target failed: %1$s.', 'wpsitesynccontent'), $data); 1864 1925 break; 1865 case self::ERROR_CANNOT_WRITE_TOKEN: $error = __('Cannot write authentication token.', 'wpsitesynccontent'); break; 1866 case self::ERROR_UPLOAD_NO_CONTENT: $error = __('Attachment upload failed. No content found; is there a broken link?', 'wpsitesynccontent'); break; 1867 case self::ERROR_PHP_ERROR_ON_TARGET: $error = __('A PHP error occurred on Target while processing your request. Examine log files for more information.', 'wpsitesynccontent'); break; 1868 case self::ERROR_SSL_PROTOCOL_ERROR: $error = __('Unknown SSL protocol error on Target. Check DNS settings on Target domain.', 'wpsitesynccontent'); break; 1869 case self::ERROR_WORDFENCE_BLOCKED: $error = __('Wordfence has blocked the Push operation. Try Learning Mode.', 'wpsitesyncontent'); break; 1870 case self::ERROR_MODSECURITY_BLOCKED: $error = __('Mod_Security has blocked the Push operation. Try adding Source site to white list.', 'wpsitesynccontent'); break; 1871 case self::ERROR_CLOUDFLARE_BLOCKED: $error = __('Cloudflare has blocked the Push operation. Try adding Source site to white list.', 'wpsitesynccontent'); break; 1872 case self::ERROR_CLOUDFRONT_BLOCKED: $error = __('CloudFront has blocked the Push operation. Try adding Source site to white list.', 'wpsitesynccontent'); break; 1873 case self::ERROR_INVALID_RESPONSE_TYPE: $error = sprintf(__('Target site responded with non-JSON content type: %1$s.', 'wpsitesynccontent'), $data); break; 1874 case self::ERROR_UNRECOGNIZED_OPERATION: $error = __('WPSiteSync operation is not recognized.', 'wpsitesynccontent'); break; 1875 1926 case self::ERROR_CANNOT_WRITE_TOKEN: $error = __('Cannot write authentication token.', 'wpsitesynccontent'); break; 1927 case self::ERROR_UPLOAD_NO_CONTENT: $error = __('Attachment upload failed. No content found; is there a broken link?', 'wpsitesynccontent'); break; 1928 case self::ERROR_PHP_ERROR_ON_TARGET: $error = __('A PHP error occurred on Target while processing your request. Examine log files for more information.', 'wpsitesynccontent'); break; 1929 case self::ERROR_SSL_PROTOCOL_ERROR: $error = __('Unknown SSL protocol error on Target. Check DNS settings on Target domain.', 'wpsitesynccontent'); break; 1930 case self::ERROR_WORDFENCE_BLOCKED: $error = __('Wordfence has blocked the Push operation. Try Learning Mode.', 'wpsitesyncontent'); break; 1931 case self::ERROR_MODSECURITY_BLOCKED: $error = __('Mod_Security has blocked the Push operation. Try adding Source site to white list.', 'wpsitesynccontent'); break; 1932 case self::ERROR_CLOUDFLARE_BLOCKED: $error = __('Cloudflare has blocked the Push operation. Try adding Source site to white list.', 'wpsitesynccontent'); break; 1933 case self::ERROR_CLOUDFRONT_BLOCKED: $error = __('CloudFront has blocked the Push operation. Try adding Source site to white list.', 'wpsitesynccontent'); break; 1934 case self::ERROR_INVALID_RESPONSE_TYPE: $error = sprintf(__('Target site responded with non-JSON content type: %1$s.', 'wpsitesynccontent'), $data); break; 1935 case self::ERROR_UNRECOGNIZED_OPERATION: $error = __('WPSiteSync operation is not recognized.', 'wpsitesynccontent'); break; 1936 case self::ERROR_MISSING_TOKEN: $error = __('Missing Authentication Token.', 'wpsitesynccontent'); break; 1937 case self::ERROR_MISSING_USER: $error = __('Athenticating user does not exist.', 'wpsitesynccontent'); break; 1938 case self::ERROR_GB_ATTACHMENT_ID: $error = sprintf(__('Attachment ID could not be found in block "%1$s".', 'wpsitesynccontent'), $data); break; 1939 case self::ERROR_GB_TAX_ID: $error = sprintf(__('Taxonomy ID could not be found in block "%1$s".', 'wpsitesynccontent'), $data); break; 1876 1940 default: 1877 1941 $error = apply_filters('spectrom_sync_error_code_to_text', sprintf(__('Unrecognized error: %d', 'wpsitesynccontent'), $code), $code, $data); … … 1892 1956 $notice = ''; 1893 1957 switch ($code) { 1894 case self::NOTICE_FILE_EXISTS: $notice = __('The file name already exists.', 'wpsitesynccontent'); break;1895 case self::NOTICE_CONTENT_SYNCD: $notice = __('Content Synchronized.', 'wpsitesynccontent'); break;1896 case self::NOTICE_INTERNAL_ERROR: $notice = __('Internal error:', 'wpsitesynccontent'); break;1958 case self::NOTICE_FILE_EXISTS: $notice = __('The file name already exists.', 'wpsitesynccontent'); break; 1959 case self::NOTICE_CONTENT_SYNCD: $notice = __('Content Synchronized.', 'wpsitesynccontent'); break; 1960 case self::NOTICE_INTERNAL_ERROR: $notice = __('Internal error:', 'wpsitesynccontent'); break; 1897 1961 default: 1898 1962 $notice = apply_filters('spectrom_sync_notice_code_to_text', … … 1929 1993 return $this->_response; 1930 1994 } 1995 1931 1996 } 1932 1997 -
wpsitesynccontent/trunk/classes/apiresponse.php
r2321211 r2491116 291 291 public function send($exit = TRUE) 292 292 { 293 // TODO: use wp_send_json()? 294 293 295 if ($this->nosend) 294 296 return; … … 320 322 if (NULL !== $this->request_id) 321 323 header(self::HEADER_SYNC_REQUEST_ID . ': ' . $this->request_id); // send this header so Sources get feedback with the ID the request was made with 322 else SyncDebug::log(__METHOD__.'():' . __LINE__ . ' no request id found'); 324 else SyncDebug::log(__METHOD__.'():' . __LINE__ . ' no request id found'); #!# 323 325 324 326 header('Content-Type: application/json'); -
wpsitesynccontent/trunk/classes/auth.php
r2325466 r2491116 21 21 //SyncDebug::log(__METHOD__.'()'); 22 22 $info = array(); 23 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' post data=' . var_export($_POST, TRUE));23 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' post data=' . SyncDebug::arr_sanitize($_POST)); 24 24 $username = $this->post('username', NULL); 25 25 $password = $this->post('password', NULL); 26 26 $token = $this->post('token', NULL); 27 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' user=' . $username . ' pass =' . strlen($password) . ' characters token=' . substr($token, -16));27 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' user=' . $username . ' password=' . strlen($password) . ' characters token=' . substr($token, -16)); 28 28 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' post= ' . SyncDebug::arr_sanitize($_POST)); 29 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' password=' . $password); 29 30 $source_model = new SyncSourcesModel(); 30 31 $api_controller = SyncApiController::get_instance(); … … 42 43 } else { 43 44 $info['user_login'] = $username; 44 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' - target: ' . get_bloginfo('wpurl')); 45 $info['user_password'] = $this->decode_password($password, get_bloginfo('wpurl')); 45 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' target: ' . get_bloginfo('wpurl')); 46 47 $target = get_bloginfo('wpurl'); 48 if (isset($_POST['host'])) 49 $target = $_POST['host']; 50 $info['user_password'] = $this->decode_password($password, $target); 46 51 $info['remember'] = FALSE; 47 52 … … 67 72 $error_code = 0; 68 73 69 // handle nonSyncAuthError instances #74 // handle nonSyncAuthError instances 70 75 $err_type = SyncAuthError::TYPE_VALIDATION_FAILED; 71 76 if (is_a($user_signon, 'SyncAuthError')) … … 80 85 default: 81 86 $error_code = SyncApiResponse::ERROR_BAD_CREDENTIALS; // default to generic error 82 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' unrecognized exception code ' . $ user_signon->type);87 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' unrecognized exception code ' . $err_type); 83 88 } 84 89 $resp->error_code($error_code, NULL); … … 209 214 } 210 215 216 $cleartext = wp_slash($cleartext); #277 211 217 //SyncDebug::log(' cleartext: ' . var_export($cleartext, TRUE)); 212 218 return $cleartext; -
wpsitesynccontent/trunk/classes/autherror.php
r2321211 r2491116 11 11 public function __construct($type) 12 12 { 13 new WP_Error($code, $message, $data);13 // new WP_Error($code, $message, $data); 14 14 $this->type = $type; 15 15 switch ($type) { 16 case SyncApiRequest::ERROR_BAD_CREDENTIALS: 17 $code = __('Token Validation Failed', 'wpsitesynccontent'); 16 default: 17 case self::TYPE_VALIDATION_FAILED: 18 $code = 'token_invalid'; 18 19 break; 19 case SyncApiRequest::ERROR_MISSING_TOKEN:20 $code = __('Token Missing', 'wpsitesynccontent');20 case self::TYPE_MISSING_TOKEN: 21 $code = 'token_missing'; 21 22 break; 22 case SyncApiRequest::ERROR_MISSING_USER:23 $code = __('User Invalid', 'wpsitesynccontent');23 case self::TYPE_INVALID_USER: 24 $code = 'invalid_user'; 24 25 break; 25 26 } 26 parent::__construct($code );27 parent::__construct($code, __('Authentication error', 'wpsitesynccontent')); 27 28 } 28 29 } -
wpsitesynccontent/trunk/classes/gutenbergentry.php
r2321211 r2491116 45 45 if (FALSE !== ($pos = strpos($prop, ':'))) { 46 46 switch (substr($prop, $pos)) { 47 default: 47 48 case ':i': $this->prop_type = self::PROPTYPE_IMAGE; break; 48 49 case ':l': $this->prop_type = self::PROPTYPE_LINK; break; -
wpsitesynccontent/trunk/classes/input.php
r1451657 r2491116 3 3 class SyncInput 4 4 { 5 const SANITIZE_EMAIL = 0x01; 6 const SANITIZE_FILENAME = 0x02; 7 const SANITIZE_HEXCOLOR = 0x04; 8 const SANITIZE_KEY = 0x08; 9 const SANITIZE_URL = 0x10; 10 5 11 /* 6 12 * Return the named element for a given get variable … … 9 15 * @return mixed The named get parameter if found, otherwise the $default value provided. 10 16 */ 11 public function get($name, $default = '' )17 public function get($name, $default = '', $sanitize = 0) 12 18 { 19 $ret = $default; 13 20 if (isset($_GET[$name])) 14 return sanitize_text_field($_GET[$name]); 15 return $default; 21 $ret = sanitize_text_field($_GET[$name]); 22 if (self::SANITIZE_EMAIL & $sanitize) 23 $ret = sanitize_email($ret); 24 if (self::SANITIZE_FILENAME & $sanitize) 25 $ret = sanitize_file_name($ret); 26 if (self::SANITIZE_HEXCOLOR & $sanitize) 27 $ret = sanitize_hex_color($ret); 28 if (self::SANITIZE_KEY & $sanitize) 29 $ret = sanitize_key($ret); 30 if (self::SANITIZE_URL & $sanitize) 31 $ret = esc_url_raw($ret); 32 return $ret; 33 # sanitize_html_class($class); 34 # sanitize_mime_type($mime_type); 35 # sanitize_user($username); 16 36 } 17 37 -
wpsitesynccontent/trunk/classes/licensesettings.php
r2247839 r2491116 136 136 { 137 137 // TODO: $values is always NULL. why? 138 SyncDebug::log(__METHOD__.'() values=' . var_export($values, TRUE));139 //SyncDebug::log(' - post=' . var_export($_POST, TRUE));138 SyncDebug::log(__METHOD__.'() values=' . SyncDebug::arr_sanitize($values)); 139 //SyncDebug::log(' - post=' . SyncDebug::arr_sanitize($_POST)); 140 140 $input = $this->post('spectrom_sync_settings', array()); 141 //SyncDebug::log(' - input=' . var_export($input, TRUE));141 //SyncDebug::log(' - input=' . SyncDebug::arr_sanitize($input)); 142 142 //SyncDebug::log(' method=' . $_SERVER['REQUEST_METHOD']); 143 143 -
wpsitesynccontent/trunk/classes/model.php
r2321211 r2491116 69 69 public function save_sync_data($data) 70 70 { 71 global $wpdb;72 73 71 // sanitize the `content_type` data 74 72 if (!isset($data['content_type'])) … … 77 75 $data['content_type'] = sanitize_key($data['content_type']); 78 76 79 if (!in_array($data['content_type'], array('comment', 'post', ' term', 'user')))77 if (!in_array($data['content_type'], array('comment', 'post', 'postmeta', 'term', 'user'))) 80 78 throw new Exception('The `content_type` passed to save_sync_data() is invalid'); 81 79 … … 96 94 $data['content_type']); 97 95 98 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' data=' . SyncDebug::arr_santize($data)); 96 global $wpdb; 97 98 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' data=' . SyncDebug::arr_sanitize($data)); 99 99 if (NULL !== $sync_data) { 100 100 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' read: ' . SyncDebug::arr_sanitize($sync_data)); … … 124 124 $type = sanitize_key($type); 125 125 if (defined('WP_DEBUG') && WP_DEBUG) { 126 if (!in_array($type, array('comment', 'post', ' term', 'user')))126 if (!in_array($type, array('comment', 'post', 'postmeta', 'term', 'user'))) 127 127 throw new Exception('The `type` passed to get_sync_data() is invalid'); 128 128 } … … 163 163 $type = sanitize_key($type); 164 164 if (defined('WP_DEBUG') && WP_DEBUG) { 165 if (!in_array($type, array('comment', 'post', ' term', 'user')))165 if (!in_array($type, array('comment', 'post', 'postmeta', 'term', 'user'))) 166 166 throw new Exception('The `type` passed to get_sync_target_data() is invalid'); 167 167 } … … 199 199 $type = sanitize_key($type); 200 200 if (defined('WP_DEBUG') && WP_DEBUG) { 201 if (!in_array($type, array('comment', 'post', ' term', 'user')))201 if (!in_array($type, array('comment', 'post', 'postmeta', 'term', 'user'))) 202 202 throw new Exception('The `type` passed to get_sync_target_post() is invalid'); 203 203 } … … 308 308 $push_data['origin'] = $url; 309 309 310 // also include the ‘site_key’value generated on initial installation.310 // also include the 'site_key' value generated on initial installation. 311 311 $push_data['site_key'] = SyncOptions::get('site_key'); 312 312 … … 335 335 private function _build_post_meta($post_id) 336 336 { 337 // any postmeta data associated with the current post ID that is prefixed with ‘_spectrom_sync_’is not to be collected337 // any postmeta data associated with the current post ID that is prefixed with '_spectrom_sync_' is not to be collected 338 338 $post_meta = get_post_meta($post_id); 339 339 if ($post_meta) { … … 348 348 foreach ($value as &$meta_entry) { 349 349 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' key=' . $key . ' value=' . $meta_entry); 350 if ($this-> _is_json($meta_entry))350 if ($this->is_json($meta_entry)) 351 351 $meta_entry = str_replace('\"', '~syncescquote~', $meta_entry); 352 352 $meta_entry = str_replace('\\u', '~syncuni~', $meta_entry); … … 365 365 * @return boolean TRUE if string contains JSON encoded data; FALSE if not a string or not containing JSON 366 366 */ 367 p rivate function _is_json($str)368 { 369 if (!is_string($str)) 367 public function is_json($str) 368 { 369 if (!is_string($str)) { 370 370 return FALSE; 371 } 371 372 if (!empty($str)) { 373 if (FALSE === stripos($str, '{') || 374 FALSE === stripos($str, '}')) 375 return FALSE; 376 377 $str = str_replace(array('~syncescquote~', '~syncuni~'), array('\\"', '\\u'), $str); 372 378 @json_decode($str); 373 379 return (JSON_ERROR_NONE === json_last_error()); -
wpsitesynccontent/trunk/classes/options.php
r2321211 r2491116 95 95 } 96 96 97 // override any settings with defines declared via wp-con tent#23997 // override any settings with defines declared via wp-config #239 98 98 if (defined('WPSITESYNC_TARGET')) 99 99 self::$_options['host'] = WPSITESYNC_TARGET; -
wpsitesynccontent/trunk/classes/settings.php
r2321211 r2491116 15 15 private $_tab = ''; 16 16 17 const SETTINGS_PAGE = 'sync'; // TODO: update name 17 // TODO: update name 18 const SETTINGS_PAGE = 'sync'; 18 19 19 20 private function __construct() … … 154 155 public function settings_api_init() 155 156 { 157 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' action=' . current_action()); 156 158 // check that current user is capable of performing operation 157 159 if (!current_user_can('manage_options') || !SyncOptions::has_cap()) … … 188 190 private function _init_general_settings() 189 191 { 190 //SyncDebug::log(__METHOD__.'() tab=' . $this->_tab);192 SyncDebug::log(__METHOD__.'() tab=' . $this->_tab . ' action=' . current_action()); 191 193 $data = $option_values = SyncOptions::get_all(); // $this->_options; 192 194 … … 320 322 case 'id': $desc = __('ID - Search for matching Content on Target by Post ID.', 'wpsitesynccontent'); 321 323 break; 322 case 'title': $desc = __('Post Title - Search for matching Content on Target by Post Title only.', 'wpsitesynccontent');323 default:324 break;325 324 case 'title-slug': $desc = __('Title, then Slug - Search for matching Content on Target by Post Title, then by Slug.', 'wpsitesynccontent'); 325 break; 326 case 'title': 327 default: $desc = __('Post Title - Search for matching Content on Target by Post Title only.', 'wpsitesynccontent'); 326 328 break; 327 329 } … … 350 352 $min_role = isset($data['min_role']) ? $data['min_role'] : 'author'; 351 353 switch ($min_role) { 354 default: 352 355 case 'author': $default_role = '|author|editor|administrator|'; 353 default:354 356 break; 355 357 case 'editor': $default_role = '|editor|administrator|'; … … 534 536 public function render_button_field($args) 535 537 { 536 echo '<button type="button" id="spectrom-button-', $args['name'], '" class="button-primary spectrom-ui-button">', $args['title'], '</button>'; 538 // $title = ''; 539 // if (isset($args['title'])) 540 // $title = ' title="' . esc_attr($args['title']) . '" '; 541 $click = ''; 542 if (isset($args['click'])) 543 $click = ' onclick="' . esc_attr($args['click']) . '" '; 544 545 echo '<button type="button" id="spectrom-button-', $args['name'], '" class="button-primary spectrom-ui-button"', 546 $click, '>', $args['title'], '</button>'; 547 537 548 if (!empty($args['message'])) 538 549 echo '<p>', $args['message'], '</p>'; … … 578 589 { 579 590 //SyncDebug::log(__METHOD__.'() tab=' . $this->_tab); 580 //SyncDebug::log(__METHOD__.'() values=' . var_export($values, TRUE));581 591 $settings = SyncOptions::get_all(); // $this->_options; 582 592 //SyncDebug::log(__METHOD__.'() settings: ' . var_export($settings, TRUE)); … … 609 619 610 620 foreach ($values as $key => $value) { 611 //SyncDebug::log(" key={$key} value=[" . var_export($value, TRUE) . ']');612 621 if (empty($values[$key]) && 'password' === $key) { 613 622 // ignore this so that passwords are not required on every settings update 623 unset($out['password']); 614 624 } else { 615 625 if ('password' !== $key && !is_array($value)) … … 623 633 if (empty($value)) { 624 634 // do nothing 635 $value = ''; 625 636 } else if (FALSE === $this->_is_valid_url($value)) { 626 637 add_settings_error('sync_options_group', 'invalid-url', __('Invalid URL.', 'wpsitesynccontent')); … … 635 646 // TODO: refactor so that 'host' and 'username' password checking is combined 636 647 // check to see if 'username' is changing and force use of password 637 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' change username: user="' . $value . '" password="' . $values['password'] . '"'); 638 if ('' === $value && '' === $values['password'] /* empty($value) && empty($values['password']) */ ) { 648 if ('' === $value && '' === $values['password']) { 639 649 // do nothing 650 $value = ''; 640 651 } else if ($value !== $settings['username'] && empty($values['password'])) { 641 652 add_settings_error('sync_username_password', 'missing-password', __('When changing Username, a password is required.', 'wpsitesynccontent')); … … 652 663 $out[$key] = '1' === $value ? '1' : '0'; 653 664 } else if ('roles' === $key) { 654 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' POST=' . var_export($_POST, TRUE));655 665 $roles = array(); 656 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' value=' . var_export($value, TRUE));657 666 foreach ($value as $role => $on) { 658 667 $roles[] = $role; … … 661 670 $roles[] = 'administrator'; // always force administrator access 662 671 $out[$key] = SyncOptions::ROLE_DELIMITER . implode(SyncOptions::ROLE_DELIMITER, $roles) . SyncOptions::ROLE_DELIMITER; 663 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' roles: ' . $out[$key]);664 672 } else if (0 === strlen(trim($value))) { 665 673 if (!$missing_error) { … … 679 687 } 680 688 } 681 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' output array: ' . var_export($out, TRUE));682 689 683 690 // authenticate if there was a password provided or the host/username are different … … 700 707 if (!empty($out['password']) || $re_auth) { 701 708 $out['auth'] = 0; 702 703 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' authenticating with data ' . var_export($out, TRUE));704 709 $api = new SyncApiRequest(); 705 $res = $api->api('auth', $out); 710 $auth = array( 711 'host' => $out['host'], 712 'username' => $out['username'], 713 'password' => $out['password'], 714 ); 715 SyncDebug::log(__METHOD__.'():' . __LINE__ . ' authenticating...'); 716 $res = $api->api('auth', $auth); 706 717 if (!is_wp_error($res)) { 707 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' response from auth request: ' . var_export($res, TRUE));708 718 if (isset($res->response->success) && $res->response->success) { 709 719 $out['auth'] = 1; 710 720 $out['target_site_key'] = $res->response->data->site_key; 711 //SyncDebug::log(__METHOD__.'() got token: ' . substr($res->response->data->token, -16));712 721 } else { 713 722 // authentication failure response from Target- report this to user 714 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' authentication response from Target');715 723 $msg = SyncApiRequest::error_code_to_string($res->error_code); 716 724 $msg .= ' <a href="https://wpsitesync.com/knowledgebase/wpsitesync-error-messages/#error' . $res->error_code . '" target="_blank" style="text-decoration:none"><span class="dashicons dashicons-info"></span></a>'; … … 737 745 738 746 $ret = apply_filters('spectrom_sync_validate_settings', $out, $values); 739 //SyncDebug::log(__METHOD__.'() validated settings: ' . SyncDebug::arr_sanitize($ret));740 747 return $ret; 741 748 } … … 834 841 $rate_text = sprintf(__('Thank you for using <a href="%1$s" target="_blank">WPSiteSync for Content</a>! Please <a href="%2$s" target="_blank">rate us</a> on <a href="%2$s" target="_blank">WordPress.org</a>', 'wpsitesynccontent'), 835 842 esc_url('https://wpsitesync.com'), 836 esc_url('https://wordpress.org/support/view/plugin-reviews/wpsitesynccontent?filter=5#postform')843 esc_url('https://wordpress.org/support/plugin/wpsitesynccontent/reviews?rate=5#new-post') 837 844 ); 838 845 -
wpsitesynccontent/trunk/classes/shortcodeentry.php
r2247839 r2491116 132 132 case 'p': $type = self::TYPE_POST_ID; break; // post 133 133 case 'pa': $type = self::TYPE_POST_ATTACH; break; // post attachments 134 case 'l': $type = self::TYPE_IMAGE_LIST; break; // list TODO: change to 'il' 134 // TODO: change to 'il' to be consistent with type/list format 135 case 'l': $type = self::TYPE_IMAGE_LIST; break; // list 135 136 case 'pl': $type = self::TYPE_POST_LIST; break; // post list 136 137 case 'a': $type = self::TYPE_ATTACHMENT; break; // attachment -
wpsitesynccontent/trunk/install/pluginupdater.php
r2247839 r2491116 87 87 return $_transient_data; 88 88 } 89 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' trans data=' . var_export($_transient_data, TRUE));89 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' trans data=' . SyncDebug::arr_sanitize($_transient_data)); 90 90 if ( ! empty( $_transient_data->response ) && ! empty( $_transient_data->response[ $this->name ] ) && false === $this->wp_override ) { 91 91 return $_transient_data; … … 107 107 $_transient_data->last_checked = time(); 108 108 $_transient_data->checked[ $this->name ] = $this->version; 109 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' checked:[' . $this->name . ']=' . var_export($_transient_data->checked[$this->name], TRUE));109 //SyncDebug::log(__METHOD__.'():' . __LINE__ . ' checked:[' . $this->name . ']=' . SyncDebug::arr_sanitize($_transient_data->checked[$this->name])); 110 110 } 111 111 return $_transient_data; -
wpsitesynccontent/trunk/readme.txt
r2340021 r2491116 5 5 Requires at least: 3.5 6 6 Requires PHP: 5.3.1 7 Tested up to: 5. 4.27 Tested up to: 5.7 8 8 Stable tag: trunk 9 9 License: GPLv2 or later … … 14 14 == Description == 15 15 16 WPSiteSync for Content helps Designers, Developers and Content Creators Synchronize SPECIFIC Content (i.e. Posts and Pages) between WordPress sites, AFTER a site has gone live. You can do this in Real-Time, with a simple CLICK of a button! 16 WPSiteSync for Content helps Designers, Developers and Content Creators Synchronize 17 SPECIFIC Content (i.e. Posts and Pages) between WordPress sites, AFTER a site has gone live. 18 You can do this in Real-Time, with a simple CLICK of a button! 17 19 18 20 A typical development workflow looks like this: … … 23 25 4. Deploy Site to LIVE host 24 26 25 The challenge presented once you've deployed is that any future content changes must either be done on the live site, or a full deployment is necessary if the changes were made locally or on a staging site. 26 27 WPSiteSync is unique in that it solves this problem by offering the ability to only deploy the CONTENT that has changed. This means ZERO down-time and ZERO data loss. It is the missing piece of the development puzzle, allowing for a proper workflow beyond the deployment and into its ongoing operation. 27 The challenge presented once you've deployed is that any future content changes must 28 either be done on the live site, or a full deployment is necessary if the changes were 29 made locally or on a staging site. 30 31 WPSiteSync is unique in that it solves this problem by offering the ability to only 32 deploy the CONTENT that has changed. This means ZERO down-time and ZERO data loss. 33 It is the missing piece of the development puzzle, allowing for a proper workflow 34 beyond the deployment and into its ongoing operation. 28 35 29 36 With WPSiteSync, the new workflow looks like this: … … 45 52 * You only need to synchronize specific content (not the complete database) 46 53 47 You can use <em>WPSiteSync for Content</em> to synchronize your Posts and Pages between any two WordPress sites, in any configuration. Some examples include: 54 You can use <em>WPSiteSync for Content</em> to synchronize your Posts and Pages between any two 55 WordPress sites, in any configuration. Some examples include: 48 56 49 57 * Local -> Staging … … 54 62 [youtube https://www.youtube.com/watch?v=KpeiTMbdj_Y] 55 63 56 ><strong>Support Details:</strong> We are happy to provide support and help troubleshoot issues. Visit our Support page at <a href="https://serverpress.com/contact/" target="_blank">https://serverpress.com/contact/</a>. Users should know however, that we check the WordPress.org support forums once a week on Wednesdays from 6pm to 8pm PST (UTC -8). 57 58 The <em>WPSiteSync for Content</em> plugin was specifically designed to simplify your workflow when creating content between development, staging and live servers. This tool removes the need to migrate an entire database, potentially overwriting new content on your live site such as comments, reviews, purchases, etc. Now you can update individual Pages or Posts as desired, leaving everything else intact. The best part is that it's a simple click of a button, reducing errors and saving you time. 59 60 WPSiteSync for Content is fully functional in any WordPress environment. We recommend using DesktopServer for your Local Development Environment, but it is not a requirement. 64 ><strong>Support Details:</strong> We are happy to provide support and help troubleshoot 65 issues. Visit our Support page at <a href="https://serverpress.com/contact/" target="_blank">https://serverpress.com/contact/</a>. 66 Users should know however, that we check the WordPress.org support forums once 67 a week on Wednesdays from 6pm to 8pm PST (UTC -8). 68 69 The <em>WPSiteSync for Content</em> plugin was specifically designed to simplify 70 your workflow when creating content between development, staging and live servers. 71 This tool removes the need to migrate an entire database, potentially overwriting 72 new content on your live site such as comments, reviews, purchases, etc. Now you 73 can update individual Pages or Posts as desired, leaving everything else intact. 74 The best part is that it's a simple click of a button, reducing errors and saving 75 you time. 76 77 WPSiteSync for Content is fully functional in any WordPress environment. We recommend 78 using DesktopServer for your Local Development Environment, but it is not a requirement. 61 79 62 80 <strong>This benefits your Development Workflow in more ways than one:</strong> … … 95 113 * FULL access to ALL future Premium Extensions 96 114 97 <strong>For more perks such as Early Access</strong> and <strong>Exclusive Preview</strong> of upcoming Features, please visit us at <a href="https://wpsitesync.com">WPSiteSync.com</a> and join our newsletter. 98 99 <strong>ServerPress, LLC is not responsible for any loss of data that may occur as a result of WPSiteSync for Content's use.</strong> Always backup your data before initial use of this product. Should you experience such an issue, we want to know about it right away. Please <a href="https://serverpress.com/contact/" target="_blank">contact our Support Team</a> and we'll do everything we can to get you up and running. 115 <strong>For more perks such as Early Access</strong> and <strong>Exclusive Preview</strong> 116 of upcoming Features, please visit us at <a href="https://wpsitesync.com">WPSiteSync.com</a> 117 and join our newsletter. 118 119 <strong>ServerPress, LLC is not responsible for any loss of data that may occur as a result 120 of WPSiteSync for Content's use.</strong> Always backup your data before initial use of this 121 product. Should you experience such an issue, we want to know about it right away. Please 122 <a href="https://serverpress.com/contact/" target="_blank">contact our Support Team</a> 123 and we'll do everything we can to get you up and running. 100 124 101 125 == Installation == … … 115 139 2. Activate the plugin through the 'Plugins' menu in WordPress. 116 140 117 You will need to Install and Activate the WPSiteSync for Content plugin on your development website (the Source) as well as the Target web site (where the Content is being moved to). 118 119 Once activated, you can use the Configuration page found at Settings -> WPSiteSync, on the Source website to set the URL of the Target site and enter the login credentials to use when sending data. This will allow the WPSiteSync for Content plugin to communicate with the Target website, authenticate, and then move the data between the websites. You do not need to Configure WPSiteSync for Content on the Target website as this will only be receiving Synchronization requests from the Source site. 141 You will need to Install and Activate the WPSiteSync for Content plugin on your 142 development website (the Source) as well as the Target web site (where the Content 143 is being moved to). 144 145 Once activated, you can use the Configuration page found at Settings -> WPSiteSync, 146 on the Source website to set the URL of the Target site and enter the login credentials 147 to use when sending data. This will allow the WPSiteSync for Content plugin to 148 communicate with the Target website, authenticate, and then move the data between 149 the websites. You do not need to Configure WPSiteSync for Content on the Target 150 website as this will only be receiving Synchronization requests from the Source 151 site. 120 152 121 153 == Frequently Asked Questions == … … 123 155 = Do I need to Install WPSiteSync for Content on both sites? = 124 156 125 Yes! The WPSiteSync for Content needs to be installed on the local or Staging server (the website you're moving the data from - the Source), as well as the Live server (the website you're moving the data to - the Target). 157 Yes! The WPSiteSync for Content needs to be installed on the local or Staging server 158 (the website you're moving the data from - the Source), as well as the Live server 159 (the website you're moving the data to - the Target). 126 160 127 161 = Does this plugin Synchronize all of my content (Pages and Posts) at once? = 128 162 129 No. WPSiteSync for Content will only synchronize the one Page or Post content that you are editing. And it will only Synchronize the content when you tell it to. This allows you to control exactly what content is moved between sites and when it will be moved. 163 No. WPSiteSync for Content will only synchronize the one Page or Post content that 164 you are editing. And it will only Synchronize the content when you tell it to. This 165 allows you to control exactly what content is moved between sites and when it will 166 be moved. 130 167 131 168 = Will this overwrite data while I am editing? = 132 169 133 No. WPSiteSync checks to see if the Content is being edited by someone else on the Target web site. If it is, it will not update the Content, allowing you to coordinate with the users on the Target web site. 134 135 In addition, each time Content is updated or Synchronized on the Target web site, a Post Revision is created (using the Post Revision settings). This allows you to recover Content to a previous version if needed. 170 No. WPSiteSync checks to see if the Content is being edited by someone else on 171 the Target web site. If it is, it will not update the Content, allowing you to 172 coordinate with the users on the Target web site. 173 174 In addition, each time Content is updated or Synchronized on the Target web site, 175 a Post Revision is created (using the Post Revision settings). This allows you to 176 recover Content to a previous version if needed. 136 177 137 178 = Does WPSiteSync only update Page and Posts Content? = 138 179 139 Yes. However, support for synchronizing Custom Post Types and Custom Taxonomies is available with our <a href="https://wpsitesync.com/downloads/wpsitesync-for-custom-post-types/" target="_blank">WPSiteSync for Custom Post Types</a> add-on. Additional plugins for User Attribution, Synchronizing Menus and Pulling content are available as well. 140 141 More complex data, such as WooCommerce products, Forms (like Gravity Forms or Ninja Forms), and other plugins that use custom database tables are supported by additional plugins that work with those products. 180 Yes. However, support for synchronizing Custom Post Types is available with our 181 <a href="https://wpsitesync.com/downloads/wpsitesync-for-custom-post-types/" target="_blank">WPSiteSync 182 for Custom Post Types</a> add-on. Additional plugins for User Attribution, Synchronizing 183 Menus and Pulling content are available as well. 184 185 More complex data, such as WooCommerce products, Forms (like Gravity Forms or Ninja 186 Forms), and other plugins that use custom database tables are supported by 187 additional plugins that work with those products. 142 188 143 189 = Is WPSiteSync Gutenberg Compatible? = 144 190 145 Yes! The free version of <em>WPSiteSync for Content</em> supports all Gutenberg blocks that are part of WordPress. There are many plugins that add more block types than those available in WordPress, however. Our add-on product, <a href="https://wpsitesync.com/downloads/wpsitesync-for-gutenberg-blocks/" target="_blank">WPSiteSync for Gutenberg Blocks</a> adds support for several of the more popular third party Gutenberg add-ons. 146 147 If your favorite Gutenberg Block plugin is not currently supported, let us know and we can add support for it. 191 Yes! The free version of <em>WPSiteSync for Content</em> supports all Gutenberg 192 blocks that are part of WordPress. There are many plugins that add more block types 193 than those available in WordPress, however. Our add-on product, <a href="https://wpsitesync.com/downloads/wpsitesync-for-gutenberg-blocks/" 194 target="_blank">WPSiteSync for Gutenberg Blocks</a> adds support for several of 195 the more popular third party Gutenberg add-ons. 196 197 If your favorite Gutenberg Block plugin is not currently supported, let us know 198 and we can add support for it. 148 199 149 200 == Screenshots == … … 153 204 154 205 == Changelog == 206 = 1.7 - Mar 9, 2021 = 207 * fix: Ensure JSON encoded metadata maintains escaped characters within strings. (Thanks Kris B.) 208 * fix: Mimic WordPress's "feature" of slash encoding special characters in password strings. (Thanks Brian S.) 209 * fix: Add braces to denote code block. (Thanks Miguel D.) 210 * fix: Improve handling of multiple postmeta entries with the same meta_key. (Thanks Miguel D.) 211 * enhancement: Allow WPSiteSync API operations to work when Members' "Enable Private Site" setting is turned on. (Thanks Christopher B.) 212 * enhancement: Allow connections to DesktopServer's Ngrok site. (Thanks Wrenford H.) 213 * enhancement: Add mechanism to inform Target site of any add-ons required to process data from Source. 214 * enhancement: Add before/after API request processing hooks. 215 * enhancement: Change declaration of parse_shortcodes() so it can be used by Elementor add-on. 216 * enhancement: Check for Gutenberg block property's existence before accessing to prevent invalid references. 217 * enhancement: Update compatibility with Coming Soon v6+ (Thanks Ned G.) 218 * enhancement: Improve error detection in processing AJAX requests (Thanks Ned G.) 219 * enhancement: Improve Block property detection and error recovery. 220 155 221 = 1.6.1 - Jun 16, 2020 = 156 222 * fix: Find child attachments to a post in cases where the post_content is empty. (Thanks Paul W.) -
wpsitesynccontent/trunk/wpsitesynccontent.php
r2325466 r2491116 6 6 Author: WPSiteSync 7 7 Author URI: https://wpsitesync.com 8 Version: 1. 6.18 Version: 1.7 9 9 Text Domain: wpsitesynccontent 10 10 Domain path: /language … … 26 26 class WPSiteSyncContent 27 27 { 28 const PLUGIN_VERSION = '1. 6.1';28 const PLUGIN_VERSION = '1.7'; 29 29 const PLUGIN_NAME = 'WPSiteSyncContent'; 30 30 … … 226 226 227 227 // send usage information 228 if ( '1' === SyncOptions::get('report', '0'))228 if (self::$report || '1' === SyncOptions::get('report', '0')) 229 229 new SyncUsage(); 230 230
Note: See TracChangeset
for help on using the changeset viewer.