html = $html; $this->plugin = $plugin; } public abstract function replacePostUpdatedMessage(array $messages): array; protected function cloneItemMeta(int $fromPostId, int $toPostId) { global $wpdb; $metaFields = get_post_custom($fromPostId); foreach ($metaFields as $metaKey => $metaValues) { if (! is_array($metaValues) || ! $metaValues) { continue; } foreach ($metaValues as $i => $metaValue) { $wpdb->insert($wpdb->prefix . 'postmeta', [ 'post_id' => $toPostId, 'meta_key' => sanitize_text_field($metaKey), 'meta_value' => $metaValue, ], [ '%d', '%s', '%s', ]); } } } protected function getActionClone(): string { return static::NAME . '_clone'; } protected function getActionCloned(): string { return static::NAME . '_cloned'; } /** * @param int|string $postId Can be string, e.g. 'options' * * @return bool */ protected function isMyPost($postId): bool { // for 'site-settings' and similar if (! is_numeric($postId)) { return false; } $post = get_post($postId); if (static::NAME !== $post->post_type || wp_is_post_revision($postId) || 'publish' !== $post->post_status) { return false; } return true; } public function setHooks(): void { add_action('admin_init', [$this, 'cloneItemAction']); add_action('admin_notices', [$this, 'showItemClonedMessage']); add_action('add_meta_boxes', [$this, 'addMetaboxes']); add_action('admin_enqueue_scripts', [$this, 'addCodemirrorToCPTEditScreens']); add_action('post_edit_form_tag', [$this, 'disableAutocompleteForPostEdit']); add_filter('views_edit-' . static::NAME, [$this, 'printPostTypeDescription',]); add_filter('post_row_actions', [$this, 'getRowActions',], 10, 2); add_filter('post_updated_messages', [$this, 'replacePostUpdatedMessage']); } public function cloneItemAction(): void { if (! isset($_GET[$this->getActionClone()])) { return; } $viewId = (int)$_GET[$this->getActionClone()]; $viewPost = get_post($viewId); if (! $viewPost || static::NAME !== $viewPost->post_type) { return; } check_admin_referer('bulk-posts'); $args = [ 'post_type' => $viewPost->post_type, 'post_status' => 'draft', 'post_name' => $viewPost->post_name, 'post_title' => $viewPost->post_title . ' Clone', 'post_author' => $viewPost->post_author, ]; $newPostId = wp_insert_post($args); // something went wrong if (is_wp_error($newPostId)) { return; } $this->cloneItemMeta($viewPost->ID, $newPostId); $targetUrl = get_admin_url(null, '/edit.php?post_type=' . static::NAME); $targetUrl .= '&' . $this->getActionCloned() . '=1'; wp_redirect($targetUrl); exit; } public function showItemClonedMessage(): void { if (! isset($_GET[$this->getActionCloned()])) { return; } echo '
' . '

Item success cloned.

' . '
'; } public function getRowActions(array $actions, WP_Post $view): array { if (static::NAME !== $view->post_type) { return $actions; } $trash = str_replace('>Trash<', '>Delete<', $actions['trash']); // quick edit unset($actions['inline hide-if-no-js']); unset($actions['trash']); $cloneLink = get_admin_url(null, '/edit.php?post_type=' . static::NAME); $cloneLink .= '&' . $this->getActionClone() . '=' . $view->ID . '&_wpnonce=' . wp_create_nonce( 'bulk-posts' ); $actions['clone'] = "Clone"; $actions['trash'] = $trash; return $actions; } protected function addProBannerMetabox(): void { add_meta_box( 'acf-views_pro', 'PRO', function ($post, $meta) { echo $this->html->proBanner(Plugin::PRO_VERSION_URL, $this->plugin->getAssetsUrl('pro.png')); }, [ static::NAME, ], 'side' ); } public function addMetaboxes(): void { add_meta_box( 'acf-views_review', 'Rate & Review', function ($post, $meta) { echo $this->html->postboxReview(); }, [ static::NAME, ], 'side' ); add_meta_box( 'acf-views_support', 'Having issues?', function ($post, $meta) { echo $this->html->postboxSupport(); }, [ static::NAME, ], 'side' ); $this->addProBannerMetabox(); } public function addCodemirrorToCPTEditScreens() { if (! $this->plugin->isCPTScreen(static::NAME)) { return; } $cmSettings = [ '_html' => wp_enqueue_code_editor([ 'type' => 'text/html', 'codemirror' => [ 'lint' => true, ], ]), '_css' => wp_enqueue_code_editor([ 'type' => 'text/css', 'codemirror' => [ 'lint' => true, ], ]), '_js' => wp_enqueue_code_editor([ 'type' => 'javascript', 'codemirror' => [ 'lint' => true, 'matchBrackets' => true, 'autoCloseBrackets' => true, ], ]), '_php' => wp_enqueue_code_editor([ 'type' => 'php', 'codemirror' => [ 'matchBrackets' => true, 'autoCloseBrackets' => true, ], ]), ]; wp_localize_script('jquery', '_cm_settings', $cmSettings); wp_enqueue_script('wp-theme-plugin-editor'); wp_enqueue_style('wp-codemirror'); } public function printPostTypeDescription($views) { $screen = get_current_screen(); $postType = get_post_type_object($screen->post_type); if ($postType->description) { printf('

%s

', esc_html($postType->description)); } return $views; // return original input unchanged } /** * Otherwise in case editing fields (without saving) and reloading a page, * then the fields have these unsaved values, it's wrong and breaks logic (e.g. of group-field selects) */ public function disableAutocompleteForPostEdit(WP_Post $post): void { if (static::NAME !== $post->post_type) { return; } echo ' autocomplete="off"'; } }