options = $options; add_action( 'admin_init', [ $this, 'init' ] ); } /** * Option keys that only Pro's settings UI ever writes. * * @return string[] */ protected function get_pro_only_option_keys(): array { return [ 'replace_tax', 'replace_post_metas', 'update_guid', 'delete_comments', 'after_republish_action', 'clone_roles', 'refresh_roles' ]; } /** Registers the setting and builds the sections/fields for both settings tabs. */ public function init(): void { $this->settings_options = [ [ 'option_group' => Content_Refresher::OPTION_NAME, 'option_name' => Content_Refresher::OPTION_NAME, 'args' => [ 'sanitize_callback' => [ $this, 'sanitize_settings' ] ], ], ]; $this->settings_sections = [ 'clone' => [ 'id' => 'adminoptim-content-refresher-clone-settings', 'title' => '', 'description' => __( 'Create a separate, independent draft copy of existing content.', 'admin-optimizer' ), 'menu_slug' => Content_Refresher::OPTION_NAME . '_clone', 'option_name' => Content_Refresher::OPTION_NAME, 'fields' => [ [ 'type' => 'checkbox', 'title' => __( 'Allow cloning', 'admin-optimizer' ), 'id' => 'content-refresher-allow-cloning', 'name' => 'allow_cloning', 'label' => __( 'Show the Clone action for permitted post types and roles.', 'admin-optimizer' ), ], [ 'type' => 'callback', 'title' => __( 'Post types', 'admin-optimizer' ), 'id' => 'content-refresher-clone-post-types', 'name' => 'clone_post_types', 'callback' => [ $this, 'render_post_types' ], 'param' => 'clone', ], [ 'type' => 'radio', 'title' => __( 'After cloning', 'admin-optimizer' ), 'id' => 'content-refresher-after-cloning', 'name' => 'after_cloning', 'choices' => [ 'return' => __( 'Return to the content list', 'admin-optimizer' ), 'edit' => __( 'Open the cloned post for editing', 'admin-optimizer' ), ], 'default' => 'return', ], ], ], 'refresh' => [ 'id' => 'adminoptim-content-refresher-refresh-settings', 'title' => '', 'description' => __( 'Content Refresh creates a working copy that replaces the original when refreshed, preserving the original URL, links, and SEO history. Unlike cloning, it does not create a separate published article.', 'admin-optimizer' ), 'menu_slug' => Content_Refresher::OPTION_NAME, 'option_name' => Content_Refresher::OPTION_NAME, 'fields' => [ [ 'type' => 'checkbox', 'title' => __( 'Allow content refresh', 'admin-optimizer' ), 'id' => 'content-refresher-allow-refresh', 'name' => 'allow_content_refresh', 'label' => __( 'Allow permitted users to create, edit, and schedule refresh copies.', 'admin-optimizer' ), 'desc' => __( 'A refresh copy overwrites its original post when refreshed; a normal clone remains an independent post.', 'admin-optimizer' ), ], [ 'type' => 'callback', 'title' => __( 'Post types', 'admin-optimizer' ), 'id' => 'content-refresher-refresh-post-types', 'name' => 'refresh_post_types', 'callback' => [ $this, 'render_post_types' ], 'param' => 'refresh', ], [ 'type' => 'checkbox', 'title' => __( 'Author', 'admin-optimizer' ), 'id' => 'content-refresher-replace-author', 'name' => 'replace_author', 'label' => __( 'Replace the original author with the refresh-copy author.', 'admin-optimizer' ), ], [ 'type' => 'checkbox', 'title' => __( 'Publish date', 'admin-optimizer' ), 'id' => 'content-refresher-replace-publish-date', 'name' => 'replace_publish_date', 'label' => __( 'Update the original publish date when refreshed.', 'admin-optimizer' ), ], [ 'type' => 'checkbox', 'title' => __( 'Modified date', 'admin-optimizer' ), 'id' => 'content-refresher-replace-modified-date', 'name' => 'replace_modified_date', 'label' => __( 'Update the original modified date when refreshed.', 'admin-optimizer' ), ], [ 'type' => 'select', 'title' => __( 'Cleanup behavior', 'admin-optimizer' ), 'id' => 'content-refresher-cleanup', 'name' => 'after_republish_action', 'choices' => [ 'trash' => __( 'Move the refresh copy to Trash', 'admin-optimizer' ), ], 'default' => 'trash', ], ], ], ]; if ( $this->is_pro_active() ) { $this->settings_sections = $this->replace_field( $this->settings_sections, 'adminoptim-content-refresher-refresh-settings', 'content-refresher-cleanup', [ 'type' => 'select', 'title' => __( 'Cleanup behavior', 'admin-optimizer' ), 'id' => 'content-refresher-cleanup', 'name' => 'after_republish_action', 'choices' => [ 'trash' => __( 'Move the refresh copy to Trash', 'admin-optimizer' ), 'delete' => __( 'Permanently delete the refresh copy', 'admin-optimizer' ), ], 'default' => 'trash', ] ); $this->settings_sections = $this->inject_pro_fields( $this->settings_sections, 'adminoptim-content-refresher-refresh-settings', [ [ 'type' => 'callback', 'title' => __( 'Roles', 'admin-optimizer' ), 'id' => 'content-refresher-refresh-roles', 'name' => 'refresh_roles', 'callback' => [ $this, 'render_roles' ], 'param' => 'refresh', ], [ 'type' => 'checkbox', 'title' => __( 'Taxonomies', 'admin-optimizer' ), 'id' => 'content-refresher-replace-taxonomies', 'name' => 'replace_tax', 'label' => __( 'Replace categories, tags, and other taxonomy terms.', 'admin-optimizer' ), ], [ 'type' => 'checkbox', 'title' => __( 'Custom fields', 'admin-optimizer' ), 'id' => 'content-refresher-replace-meta', 'name' => 'replace_post_metas', 'label' => __( 'Replace matching custom fields with values from the refresh copy.', 'admin-optimizer' ), ], [ 'type' => 'checkbox', 'title' => __( 'RSS identity', 'admin-optimizer' ), 'id' => 'content-refresher-update-guid', 'name' => 'update_guid', 'label' => __( 'Mark the refreshed article as new for feed readers.', 'admin-optimizer' ), ], [ 'type' => 'checkbox', 'title' => __( 'Comments', 'admin-optimizer' ), 'id' => 'content-refresher-delete-comments', 'name' => 'delete_comments', 'label' => __( 'Delete comments from the original after a successful refresh.', 'admin-optimizer' ), ], ] ); $this->settings_sections = $this->inject_pro_fields( $this->settings_sections, 'adminoptim-content-refresher-clone-settings', [ [ 'type' => 'callback', 'title' => __( 'Roles', 'admin-optimizer' ), 'id' => 'content-refresher-clone-roles', 'name' => 'clone_roles', 'callback' => [ $this, 'render_roles' ], 'param' => 'clone', ], ] ); } else { $this->settings_sections['refresh_pro'] = [ 'id' => 'adminoptim-content-refresher-refresh-settings-pro', 'title' => __( 'Pro Options', 'admin-optimizer' ), /* translators: %1$s is the anchor link to the Pro version. %2$s is the closing anchor tag */ 'description' => sprintf( __( 'Upgrade to the %1$sPro version%2$s to access these features', 'admin-optimizer' ), '', '' ), 'menu_slug' => Content_Refresher::OPTION_NAME . '_pro', 'option_name' => Content_Refresher::OPTION_NAME, 'args' => [ 'before_section' => '
', ], 'fields' => [ [ 'type' => 'callback', 'title' => __( 'Roles', 'admin-optimizer' ), 'id' => 'content-refresher-refresh-roles-pro', 'name' => 'refresh_roles', 'callback' => [ $this, 'render_roles' ], 'description' => __( 'Currently, only Administrator can create refresher post. Assign the refresh permission to the following user roles:', 'admin-optimizer' ), 'param' => 'refresh', ], [ 'type' => 'checkbox', 'title' => __( 'Taxonomies', 'admin-optimizer' ), 'id' => 'content-refresher-replace-taxonomies-pro', 'name' => 'replace_tax_pro', 'disabled' => 'disabled', 'label' => __( 'Replace categories, tags, and other taxonomy terms.', 'admin-optimizer' ), ], [ 'type' => 'checkbox', 'title' => __( 'Custom fields', 'admin-optimizer' ), 'id' => 'content-refresher-replace-meta-pro', 'name' => 'replace_post_metas_pro', 'disabled' => 'disabled', 'label' => __( 'Replace matching custom fields with values from the refresh copy.', 'admin-optimizer' ), ], [ 'type' => 'checkbox', 'title' => __( 'RSS identity', 'admin-optimizer' ), 'id' => 'content-refresher-update-guid-pro', 'name' => 'update_guid_pro', 'disabled' => 'disabled', 'label' => __( 'Mark the refreshed article as new for feed readers.', 'admin-optimizer' ), ], [ 'type' => 'checkbox', 'title' => __( 'Comments', 'admin-optimizer' ), 'id' => 'content-refresher-delete-comments-pro', 'name' => 'delete_comments_pro', 'disabled' => 'disabled', 'label' => __( 'Delete comments from the original after a successful refresh.', 'admin-optimizer' ), ], [ 'type' => 'radio', 'title' => __( 'Cleanup behavior', 'admin-optimizer' ), 'id' => 'content-refresher-cleanup-pro', 'name' => 'after_republish_action_pro', 'choices' => [ 'trash' => __( 'Move the refresh copy to Trash', 'admin-optimizer' ), 'delete' => __( 'Permanently delete the refresh copy', 'admin-optimizer' ), ], 'disabled' => 'disabled', ], ], ]; $this->settings_sections['clone_pro'] = [ 'id' => 'adminoptim-content-refresher-clone-settings-pro', 'title' => __( 'Pro Options', 'admin-optimizer' ), /* translators: %1$s is the anchor link to the Pro version. %2$s is the closing anchor tag */ 'description' => sprintf( __( 'Upgrade to the %1$sPro version%2$s to access these features', 'admin-optimizer' ), '', '' ), 'menu_slug' => Content_Refresher::OPTION_NAME . '_clone_pro', 'option_name' => Content_Refresher::OPTION_NAME, 'args' => [ 'before_section' => '', ], 'fields' => [ [ 'type' => 'callback', 'title' => __( 'Roles', 'admin-optimizer' ), 'id' => 'content-refresher-clone-roles-pro', 'name' => 'clone_roles', 'callback' => [ $this, 'render_roles' ], 'description' => __( 'Currently, only Administrator can clone post. Assign the clone permission to the following user roles:', 'admin-optimizer' ), 'param' => 'clone', ], ], ]; } $this->setup(); } /** Render post types for either settings section. */ public function render_post_types( $args, $context ): void { $option_key = 'clone' === $context ? 'clone_post_types' : 'refresh_post_types'; $selected = isset( $this->options[ $option_key ] ) ? (array) $this->options[ $option_key ] : []; $post_types = get_post_types( [ 'show_ui' => true ], 'objects' ); foreach ( $post_types as $post_type ) { if ( 'attachment' === $post_type->name ) { continue; } $id = 'adminoptim-' . $context . '-post-type-' . $post_type->name; ?>