Last active
November 6, 2017 01:28
-
-
Save norcross/35fc4e7fe17a296b959a to your computer and use it in GitHub Desktop.
cross post (syndicate) posts on a WP multisite install
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* | |
Plugin Name: Multisite Cross Post | |
Description: Cross post content between a WP multisite installation | |
Author: Andrew Norcross | |
Version: 0.1 | |
Author URI: http://andrewnorcross.com | |
*/ | |
class RKV_Multi_CrossPost { | |
public static $instance; | |
/** | |
* This is our constructor. There are many like it, but this one is mine. | |
*/ | |
public function __construct() { | |
self::$instance = $this; | |
$this->init(); | |
} | |
/** | |
* our main init call, to pull in our functions | |
* will bail if we aren't on multisite, because | |
* clearly you didnt read the description | |
* of this code. | |
* | |
* @return void | |
*/ | |
public function init() { | |
// not multisite? you get nothing! You lose! Good day, sir! | |
if ( ! is_multisite() ) { | |
return; | |
} | |
// remove the default canonical | |
remove_action( 'wp_head', 'rel_canonical' ); | |
// load the metabox, our save post trigger, transitions, and add back our canonical | |
add_action( 'add_meta_boxes', array( $this, 'add_meta_box' ) ); | |
add_action( 'save_post', array( $this, 'save_post' ) ); | |
add_action( 'transition_post_status', array( $this, 'syndicate_posts' ), 10, 3 ); | |
add_action( 'wp_head', array( $this, 'rel_canonical' ) ); | |
} | |
/** | |
* our metabox to load on the sidebar of a post | |
* will only load if the current user has other sites | |
* available to them | |
* | |
* @return void | |
*/ | |
public function add_meta_box() { | |
// do our blog count check | |
if ( count( get_blogs_of_user( get_current_user_id() ) ) < 2 ) { | |
return; | |
} | |
// load said box | |
add_meta_box( 'rkv-post-syndication', 'Post Syndication', array( $this, 'render_meta_box' ), 'post', 'side', 'default' ); | |
} | |
/** | |
* render our metabox | |
* @param [type] $post [description] | |
* @return [type] [description] | |
*/ | |
public function render_meta_box( $post ) { | |
// fetch any sites already selected | |
$syndicated = get_post_meta( $post->ID, '_rkv_sites_syndicated', true ); | |
// either provide our array of saved sites or an empty | |
$syndicated = is_array( $syndicated ) ? $syndicated : array(); | |
// get all available sites to the user | |
$sites = get_blogs_of_user( get_current_user_id() ); | |
// create a nonce | |
wp_nonce_field( 'rkv_syndicate_sites', 'rkv_syndicate_sites', false, true ); | |
// loop through each site | |
foreach ( $sites as $site ) { | |
// exclude the current site | |
if ( get_current_blog_id() == $site->userblog_id ) { | |
continue; | |
} | |
// set our site ID and name as a variable | |
$site_id = absint( $site->userblog_id ); | |
$site_name = esc_html( $site->blogname ); | |
// do the 'checked' thing since the default WP function doesn't handle arrays well | |
$checked = in_array( $site_id, $syndicated ) ? 'checked="checked"' : ''; | |
// create our checkboxes | |
echo '<input type="checkbox" name="rkv-sites[]" id="rkv-sites-' . absint( $site_id ) . '" value="' . absint( $site_id ) . '" '. $checked . '/>'; | |
echo '<label for="rkv-sites-' . absint( $site_id ) . '">' . esc_attr( $site_name ) . '</label>'; | |
echo '<br/>'; | |
} // end loop of sites | |
} | |
/** | |
* our save post function | |
* @param [type] $post_id [description] | |
* @return [type] [description] | |
*/ | |
public function save_post( $post_id ) { | |
// remove the default action first to avoid infinite loops | |
// read more here: http://codex.wordpress.org/Plugin_API/Action_Reference/save_post#Avoiding_infinite_loops | |
remove_action( 'save_post', array( $this, 'save_post' ) ); | |
// make sure we aren't using autosave | |
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { | |
return $post_id; | |
} | |
// bail without a nonce or if nonce fails | |
if ( empty( $_POST[ 'rkv_syndicate_sites' ] ) || ! wp_verify_nonce( $_POST[ 'rkv_syndicate_sites' ], 'rkv_syndicate_sites' ) ) { | |
return; | |
} | |
// check user cap | |
if ( ! current_user_can( 'edit_post', $post_id ) ) { | |
return; | |
} | |
// delete any meta and bail if no sites were passed | |
if ( ! isset( $_POST[ 'rkv-sites' ] ) || empty( $_POST[ 'rkv-sites' ] ) ) { | |
delete_post_meta( $post_id, '_rkv_sites_syndicated' ); | |
return; | |
} | |
// set sites as a variable | |
$sites = $_POST[ 'rkv-sites' ]; | |
// create a fancy array | |
foreach ( $sites as $key => $site ) { | |
$sites[$key] = absint( $site ); | |
} | |
// update our post meta | |
update_post_meta( $post_id, '_rkv_sites_syndicated', $sites ); | |
// get our post object | |
$p = get_post( $post_id ); | |
// run our syndication function | |
$this->syndicate_posts( 'publish', 'publish', $p ); | |
} | |
/** | |
* our actual syndication setup which piggybacks on | |
* the post transition | |
* @param [type] $new_status [description] | |
* @param [type] $old_status [description] | |
* @param [type] $post [description] | |
* @return [type] [description] | |
*/ | |
public function syndicate_posts( $new_status, $old_status, $post ) { | |
// remove the default action | |
remove_action( 'transition_post_status', array( $this, 'syndicate_posts' ), 10, 3 ); | |
// bail if we aren't publishing | |
if ( $new_status != 'publish' ) { | |
return; | |
} | |
// bail if we have a canonical | |
if ( get_post_meta( $post->ID, '_rkv_canonical', true ) ) { | |
return; | |
} | |
// fetch data | |
$canonical = get_permalink( absint( $post->ID ) ); | |
$sites = get_post_meta( absint( $post->ID ), '_rkv_sites_syndicated', true ); | |
$posts = get_post_meta( absint( $post->ID ), '_rkv_posts_syndicated', true ); | |
$posts = $posts ? $posts : array(); | |
// set some default values | |
$thumb_url = false; | |
$gallery = false; | |
$cats = array(); | |
$tags = array(); | |
// check for thumbnail | |
if ( has_post_thumbnail( absint( $post->ID ) ) ) { | |
$thumb_id = get_post_thumbnail_id( $post->ID ); | |
$thumb_src = wp_get_attachment_image_src( absint( $thumb_id ), 'thumbnail', false ); | |
$thumb_url = ! empty( $thumb_src[0] ) ? esc_url( $thumb_src[0] ) : ''; | |
} | |
// check for slideshow | |
$gallery_check = get_post_gallery( absint( $post->ID ), false ); | |
// we have one. proceed. | |
if ( $gallery_check ) { | |
$gallery_ids = explode( ',', $gallery_check['ids'] ); | |
// loop the gallery | |
foreach ( $gallery_ids as $gallery_id ) { | |
// fetch the source of each | |
$gallery_src = wp_get_attachment_image_src( absint( $gallery_id ), 'full', false ); | |
// parse out our URL check | |
$gallery_url = ! empty( $gallery_src[0] ) ? esc_url( $gallery_src[0] ) : ''; | |
// set an array | |
$gallery_data = array( | |
'id' => absint( $gallery_id ), | |
'url' => $gallery_url | |
); | |
// merge into a single array item | |
$gallery[] = $gallery_data; | |
} | |
} | |
// check for categories | |
$categories = wp_get_post_categories( $post->ID ); | |
if ( $categories ) { | |
foreach ( $categories as $c ) { | |
$cats[] = get_category( $c )->name; | |
} | |
} | |
// check for post tags | |
$post_tags = wp_get_post_tags( $post->ID ); | |
if ( $post_tags ) { | |
foreach ( $post_tags as $t ) { | |
$tags[] = get_tag( $t )->name; | |
} | |
} | |
// loop the sites and start posting | |
foreach( $sites as $site ) { | |
// switch | |
switch_to_blog( $site ); | |
$p = (array) $post; | |
if ( ! empty( $posts[ $site ] ) ) { | |
$p['ID'] = $posts[ $site ]; | |
} else { | |
unset( $p['ID'] ); | |
} | |
$posts[ $site ] = wp_insert_post( $p ); | |
// build our featured image on the new site | |
if ( $thumb && ! get_the_post_thumbnail() ) { | |
set_post_thumbnail( $posts[ $site ], $this->_sideload_image( $thumb, $posts[ $site ] ) ); | |
} | |
$exist = get_post_meta( $posts[ $site ], '_rkv_gallery_move', true ); | |
if ( $gallery && ! $exist ) { | |
// build our image gallery on the new site | |
foreach ( $gallery as $gallery_item ) { | |
$old_ids[] = $gallery_item['id']; | |
$new_ids[] = $this->_sideload_image( $gallery_item['url'], $posts[ $site ] ); | |
} | |
// set flag to avoid image copy duplication | |
update_post_meta( $posts[ $site ], '_rkv_gallery_move', true ); | |
// store gallery IDs to update content later | |
update_post_meta( $posts[ $site ], '_rkv_galleryids_old', $old_ids ); | |
update_post_meta( $posts[ $site ], '_rkv_galleryids_new', $new_ids ); | |
} | |
// run gallery ID swap function | |
if ( $gallery ) { | |
$this->_gallery_swap( $posts[ $site ] ); | |
} | |
// set categories if present | |
if ( ! empty( $cats ) ) { | |
wp_set_object_terms( $posts[ $site ], $cats, 'category' ); | |
} | |
// set post tags if present | |
if ( ! empty( $tags ) ) { | |
wp_set_object_terms( $posts[ $site ], $tags, 'post_tag' ); | |
} | |
// update our canonical with the originating site URL | |
update_post_meta( $posts[ $site ], '_rkv_canonical', $canonical ); | |
// jump back to the current site | |
restore_current_blog(); | |
} | |
// set our meta of syndicated items | |
update_post_meta( $post->ID, '_rkv_posts_syndicated', $posts ); | |
} | |
/** | |
* update our gallery image IDs to match them on the new site | |
* otherwise it'll use the original IDs. which is wrong. | |
* | |
* @param [type] $post_id [description] | |
* @return [type] [description] | |
*/ | |
private function _gallery_swap( $post_id ) { | |
// pull our meta | |
$old_ids = get_post_meta( $post_id, '_rkv_galleryids_old', true ); | |
$new_ids = get_post_meta( $post_id, '_rkv_galleryids_new', true ); | |
// fetch our content | |
$content = get_post_field( 'post_content', $post_id, 'raw' ); | |
// do a basic string replace. | |
$newgallery = str_replace( $old_ids, $new_ids, $content ); | |
// set an array of the post ID and updated content | |
$updated = array( | |
'ID' => $post_id, | |
'post_content' => $newgallery | |
); | |
// update said post | |
wp_update_post( $updated ); | |
// and be on your way | |
return; | |
} | |
/** | |
* our canonical thing. SEOs love this shit | |
* @return [type] [description] | |
*/ | |
public function rel_canonical() { | |
if ( ! is_singular() ) { | |
return; | |
} | |
if ( is_main_site() ) { | |
rel_canonical(); | |
} else { | |
echo '<link rel="canonical" href="' . get_post_meta( get_the_ID(), '_rkv_canonical', true ) . "\" />\n"; | |
} | |
} | |
/** | |
* this is kind of a bastard, but its the only real way | |
* to do it properly. basically this mimics the upload | |
* function in WP core that a normal user would do | |
* but does it on the fly. like Shaft. | |
* | |
* @param [type] $file [description] | |
* @param [type] $post_id [description] | |
* @param [type] $desc [description] | |
* @param array $post_data [description] | |
* @return [type] [description] | |
*/ | |
private function _sideload_image( $file, $post_id, $desc = null, $post_data = array() ) { | |
// bad file. GO HOME | |
if ( ! filter_var( $file, FILTER_VALIDATE_URL ) ) { | |
return new WP_Error( 'invalid_url', 'URL is not valid' ); | |
} | |
// we have a file | |
if ( ! empty($file) ) { | |
// Download file to temp location | |
$tmp = download_url( $file ); | |
// Set variables for storage | |
// fix file filename for query strings | |
preg_match( '/[^\?]+\.(jpeg|jpg|gif|png)\b/i', $file, $matches ); | |
// no matches. bail. | |
if ( empty( $matches[0] ) ) { | |
return false; | |
} | |
// set up our file array | |
$file_array['name'] = basename( $matches[0] ); | |
$file_array['tmp_name'] = $tmp; | |
// If error storing temporarily, unlink | |
if ( is_wp_error( $tmp ) ) { | |
@unlink( $file_array['tmp_name'] ); | |
$file_array['tmp_name'] = ''; | |
} | |
// do the validation and storage stuff | |
$id = media_handle_sideload( $file_array, $post_id, $desc, $post_data ); | |
// If error storing permanently, unlink | |
if ( is_wp_error( $id ) ) { | |
@unlink( $file_array['tmp_name'] ); | |
return $id; | |
} | |
// send back the ID | |
return $id; | |
} | |
// But I have an honorable compromise. Just walk away. | |
return false; | |
} | |
} | |
new RKV_Multi_CrossPost; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment