It’s been too long since I blogged some WordPress code. That sounds like some sort of confessional. “Forgive my Ma.tt, for I have sinned, it’s been 34 days since my last WordPress post…”

And this is something I did maybe back in January, something Mariana Funes has asked for a while for her to use on her Daily Stillness site.

Because she has had long stints managing the DS106 Daily Create she is aware of the times when you might be tapped of ideas, and thought it would be useful to have a capability to copy a previous daily and issue it as a new one (there was a larger idea of having something to have like 30 of them continually loop, but that seemed more than hairy to me).

It’s not overly complex- a Daily _____ in the theme I made for this is am ordinary post, and I knew programmatically how to create posts; extra stuff needs to go with it- tags, categories, post_meta.

There are a few tiny wrinkles- the Daily Blank inserts the tag in the title, so on a copy where as recycled it gets a new tag, that would need to be replaced. And the bottom portion of each post has links and tags built into the prompt:

The extra "stuff" the theme adds as instructions for each Daily _______
The extra “stuff” the theme adds as instructions for each Daily _______

And an idea I concocted in the middle- if a Daily _____ is recycled, you’d like something that provides links back to its “parent” one, in fact there might be a chain of them if it is recycled again.

So I would need some code that an administration URL could be called, pass it the ID of an existing post, and the script creates a new one based on copying its info.

I found as a good starting point some code from Misha Rudrastyh on How to Duplicate Posts and Pages Without Plugins — most of my code had to be done differently on the post creation part, but I liked the approach of adding a “recycle” link in the Dashboard, that comes up when you hover over a published post / Daily Blank

The Daily ____ comes with built in recycling
The Daily ____ comes with built in recycling

The way into this is coding a thing that I can write my own action/handler for a WordPress Dashboard URL like most of the other menu items you see there- it’s the admin_post action.

I’m defining my own admin action called dailyblank_recycle — this always seems convoluted the first 3 or 15 times you try it. But I am adding an action to the admin interface, that when it happens, will snap in to action the function prefix_admin_dailyblank_recycle (and these functionas all have to be named like this).

// set up admin action 
add_action( 'admin_post_dailyblank_recycle', 'prefix_admin_dailyblank_recycle' );

The function has a lot in it. We first make sure that the right hook dailyblank_recycle has been called and that the calling URL is sending an ID for the post you want to copy.

One of the tricks is removing from the old post the first “word” in the title (the theme prepends the hashtag to the title). Huh? If I want to clone today’s DS106 Daily Create, it’s title is “#tdc1551 Sketch negative space leaves” so when I copy it I need to whop off that “#tdc1551 ” from the front (when the new post is saved, its brand new tag is pre-pended).

// remove first word of title, which has the old hashtag in it
$new_title = substr( strstr( $post->post_title," " ), 1 );

Likewise, as mentioned above I need to strip off the instructions at the bottom.

The trick (which I found somewhere in StackExchange I am sure) is a double string function that more or less finds the first blank in the title, and makes a new string from there to the end of the original. Luckily I apply a special CSS class to this bit, so what I need to do is remove from the original post everything after/including

<p class="tweet-deets>
.

Some savvy PHP code could probably do this in one line, it took me three. Plus comments.

// make a copy of the original post content
$old_content = $post->post_content;

// find the char position of the tweet instructions in original daily
$end_of_content = strrpos( $old_content,  '<p class="tweet-deets' );

// get all content up to the tweet instructions
$new_content = strstr ( $old_content, '<p class="tweet-deets', true );

And there is some bits in the full code belong where I add a post_meta field to provide the ID for the post the new one is recycle from– and knowing it could be done multiple times, it can be a comma separated list.

The clever part I got from Misha’s code was that after the new post was created, the browser is redirected to opening it in editing mode.

So here is the full function:

function prefix_admin_dailyblank_recycle() {
// looks for a post= value in URL and makes a copy of Daily Blank with that ID as a new one

	// make sure we got something to work with
	if (! ( isset( $_GET['post']) || isset( $_POST['post'])  || ( isset($_REQUEST['action']) && 'dailyblank_recyle' == $_REQUEST['action'] ) ) ) {
		wp_die('Aaack. Missing id for Daily to copy.');
	}
 
	// get the original post id
	$post_id = (isset($_GET['post']) ? $_GET['post'] : $_POST['post']);

	// and get all the original post data
	$post = get_post( $post_id );
	
 	// use same author
	$new_post_author = $post->post_author;
 
	// if post data exists, create the new duplicate
	if (isset( $post ) && $post != null) {
		
		// remove first word of title, which has the old hashtag in it
		$new_title = substr( strstr( $post->post_title," " ), 1 );
		
		$old_content = $post->post_content;
		
		// find the char position of the tweet instructions in original daily
		$end_of_content = strrpos( $old_content,  '<p class="tweet-deets' );
	
		// get all content up to the tweet instructions
		$new_content = strstr ( $old_content, '<p class="tweet-deets', true );
	
		// new post data array
		$args = array(
			'post_author'    => $new_post_author,
			'post_content'   => $new_content,
			'post_status'    => 'draft',
			'post_title'     => $new_title,
			'post_type'      => $post->post_type,
		);
 
		// make us a post!
		$new_post_id = wp_insert_post( $args );
 
		// get the existing categories an stuff 'em in an array
		$categories = get_the_category( $post_id );
		
		foreach ($categories as $cat) {
			$newcats[] = $cat->term_id;
		}
		
		// assign categories to the new post
		wp_set_post_categories( $new_post_id, $newcats );
		
		// see if there is an author in the post meta
		$wAuthor =  get_post_meta($post_id, 'wAuthor', 1 );
		
		// got one? add to new post
		if ( $wAuthor  ) add_post_meta( $new_post_id, 'wAuthor', $wAuthor );
		
		// see if there is a recycled list already
		$oldRecycled = get_post_meta($post_id, 'wRecycled', 1 );
		
		// if its been recycled before, append the existing post if, otherwise, just add it
		$wRecycled = ( $oldRecycled  ) ? $oldRecycled . ',' . $post_id : $post_id;
		
		// add post meta for recycled list
		add_post_meta( $new_post_id, 'wRecycled', $wRecycled );
		
		// let's do some editing! do a redirect, send a flag for the admin notices
		wp_redirect( admin_url( 'post.php?action=edit&post=' . $new_post_id . '&recycled=1' ) );
		exit;
		
	} else {
		wp_die('Daily Duplication failed, could not find original daily: ' . $post_id);
	}
}

