search ]

Duplicate WordPress Posts & Pages Without a Plugin

The action of duplicating posts can be very useful when working with many similar posts. For example – products in a WooCommerce store. It can be said that the option to duplicate posts is especially useful when your posts have custom fields of various kinds but different titles and content. This option will save you the need to re-enter the content into the meta fields, categories, and tags each time you create a page or post.

While wanderings, I came across a guide explaining how to duplicate pages and posts without a plugin in the WordPress Dashboard.

Excuse me for not linking to the original post, I do not have the link for it.

You can use such a plugin or another for this functionality, but why use a plugin when you can add the option with simple copy/paste? Here is an example of the result we will get:

Duplicate Posts & Pages Without a Plugin

When you click on duplicate, the post will be duplicated. It will not be published of course but will be duplicated as a draft and an automatic redirection to the editing page of the duplicated post in the WordPress Dashboard will occur.

How to duplicate pages and posts without a plugin?

Let’s not waste words, here is the code that needs to be added to the functions.php file:

/*
 * Function creates post duplicate as a draft and redirects then to the edit post screen
 */
function sv_duplicate_post_as_draft() {
	global $wpdb;
	if ( ! ( isset( $_GET['post'] ) || isset( $_POST['post'] ) || ( isset( $_REQUEST['action'] ) && 'sv_duplicate_post_as_draft' == $_REQUEST['action'] ) ) ) {
		wp_die( 'No post to duplicate has been supplied!' );
	}

	/*
	 * Nonce verification
	 */
	if ( ! isset( $_GET['duplicate_nonce'] ) || ! wp_verify_nonce( $_GET['duplicate_nonce'], basename( __FILE__ ) ) ) {
		return;
	}

	/*
	 * get the original post id
	 */
	$post_id = ( isset( $_GET['post'] ) ? absint( $_GET['post'] ) : absint( $_POST['post'] ) );
	/*
	 * and all the original post data then
	 */
	$post = get_post( $post_id );

	/*
	 * if you don't want current user to be the new post author,
	 * then change next couple of lines to this: $new_post_author = $post->post_author;
	 */
	$current_user    = wp_get_current_user();
	$new_post_author = $current_user->ID;

	/*
	 * if post data exists, create the post duplicate
	 */
	if ( isset( $post ) && $post != null ) {

		/*
		 * new post data array
		 */
		$args = array(
			'comment_status' => $post->comment_status,
			'ping_status'    => $post->ping_status,
			'post_author'    => $new_post_author,
			'post_content'   => $post->post_content,
			'post_excerpt'   => $post->post_excerpt,
			'post_name'      => $post->post_name,
			'post_parent'    => $post->post_parent,
			'post_password'  => $post->post_password,
			'post_status'    => 'draft',
			'post_title'     => $post->post_title,
			'post_type'      => $post->post_type,
			'to_ping'        => $post->to_ping,
			'menu_order'     => $post->menu_order
		);

		/*
		 * insert the post by wp_insert_post() function
		 */
		$new_post_id = wp_insert_post( $args );

		/*
		 * get all current post terms ad set them to the new post draft
		 */
		$taxonomies = get_object_taxonomies( $post->post_type ); // returns array of taxonomy names for post type, ex array("category", "post_tag");
		foreach ( $taxonomies as $taxonomy ) {
			$post_terms = wp_get_object_terms( $post_id, $taxonomy, array( 'fields' => 'slugs' ) );
			wp_set_object_terms( $new_post_id, $post_terms, $taxonomy, false );
		}

		/*
		 * duplicate all post meta just in two SQL queries
		 */
		$post_meta_infos = $wpdb->get_results( "SELECT meta_key, meta_value FROM $wpdb->postmeta WHERE post_id=$post_id" );
		if ( count( $post_meta_infos ) != 0 ) {
			$sql_query = "INSERT INTO $wpdb->postmeta (post_id, meta_key, meta_value) ";
			foreach ( $post_meta_infos as $meta_info ) {
				$meta_key = $meta_info->meta_key;
				if ( $meta_key == '_wp_old_slug' ) {
					continue;
				}
				$meta_value      = addslashes( $meta_info->meta_value );
				$sql_query_sel[] = "SELECT $new_post_id, '$meta_key', '$meta_value'";
			}
			$sql_query .= implode( " UNION ALL ", $sql_query_sel );
			$wpdb->query( $sql_query );
		}


		/*
		 * finally, redirect to the edit post screen for the new draft
		 */
		wp_redirect( admin_url( 'post.php?action=edit&post=' . $new_post_id ) );
		exit;
	} else {
		wp_die( 'Post creation failed, could not find original post: ' . $post_id );
	}
}

add_action( 'admin_action_sv_duplicate_post_as_draft', 'sv_duplicate_post_as_draft' );

Next, add the following code also to the functions.php file:

/*
 * Add the duplicate link to action list for post_row_actions
 */
function sv_duplicate_post_link( $actions, $post ) {
	if (current_user_can('edit_posts')) {
		$actions['duplicate'] = '<a href="' . wp_nonce_url('admin.php?action=sv_duplicate_post_as_draft&amp;post=' . $post-&gt;ID, basename(__FILE__), 'duplicate_nonce' ) . '" title="Duplicate" rel="permalink">Duplicate</a>';
	}
	return $actions;
}

add_filter( 'post_row_actions', 'sv_duplicate_post_link', 10, 2 );

However, this code will work only for posts (Posts) and not for pages and custom post types. If you want this feature to work on pages (Pages) as well, for example, add another filter in the following manner:

add_filter( 'post_row_actions', 'sv_duplicate_post_link', 10, 2 );

 

Roee Yossef
Roee Yossef

I develop pixel-perfect custom WordPress themes, delivering high-performance, SEO-optimized websites. Have a project in mind? need assistance? Feel free to contact me!

0 Comments...

Leave a Comment

To add code, use the buttons below. For instance, click the PHP button to insert PHP code within the shortcode. If you notice any typos, please let us know!