Skip to content

Instantly share code, notes, and snippets.

@norcross
Last active November 6, 2017 01:28
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save norcross/35fc4e7fe17a296b959a to your computer and use it in GitHub Desktop.
Save norcross/35fc4e7fe17a296b959a to your computer and use it in GitHub Desktop.
cross post (syndicate) posts on a WP multisite install
<?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