For fun, I added one of those colored notices you see that let you know something is done, so when the redirection happens to show the editing of the recycled post, a notice is added to the top of the page:

// Action to spawn an admin notice
add_action( 'admin_notices', 'dailyblank_recycled_admin_notice' );

// displays a notice for recycled new posts
function dailyblank_recycled_admin_notice() {
	if ( $_GET['recycled'] == 1 ) {
		echo
    	' <div class="updated notice is-dismissible"><p>This is a newly recycled daily blank. Edit as if it were new!</p></div>';
    }
}

Now this has everything in place so an admin URL like …./wp-admin/admin-post.php?action=dailyblank_recycle&post=125 will run my function to create a new post copied from the one with ID=125.

The nifty party is adding it as one of those hover link functions on the view of posts, the hook is on post_row_actions — and is only showed on posts that have been published.

// Add a recycling link for hovers on posts that ar epublished
add_filter( 'post_row_actions', 'dailyblank_duplicate_post_link', 10, 2 );

function dailyblank_duplicate_post_link( $actions, $post ) {
	if ( current_user_can('edit_posts') and  $post->post_status == 'publish') {
		$actions['recycle'] = '<a href="admin-post.php?action=dailyblank_recycle&post=' . $post->ID . '" title="Recycle this Daily" rel="permalink">Recycle</a>';
	}
	return $actions;
}

And for extra functionality, I wanted a way to have an admin menu to trigger this code when viewing a single post. I put it on the top admin menu bar, since I already have a menu there that links to the theme’s option page:

recycle-menu

It calls the recycle code the same way as the post hover links above. Here is how it works in the theme:

add_action( 'admin_bar_menu', 'dailyblank_recycle_adminbar', 999 );

function dailyblank_recycle_adminbar( $wp_admin_bar ) {

	global $post;
	
	if ( is_single () and $post->post_status == 'publish' ) {
		
		$args = array(
			'id'    => 'recycle',
			'title' => 'Recycle This Daily Blank',
			'href'  => site_url() . '/wp-admin/admin-post.php?action=dailyblank_recycle&post=' . $post->ID,
			'parent' => 'edit',
			'meta' => array( 'class' => ''),

		);
		$wp_admin_bar->add_node( $args );
	}
}

And the last piece (I think!) is adding to the single.php template some code to display names and links to any previous Daily _______s that a newer one has been recycled from. The code….

< ?php
	// try loading any recycled IDs
	$wRecycled = get_post_meta( $post->ID, 'wRecycled', 1 ); 
	
	// if we have recycled is, lets set them up for sitting out
	if ( ! empty ( $wRecycled ) ) {
		echo '<h4 class="text-center">This Daily ' . dailyblank_option('dailykind') . ' has been recycled from previously published ones:</h4></p><p class="text-center">';
		
		// pop the into an array
		$recycle_ids = explode(',', $wRecycled );
		
		// walk the ids after sorting them
		sort (  $recycle_ids ) ;
		foreach ( $recycle_ids as $rid ) {
			echo '&bull; <a href="' . get_permalink( $rid ) . '">' . get_the_title( $rid ) . '</a> (' . get_the_date( 'M j, Y', $rid ) . ")\n";
		}
		echo '</p>';
	}
?>

Here is what it will produce, in this case, the Daily Stillness TDS268 was recycled from TDS25

recycled

This works well so far, though I keep forgetting to use it on the DS106 Daily Create.

I was thinking at first the this was kind of like “forking” that Mikel Caulfield is doing, but his Wikity project is forking from one site to another; I’m just duplicating a post and updating all its info to represent a new post’s information.

But it works! That’s what counts.


Top / Featured Image credits: I could have looked elsewhere, but just went for a search on my own flickr photos. This one is relatively recent, it is one of the recycle bins at the Mesa Public Library. Because of limited recycling options where I live, once a year I drive a truckload of glass down here.

This is my own flickr photo https://flickr.com/photos/cogdog/19888623330 shared under a Creative Commons (BY) license

The post "WordPress Recycling of Daily Creates (not quite forking)" was originally slapped on the butt by a cigar smoking doctor yelling "It's a post!" at CogDogBlog (http://cogdogblog.com/2016/04/wordpress-recycling/) on April 8, 2016.

2 Comments

  • Pat

    You might want to look into the filter “the_content” to append stuff to a post (especially if auto generated)

    • Alan Levine aka CogDog cogdogblog.com

      Yeah thanks, Pat, that’s a better route, plus it let’s me alter the output if I need to reword/reformat in the future. For existing content, I’ll have to look at a string test to see if it’s already in the body (for previous posts) or maybe write a helper function that strips it out of older posts.

      Respectfully,
      Grasshopper

Leave a Comment

All fields are required. Your email address will not be published.