PluginProbe
WP EXtra – One Click Optimize / trunk
WP EXtra – One Click Optimize vtrunk
8.7.1 8.6.8 8.7.0 trunk 5.9 8.0 8.5.0 8.5.4 8.5.5 8.6.0 8.6.1 8.6.2 8.6.3 8.6.5
wp-extra / src / Modules / Backend / Duplicate.php

Duplicate.php in WP EXtra – One Click Optimize trunk, at src/Modules/Backend/Duplicate.php

222 lines 8.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace WPEXtra\Modules\Backend;
3
4 if ( ! defined( 'ABSPATH' ) ) {
5 exit;
6 }
7
8 use WPEXtra\Helper;
9 use WPEXtra\Base;
10
11 class Duplicate extends Base {
12
13 public function __construct() {
14 parent::__construct();
15 }
16
17 protected $features = [
18 'duplicate',
19 'duplicate_tax',
20 ];
21
22 public function duplicate() {
23 add_action('admin_action_duplicate_as_draft', [$this, 'duplicate_as_draft']);
24 add_filter('post_row_actions', [$this, 'duplicate_post_link'], 10, 2);
25 add_filter('page_row_actions', [$this, 'duplicate_post_link'], 10, 2);
26 }
27
28 public function duplicate_tax() {
29 add_filter('tag_row_actions', [$this, 'add_tag_row_action'], 10, 2);
30 add_action('admin_post_duplicate-term', [$this, 'process_duplicate_term']);
31 add_action('admin_notices', [$this, 'add_admin_notice']);
32 }
33
34 public function duplicate_as_draft() {
35 $nonce = sanitize_text_field($_REQUEST['nonce'] ?? '');
36 $post_id = intval($_REQUEST['post'] ?? 0);
37 if (empty($nonce) || empty($post_id)) {
38 wp_die(esc_html__('Invalid request status.', 'wp-extra'));
39 }
40 if (!wp_verify_nonce($nonce, 'duplicate-page-' . $post_id)) {
41 wp_die(esc_html__('Security check failed.', 'wp-extra'));
42 }
43 $post = get_post($post_id);
44 if (empty($post)) {
45 wp_die(esc_html__('No posts found.', 'wp-extra'));
46 }
47 if (!current_user_can('edit_post', $post_id)) {
48 wp_die(esc_html__('Unauthorized to duplicate this item.', 'wp-extra'));
49 }
50
51 $post_status = current_user_can('publish_posts') ? 'draft' : 'pending';
52 $this->duplicate_edit_post($post_id, $post_status);
53 }
54
55 public function duplicate_edit_post($post_id, $post_status = 'draft') {
56 $post = get_post($post_id);
57 if (empty($post)) {
58 wp_die(esc_html__('No posts found.', 'wp-extra'));
59 }
60 $current_user = wp_get_current_user();
61 $args = [
62 'comment_status' => $post->comment_status,
63 'ping_status' => $post->ping_status,
64 'post_author' => $current_user->ID,
65 'post_content' => wp_slash($post->post_content),
66 'post_excerpt' => wp_slash($post->post_excerpt),
67 'post_parent' => $post->post_parent,
68 'post_password' => $post->post_password,
69 'post_status' => $post_status,
70 'post_title' => wp_slash($post->post_title . ' ' . __('(Copy)', 'wp-extra')),
71 'post_type' => $post->post_type,
72 'to_ping' => $post->to_ping,
73 'menu_order' => $post->menu_order,
74 ];
75 $new_post_id = wp_insert_post($args);
76 if (is_wp_error($new_post_id)) {
77 wp_die(esc_html($new_post_id->get_error_message()));
78 }
79
80 // Copy taxonomies
81 $taxonomies = get_object_taxonomies($post->post_type);
82 if (!empty($taxonomies) && is_array($taxonomies)) {
83 foreach ($taxonomies as $taxonomy) {
84 $post_terms = wp_get_object_terms($post_id, $taxonomy, ['fields' => 'ids']);
85 if (!empty($post_terms) && !is_wp_error($post_terms)) {
86 wp_set_object_terms($new_post_id, $post_terms, $taxonomy, false);
87 }
88 }
89 }
90
91 // Copy post meta (excluding internal locks and edits)
92 $exclude_meta = ['_edit_lock', '_edit_last'];
93 $post_meta = get_post_custom($post_id);
94 if (!empty($post_meta) && is_array($post_meta)) {
95 foreach ($post_meta as $meta_key => $meta_values) {
96 if (in_array($meta_key, $exclude_meta, true)) {
97 continue;
98 }
99 foreach ($meta_values as $meta_value) {
100 add_post_meta($new_post_id, $meta_key, maybe_unserialize($meta_value));
101 }
102 }
103 }
104
105 // Copy post format if supported
106 if (current_theme_supports('post-formats') && post_type_supports($post->post_type, 'post-formats')) {
107 $format = get_post_format($post_id);
108 if ($format) {
109 set_post_format($new_post_id, $format);
110 }
111 }
112
113 wp_safe_redirect(esc_url_raw(admin_url('post.php?action=edit&post=' . intval($new_post_id))));
114 exit;
115 }
116
117 public function duplicate_post_link($actions, $post) {
118 if ($post->post_type === 'acf-field-group') {
119 return $actions;
120 }
121 $duplicate_types = (array) Helper::get_option('duplicate', []);
122 if (current_user_can('edit_post', $post->ID) && in_array($post->post_type, $duplicate_types, true)) {
123 $actions['duplicate'] = '<a href="' . esc_url(admin_url('admin.php?action=duplicate_as_draft&post=' . intval($post->ID) . '&nonce=' . wp_create_nonce('duplicate-page-' . intval($post->ID)))) . '">' . __('Copy', 'wp-extra') . '</a>';
124 }
125 return $actions;
126 }
127
128 public function add_admin_notice() {
129 if (isset($_GET['duplicated']) && $_GET['duplicated'] === 'true') {
130 echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__('Term duplicated successfully.', 'wp-extra') . '</p></div>';
131 }
132 }
133
134 public function process_duplicate_term() {
135 check_admin_referer('duplicate-term');
136 $term_id = (int) filter_input(INPUT_GET, 'term_id');
137 $taxonomy = sanitize_key(filter_input(INPUT_GET, 'taxonomy'));
138 $tax_obj = get_taxonomy($taxonomy);
139 if (!$tax_obj || !current_user_can($tax_obj->cap->edit_terms)) {
140 wp_die(esc_html__('Sorry, you are not allowed to edit this taxonomy.', 'wp-extra'));
141 }
142 $new_term = $this->duplicate_term($term_id, $taxonomy);
143 if (is_wp_error($new_term)) {
144 wp_die(esc_html($new_term->get_error_message()));
145 }
146 $url = wp_get_referer() ?: admin_url("edit-tags.php?taxonomy={$taxonomy}");
147 $url = add_query_arg('duplicated', 'true', $url);
148 wp_safe_redirect($url);
149 exit;
150 }
151
152 public function add_tag_row_action($actions, $tag) {
153 $duplicate_tax = (array) Helper::get_option('duplicate_tax', []);
154 $tax_obj = get_taxonomy($tag->taxonomy);
155 if ($tax_obj && current_user_can($tax_obj->cap->edit_terms) && in_array($tag->taxonomy, $duplicate_tax, true)) {
156 $actions['duplicate'] = sprintf(
157 '<a href="%s">%s</a>',
158 esc_url($this->get_duplicate_term_url($tag)),
159 __('Copy', 'wp-extra')
160 );
161 }
162 return $actions;
163 }
164
165 private function get_duplicate_term_url($tag) {
166 return wp_nonce_url(add_query_arg([
167 'term_id' => $tag->term_id,
168 'taxonomy' => $tag->taxonomy,
169 'action' => 'duplicate-term',
170 ], admin_url('admin-post.php')), 'duplicate-term');
171 }
172
173 private function get_new_term_name($name, $taxonomy, $parent) {
174 $i = 1;
175 do {
176 $new_name = sprintf(__('%1$s (%2$d)', 'wp-extra'), $name, $i++);
177 } while (term_exists($new_name, $taxonomy, $parent));
178 return $new_name;
179 }
180
181 private function duplicate_term($term_id, $taxonomy) {
182 $term = get_term($term_id, $taxonomy);
183 if (is_wp_error($term) || !$term) {
184 return $term ?: new \WP_Error('invalid_term', __('Term not found.', 'wp-extra'));
185 }
186 $new_term = wp_insert_term($this->get_new_term_name($term->name, $term->taxonomy, $term->parent), $term->taxonomy, [
187 'description' => $term->description,
188 'parent' => $term->parent,
189 ]);
190 if (is_wp_error($new_term)) {
191 return $new_term;
192 }
193
194 $new_term_id = (int) $new_term['term_id'];
195
196 // Duplicate term meta
197 $term_meta = get_term_meta($term_id);
198 if (!empty($term_meta) && is_array($term_meta)) {
199 foreach ($term_meta as $meta_key => $meta_values) {
200 foreach ($meta_values as $meta_value) {
201 add_term_meta($new_term_id, $meta_key, maybe_unserialize($meta_value));
202 }
203 }
204 }
205
206 // Fast & memory-safe batch assignment of posts to the new term
207 $this->duplicate_posts_by_taxonomy($term_id, $new_term_id, $taxonomy);
208
209 return $new_term;
210 }
211
212 private function duplicate_posts_by_taxonomy($old_term_id, $new_term_id, $taxonomy) {
213 $object_ids = get_objects_in_term((int)$old_term_id, $taxonomy);
214 if (!empty($object_ids) && !is_wp_error($object_ids)) {
215 foreach (array_chunk($object_ids, 200) as $chunk) {
216 foreach ($chunk as $object_id) {
217 wp_set_object_terms((int)$object_id, (int)$new_term_id, $taxonomy, true);
218 }
219 }
220 }
221 }
222 }