Code thêm Chức năng Sao chép bài viết (Duplicate Post) trong Wp-admin

Cập nhật lần cuối 07/03/2026 bởi Hà Văn Thuật trong Thủ thuật Code vào 07/03/2026

Tính năng Nhân bản Bài viết (Duplicate Post) giúp ích gì cho bạn?

Trong quá trình quản trị website, việc phải tạo nhiều bài viết hoặc trang có cấu trúc, định dạng giống nhau là rất thường xuyên. Chức năng “Sao chép” được tích hợp sẵn này sẽ là trợ thủ đắc lực giúp bạn:

  • ⏱️ Tiết kiệm thời gian tối đa: Thay vì phải copy-paste thủ công tiêu đề, nội dung, ảnh đại diện hay cài đặt lại từng danh mục, thẻ tag… bạn chỉ cần 1 click chuột. Tất cả sẽ được nhân bản hoàn hảo trong 1 giây.
  • 🧩 Giữ nguyên định dạng phức tạp: Nếu trang của bạn sử dụng các Page Builder (như Elementor, UX Builder, WPBakery) hoặc có nhiều trường dữ liệu tùy chỉnh (Custom Fields), tính năng này sẽ clone chính xác 100% cấu trúc đó sang trang mới mà không bị vỡ giao diện.
  • 🛡️ Tuyệt đối an toàn: Bản sao mới sinh ra sẽ luôn tự động được đặt ở trạng thái “Bản nháp” (Draft) và có chữ (Sao chép) ở tiêu đề. Bạn hoàn toàn yên tâm chỉnh sửa, thay đổi nội dung mà không lo bài viết bị tự động hiển thị ra công chúng khi chưa hoàn thiện.
  • 👥 Tiện lợi cho làm việc nhóm: Khi bạn sao chép một bài của người khác, hệ thống sẽ tự động gán tên bạn làm tác giả của bản nháp mới, giúp phân rõ trách nhiệm trong quá trình chỉnh sửa.

Bạn hãy copy toàn bộ đoạn code dưới đây và cho vào file functions.php nhé

// Chức năng Sao chép bài viết (Duplicate Post)
function dev_duplicate_post_as_draft() {
global $wpdb;
if (! ( isset( $_GET['post'] ) || isset( $_POST['post'] ) || ( isset($_REQUEST['action']) && 'dev_duplicate_post_as_draft' == $_REQUEST['action'] ) ) ) {
wp_die('Không tìm thấy bài viết để sao chép!');
}
if ( !isset( $_GET['duplicate_nonce'] ) || !wp_verify_nonce( $_GET['duplicate_nonce'], basename( __FILE__ ) ) ) {
return;
}
$post_id = (isset($_GET['post']) ? absint( $_GET['post'] ) : absint( $_POST['post'] ) );
$post = get_post( $post_id );
$current_user = wp_get_current_user();
$new_post_author = $current_user->ID;
if (isset( $post ) && $post != null) {
$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 . ' (Sao chép)',
'post_type' => $post->post_type,
'to_ping' => $post->to_ping,
'menu_order' => $post->menu_order
);
$new_post_id = wp_insert_post( $args );
$taxonomies = get_object_taxonomies($post->post_type); 
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);
}
$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);
}
wp_redirect( admin_url( 'post.php?action=edit&post=' . $new_post_id ) );
exit;
} else {
wp_die('Lỗi: Không tìm thấy bài viết gốc, ID: ' . $post_id);
}
}
add_action( 'admin_action_dev_duplicate_post_as_draft', 'dev_duplicate_post_as_draft' );
// Thêm link "Sao chép" vào hàng hành động (Row Actions)
function dev_duplicate_post_link( $actions, $post ) {
if (current_user_can('edit_posts')) {
$actions['duplicate'] = '<a href="' . wp_nonce_url('admin.php?action=dev_duplicate_post_as_draft&post=' . $post->ID, basename(__FILE__), 'duplicate_nonce') . '" title="Sao chép bài viết này" rel="permalink">Sao chép</a>';
}
return $actions;
}
add_filter( 'post_row_actions', 'dev_duplicate_post_link', 10, 2 );
add_filter( 'page_row_actions', 'dev_duplicate_post_link', 10, 2 );

Đoạn code bạn cung cấp là một tính năng cực kỳ hữu ích trong WordPress. Nó tạo ra một chức năng “Sao chép” (Duplicate) trực tiếp trong trang quản trị, cho phép bạn nhân bản một bài viết hoặc trang web chỉ với một cú click chuột.

Giải thích chi tiết hoạt động của đoạn code

Đoạn code được chia làm 2 phần chính:

Phần 1: Hàm xử lý nhân bản dữ liệu (dev_duplicate_post_as_draft)

  • Bảo mật: Đầu tiên, hàm sẽ kiểm tra xem người dùng có quyền hợp lệ không và xác thực nonce (một mã bảo mật sinh ra ngẫu nhiên) để chống lại các cuộc tấn công giả mạo (CSRF).
  • Lấy dữ liệu gốc: Nó lấy ID của bài viết hiện tại và truy xuất toàn bộ nội dung của bài viết đó.
  • Tạo bản sao mới: Nó thiết lập một mảng dữ liệu ($args) chứa nội dung, trích dẫn, mật khẩu, menu order… của bài cũ để tạo một bài mới. Điểm đặc biệt là:
    • Tiêu đề sẽ tự động được thêm chữ “(Sao chép)” ở đuôi để dễ phân biệt.
    • Trạng thái bài viết mới luôn được đặt là ‘draft’ (Bản nháp) để đảm bảo an toàn, không bị xuất bản nhầm.
    • Tác giả bài viết sẽ được chuyển thành người đang thực hiện hành động sao chép.
  • Sao chép Danh mục & Thẻ (Taxonomies): Hàm lấy toàn bộ Category, Tag và các phân loại tùy chỉnh của bài cũ để gắn vào bài mới.
  • Sao chép Dữ liệu meta (Custom Fields): Nó dùng một câu lệnh SQL (UNION ALL) để sao chép toàn bộ các trường dữ liệu tùy biến (meta data) từ bài cũ sang bài mới một cách rất nhanh chóng và tối ưu (chỉ bỏ qua _wp_old_slug).
  • Chuyển hướng: Cuối cùng, nó tự động chuyển hướng người dùng thẳng đến trình soạn thảo của bài viết bản nháp vừa tạo ra để họ có thể chỉnh sửa ngay lập tức.

Phần 2: Hàm tạo nút bấm trên giao diện (dev_duplicate_post_link)

  • Hàm này kiểm tra xem người dùng có quyền edit_posts (chỉnh sửa bài viết) hay không.
  • Nếu có, nó sẽ chèn thêm một nút “Sao chép” vào menu hành động (Row Actions – nơi có các nút Chỉnh sửa, Sửa nhanh, Xóa tạm) khi bạn rê chuột vào tên một bài viết trong danh sách.
  • Nút này được áp dụng cho cả Bài viết (Post) và Trang (Page) thông qua hai dòng add_filter ở cuối.

 

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *