Skip to content

Early-bird lifetime licence: $199 once, for the first 200 buyers only. See the offer

Duplicate posts and pages

Adds “Duplicate” under each post and page in the admin lists. The copy is a draft with the same content, categories, tags, custom fields and featured image.

  • Type PHP
  • Runs Everywhere (runs as WordPress loads)
  • Tested with WordPress 7.1
PHP
<?php

// WooCommerce products have their own "Duplicate", so they're left out.
$row_action = static function ( $actions, $post ) {
	$type = get_post_type_object( $post->post_type );
	if ( 'product' !== $post->post_type && 'trash' !== $post->post_status && $type && current_user_can( $type->cap->create_posts ) && current_user_can( 'edit_post', $post->ID ) ) {
		$url                  = wp_nonce_url( admin_url( 'admin-post.php?action=sf_duplicate_post&post=' . $post->ID ), 'sf_duplicate_post_' . $post->ID );
		$actions['duplicate'] = sprintf( '<a href="%s">%s</a>', esc_url( $url ), esc_html( 'Duplicate' ) );
	}
	return $actions;
};
add_filter( 'post_row_actions', $row_action, 10, 2 );
add_filter( 'page_row_actions', $row_action, 10, 2 );

add_action(
	'admin_post_sf_duplicate_post',
	static function () {
		$id = isset( $_GET['post'] ) ? absint( $_GET['post'] ) : 0;
		check_admin_referer( 'sf_duplicate_post_' . $id );
		$post = $id ? get_post( $id ) : null;
		$type = $post ? get_post_type_object( $post->post_type ) : null;
		if ( ! $post || ! $type || ! current_user_can( $type->cap->create_posts ) || ! current_user_can( 'edit_post', $id ) ) {
			wp_die( esc_html__( 'Sorry, you are not allowed to edit this item.' ) );
		}

		$copy_id = wp_insert_post(
			wp_slash(
				array(
					'post_type'      => $post->post_type,
					'post_status'    => 'draft',
					'post_title'     => $post->post_title . ' (copy)',
					'post_content'   => $post->post_content,
					'post_excerpt'   => $post->post_excerpt,
					'post_parent'    => $post->post_parent,
					'menu_order'     => $post->menu_order,
					'post_password'  => $post->post_password,
					'comment_status' => $post->comment_status,
					'ping_status'    => $post->ping_status,
					'post_author'    => get_current_user_id(),
				)
			),
			true
		);
		if ( is_wp_error( $copy_id ) ) {
			wp_die( esc_html( $copy_id->get_error_message() ) );
		}

		foreach ( get_object_taxonomies( $post->post_type ) as $taxonomy ) {
			$terms = wp_get_object_terms( $id, $taxonomy, array( 'fields' => 'ids' ) );
			if ( ! is_wp_error( $terms ) ) {
				wp_set_object_terms( $copy_id, array_map( 'intval', $terms ), $taxonomy );
			}
		}
		foreach ( get_post_meta( $id ) as $key => $values ) {
			if ( in_array( $key, array( '_edit_lock', '_edit_last', '_wp_old_slug', '_wp_old_date' ), true ) ) {
				continue;
			}
			foreach ( $values as $value ) {
				add_post_meta( $copy_id, $key, wp_slash( maybe_unserialize( $value ) ) );
			}
		}

		wp_safe_redirect( admin_url( 'post.php?action=edit&post=' . $copy_id ) );
		exit;
	}
);

In these packs

  • Admin and login: Your branding on the login page, a tidier admin, and handy extras such as duplicating posts.

Move your snippets over this afternoon.

Importing changes nothing until you switch over, and your old shortcodes keep working.