classification = $classification; } public function register_hooks() { add_action( 'admin_menu', array( $this, 'add_menu' ) ); add_action( 'admin_init', array( $this, 'register_settings' ) ); add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ) ); add_action( 'save_post', array( $this, 'save_meta_box' ) ); add_filter( 'attachment_fields_to_edit', array( $this, 'attachment_fields' ), 10, 2 ); add_filter( 'attachment_fields_to_save', array( $this, 'save_attachment_fields' ), 10, 2 ); add_action( 'admin_post_aiparently_bulk_update', array( $this, 'handle_bulk_update' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) ); add_filter( 'plugin_action_links_' . plugin_basename( AIPARENTLY_FILE ), array( $this, 'action_links' ) ); } public function add_menu() { add_menu_page( __( 'AIparently', 'aiparently' ), __( 'AIparently', 'aiparently' ), 'edit_posts', 'aiparently', array( $this, 'render_dashboard' ), 'dashicons-visibility', 58 ); add_submenu_page( 'aiparently', __( 'Settings', 'aiparently' ), __( 'Settings', 'aiparently' ), 'manage_options', 'aiparently-settings', array( $this, 'render_settings' ) ); } public function register_settings() { register_setting( 'aiparently', 'aiparently_settings', array( 'sanitize_callback' => array( $this, 'sanitize_settings' ) ) ); } public function sanitize_settings( $input ) { $input = is_array( $input ) ? $input : array(); $position = isset( $input['position'] ) ? sanitize_key( $input['position'] ) : ''; $style = isset( $input['style'] ) ? sanitize_key( $input['style'] ) : ''; $image_label_position = isset( $input['image_label_position'] ) ? sanitize_key( $input['image_label_position'] ) : ''; return array( 'enabled' => empty( $input['enabled'] ) ? 0 : 1, 'position' => in_array( $position, array( 'before', 'after' ), true ) ? $position : 'after', 'style' => in_array( $style, array( 'badge', 'text' ), true ) ? $style : 'badge', 'images_enabled' => empty( $input['images_enabled'] ) ? 0 : 1, 'image_label_position' => in_array( $image_label_position, array( 'left', 'center', 'right' ), true ) ? $image_label_position : 'right', ); } public function action_links( $links ) { array_unshift( $links, '' . esc_html__( 'Overview', 'aiparently' ) . '' ); return $links; } public function enqueue_assets( $hook ) { if ( in_array( $hook, array( 'post.php', 'post-new.php', 'upload.php', 'media.php', 'toplevel_page_aiparently', 'aiparently_page_aiparently-settings' ), true ) ) { wp_enqueue_style( 'aiparently-admin', AIPARENTLY_URL . 'assets/css/admin.css', array(), AIPARENTLY_VERSION ); wp_enqueue_script( 'aiparently-admin', AIPARENTLY_URL . 'assets/js/admin.js', array(), AIPARENTLY_VERSION, true ); } } public function add_meta_boxes() { foreach ( array( 'post', 'page' ) as $post_type ) { add_meta_box( 'aiparently-classification', __( 'AIparently', 'aiparently' ), array( $this, 'render_meta_box' ), $post_type, 'side', 'default' ); } } public function render_meta_box( $post ) { wp_nonce_field( 'aiparently_save_classification', 'aiparently_nonce' ); $this->render_classification_fields( $this->classification->get_status( $post->ID ), 'aiparently' ); } private function render_classification_fields( $status, $prefix ) { $used = in_array( $status, array( 'assisted', 'generated' ), true ); if ( 'unchecked' === $status ) { echo '

' . esc_html__( 'Not reviewed yet. Saving will store “No” unless you change the selection.', 'aiparently' ) . '

'; } ?>

hidden>
classification->set_status( $post_id, $status ); } public function attachment_fields( $fields, $post ) { $status = $this->classification->get_status( $post->ID ); $used = in_array( $status, array( 'assisted', 'generated' ), true ); $html = ''; $html .= ' '; $html .= ''; $html .= '

'; $html .= '
'; $fields['aiparently_classification'] = array( 'label' => esc_html__( 'Was AI used?', 'aiparently' ), 'input' => 'html', 'html' => $html ); return $fields; } public function save_attachment_fields( $post, $attachment ) { $post_id = isset( $post['ID'] ) ? absint( $post['ID'] ) : 0; $nonce = isset( $attachment['aiparently_nonce'] ) ? sanitize_text_field( wp_unslash( $attachment['aiparently_nonce'] ) ) : ''; if ( ! $post_id || ! current_user_can( 'edit_post', $post_id ) || ! wp_verify_nonce( $nonce, 'aiparently_save_attachment_' . $post_id ) ) { return $post; } if ( isset( $attachment['aiparently_used'] ) ) { $used = sanitize_key( wp_unslash( $attachment['aiparently_used'] ) ); $status = 'yes' === $used && isset( $attachment['aiparently_status'] ) ? sanitize_key( wp_unslash( $attachment['aiparently_status'] ) ) : 'none'; $this->classification->set_status( $post_id, $status ); } return $post; } public function handle_bulk_update() { if ( ! current_user_can( 'edit_posts' ) ) { wp_die( esc_html__( 'You are not allowed to perform this action.', 'aiparently' ) ); } check_admin_referer( 'aiparently_bulk_update' ); $status = isset( $_POST['bulk_status'] ) ? AIparently_Classification_Service::sanitize_status( sanitize_key( wp_unslash( $_POST['bulk_status'] ) ) ) : ''; $ids = isset( $_POST['content_ids'] ) ? array_map( 'absint', (array) wp_unslash( $_POST['content_ids'] ) ) : array(); $updated = 0; if ( $status ) { foreach ( array_unique( $ids ) as $post_id ) { if ( current_user_can( 'edit_post', $post_id ) && true === $this->classification->set_status( $post_id, $status ) ) { ++$updated; } } } $url = add_query_arg( array( 'page' => 'aiparently', 'updated' => $updated ), admin_url( 'admin.php' ) ); wp_safe_redirect( $url ); exit; } private function status_label( $status ) { $labels = array( 'none' => __( 'No AI', 'aiparently' ), 'assisted' => __( 'AI-assisted', 'aiparently' ), 'generated' => __( 'AI-generated', 'aiparently' ), 'unchecked' => __( 'Not reviewed', 'aiparently' ), ); return isset( $labels[ $status ] ) ? $labels[ $status ] : $status; } public function render_dashboard() { if ( ! current_user_can( 'edit_posts' ) ) { return; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only list filter; no data is changed. $status = isset( $_GET['ai_status'] ) ? sanitize_key( wp_unslash( $_GET['ai_status'] ) ) : ''; $status = in_array( $status, array( 'unchecked', 'none', 'assisted', 'generated' ), true ) ? $status : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only pagination; no data is changed. $paged = isset( $_GET['paged'] ) ? max( 1, absint( $_GET['paged'] ) ) : 1; $query = $this->classification->list_content( array( 'status' => $status, 'paged' => $paged, 'posts_per_page' => 20, 'perm' => 'editable' ) ); $counts = $this->classification->get_counts( array( 'perm' => 'editable' ) ); ?>

__( 'Reviewed content', 'aiparently' ), 'assisted' => __( 'AI-assisted', 'aiparently' ), 'generated' => __( 'AI-generated', 'aiparently' ), 'unchecked' => __( 'Not reviewed yet', 'aiparently' ) ) as $key => $label ) : ?>
have_posts() ) : foreach ( $query->posts as $post ) : $item_status = $this->classification->get_status( $post->ID ); ?>
post_type )->labels->singular_name ); ?>status_label( $item_status ) ); ?>
add_query_arg( 'paged', '%#%' ), 'format' => '', 'current' => $paged, 'total' => max( 1, $query->max_num_pages ) ) ) ); ?>