HEX
Server: Apache/2.4.68 (Debian)
System: Linux as-cs-widget-demo-us-central1 6.1.0-44-cloud-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.164-1 (2026-03-09) x86_64
User: root (0)
PHP: 8.2.32
Disabled: NONE
Upload Files
File: //proc/self/cwd/wp-content/plugins/plugin-check/includes/Admin/Namer_Page.php
<?php
/**
 * Class WordPress\Plugin_Check\Admin\Namer_Page
 *
 * @package plugin-check
 */

namespace WordPress\Plugin_Check\Admin;

use WordPress\Plugin_Check\Traits\AI_Check_Names;
use WordPress\Plugin_Check\Traits\AI_Utils;
use WP_Error;

/**
 * Admin page for the Plugin Check Namer tool.
 *
 * @since 1.8.0
 */
final class Namer_Page {

	use AI_Check_Names;
	use AI_Utils;

	/**
	 * Menu slug.
	 *
	 * @since 1.8.0
	 * @var string
	 */
	const MENU_SLUG = 'plugin-check-namer';

	/**
	 * Admin-post action for analysis.
	 *
	 * @since 1.8.0
	 * @var string
	 */
	const ACTION_ANALYZE = 'plugin_check_namer_analyze';

	/**
	 * Hook suffix for the tools page.
	 *
	 * @since 1.8.0
	 * @var string
	 */
	protected $hook_suffix = '';

	/**
	 * Registers WordPress hooks.
	 *
	 * @since 1.8.0
	 */
	public function add_hooks() {
		add_action( 'admin_menu', array( $this, 'add_page' ) );
		add_action( 'admin_post_' . self::ACTION_ANALYZE, array( $this, 'handle_analyze' ) );
		add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
		add_action( 'admin_notices', array( $this, 'render_ai_notice' ) );
		add_action( 'wp_ajax_plugin_check_namer_analyze', array( $this, 'ajax_analyze' ) );
	}

	/**
	 * Adds the tools page.
	 *
	 * @since 1.8.0
	 */
	public function add_page() {
		$this->hook_suffix = add_management_page(
			__( 'Plugin Check Namer', 'plugin-check' ),
			__( 'Plugin Check Namer', 'plugin-check' ),
			'manage_options',
			self::MENU_SLUG,
			array( $this, 'render_page' )
		);
	}

	/**
	 * Enqueues scripts for the tools page.
	 *
	 * @since 1.8.0
	 *
	 * @param string $hook_suffix Current admin page hook suffix.
	 */
	public function enqueue_scripts( $hook_suffix ) {
		if ( $hook_suffix !== $this->hook_suffix ) {
			return;
		}

		wp_enqueue_style(
			'plugin-check-admin',
			plugins_url( 'assets/css/plugin-check-admin.css', WP_PLUGIN_CHECK_MAIN_FILE ),
			array(),
			WP_PLUGIN_CHECK_VERSION
		);

		wp_enqueue_script(
			'plugin-check-namer',
			plugins_url( 'assets/js/plugin-check-namer.js', WP_PLUGIN_CHECK_MAIN_FILE ),
			array(),
			WP_PLUGIN_CHECK_VERSION,
			true
		);

		wp_localize_script(
			'plugin-check-namer',
			'pluginCheckNamer',
			array(
				'ajaxUrl'  => admin_url( 'admin-ajax.php' ),
				'nonce'    => wp_create_nonce( 'plugin_check_namer_ajax' ),
				'messages' => array(
					'missingName'  => __( 'Please enter a plugin name.', 'plugin-check' ),
					'genericError' => __( 'An unexpected error occurred.', 'plugin-check' ),
				),
			)
		);
	}

	/**
	 * AJAX handler to analyze a plugin name.
	 *
	 * @since 1.8.0
	 */
	public function ajax_analyze() {
		check_ajax_referer( 'plugin_check_namer_ajax', 'nonce' );

		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( array( 'message' => __( 'Insufficient permissions.', 'plugin-check' ) ) );
		}

		$name = $this->get_plugin_name_from_request();
		if ( empty( $name ) ) {
			wp_send_json_error( array( 'message' => __( 'Please enter a plugin name.', 'plugin-check' ) ) );
		}

		$author = $this->get_author_name_from_request();

		$model_preference = $this->get_model_preference_from_request();
		$ai_config        = $this->get_ai_config( $model_preference );
		if ( is_wp_error( $ai_config ) ) {
			wp_send_json_error( array( 'message' => $ai_config->get_error_message() ) );
		}

		$analysis = $this->run_name_analysis( $ai_config['model_preference'], $name, $author );
		if ( is_wp_error( $analysis ) ) {
			wp_send_json_error( array( 'message' => $analysis->get_error_message() ) );
		}

		$parsed   = $this->parse_analysis( $analysis );
		$response = $this->build_ajax_response( $parsed, $analysis, $ai_config );

		wp_send_json_success( $response );
	}

	/**
	 * Builds AJAX response from parsed analysis.
	 *
	 * @since 1.8.0
	 *
	 * @param array        $parsed    Parsed analysis.
	 * @param string|array $analysis  Raw analysis.
	 * @param array        $ai_config AI configuration with model preference info.
	 * @return array Response array.
	 */
	protected function build_ajax_response( $parsed, $analysis, $ai_config = array() ) {
		$raw_output = $this->get_raw_output( $parsed, $analysis );
		$raw_output = $this->format_json_output( $raw_output );

		$response = array(
			'verdict'     => $parsed['verdict'],
			'explanation' => $parsed['explanation'],
			'raw'         => $raw_output,
		);

		if ( ! empty( $parsed['confusion_existing_plugins'] ) ) {
			$response['confusion_existing_plugins'] = $parsed['confusion_existing_plugins'];
		}
		if ( ! empty( $parsed['confusion_existing_others'] ) ) {
			$response['confusion_existing_others'] = $parsed['confusion_existing_others'];
		}
		if ( ! empty( $parsed['token_usage'] ) ) {
			$response['token_usage'] = $parsed['token_usage'];
		}

		// Add AI model preference information.
		if ( ! empty( $ai_config['model_preference'] ) ) {
			$response['ai_info'] = array(
				'model' => $ai_config['model_preference'],
			);
		}

		return $response;
	}

	/**
	 * Gets plugin name from request.
	 *
	 * @since 1.8.0
	 *
	 * @return string Plugin name or empty string.
	 */
	protected function get_plugin_name_from_request() {
		$name = isset( $_POST['plugin_name'] ) ? sanitize_text_field( wp_unslash( $_POST['plugin_name'] ) ) : '';
		return trim( $name );
	}

	/**
	 * Gets author name from request.
	 *
	 * @since 1.8.0
	 *
	 * @return string Author name or empty string.
	 */
	protected function get_author_name_from_request() {
		$author = isset( $_POST['author_name'] ) ? sanitize_text_field( wp_unslash( $_POST['author_name'] ) ) : '';
		return trim( $author );
	}

	/**
	 * Renders admin notice when AI connectors are not configured.
	 *
	 * @since 1.9.0
	 */
	public function render_ai_notice() {
		$screen = get_current_screen();
		if ( ! $screen || $screen->id !== $this->hook_suffix ) {
			return;
		}

		if ( ! current_user_can( 'manage_options' ) ) {
			return;
		}

		$ai_config = $this->get_ai_config();
		if ( ! is_wp_error( $ai_config ) ) {
			return;
		}
		?>
		<div class="notice notice-warning is-dismissible">
			<p>
				<?php
				printf(
					/* translators: %s: Error message. */
					__( 'To use the Namer tool, configure AI connectors in WordPress 7.0+ settings. Details: %s', 'plugin-check' ),
					esc_html( $ai_config->get_error_message() )
				);
				?>
			</p>
		</div>
		<?php
	}

	/**
	 * Renders the page.
	 *
	 * @since 1.8.0
	 *
	 * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
	 */
	public function render_page() {
		if ( ! current_user_can( 'manage_options' ) ) {
			return;
		}

		$ai_config = $this->get_ai_config();
		?>
		<div class="wrap">
			<h1><?php echo esc_html__( 'Plugin Check Namer Tool', 'plugin-check' ); ?></h1>

			<?php
			if ( is_wp_error( $ai_config ) ) {
				?>
				<p class="description">
					<?php esc_html_e( 'The Plugin Namer requires WordPress 7.0+ with configured AI connectors. Please enable AI connectors in core to use this tool.', 'plugin-check' ); ?>
				</p>
				<p>
					<a class="button button-primary" href="<?php echo esc_url( admin_url( 'options-connectors.php' ) ); ?>">
						<?php esc_html_e( 'Configure AI Connectors', 'plugin-check' ); ?>
					</a>
				</p>
				<?php
				return;
			}
			?>

			<form id="plugin-check-namer-form" method="post">
				<table class="form-table" role="presentation">
					<tbody>
						<tr>
							<th scope="row">
								<label for="plugin_check_namer_input"><?php echo esc_html__( 'Plugin name', 'plugin-check' ); ?></label>
							</th>
							<td>
								<input
									type="text"
									id="plugin_check_namer_input"
									name="plugin_check_namer_input"
									class="large-text"
									value=""
									required
								/>
								<p class="description">
									<?php echo esc_html__( 'Enter the plugin name you want to evaluate.', 'plugin-check' ); ?>
								</p>
							</td>
						</tr>
						<tr>
							<th scope="row">
								<label for="plugin_check_namer_author"><?php echo esc_html__( 'Author name', 'plugin-check' ); ?></label>
							</th>
							<td>
								<input
									type="text"
									id="plugin_check_namer_author"
									name="plugin_check_namer_author"
									class="regular-text"
									value=""
								/>
								<p class="description">
									<?php echo esc_html__( 'Optional: Enter the author or brand name if you own the trademark.', 'plugin-check' ); ?>
								</p>
							</td>
						</tr>
						<tr>
							<th scope="row">
								<label for="plugin_check_namer_model"><?php echo esc_html__( 'AI model', 'plugin-check' ); ?></label>
							</th>
							<td>
								<?php $model_groups = $this->get_available_model_preferences(); ?>
								<select
									id="plugin_check_namer_model"
									name="model_preference"
									class="regular-text"
								>
									<option value=""><?php echo esc_html__( 'Automatic (recommended)', 'plugin-check' ); ?></option>
									<?php foreach ( $model_groups as $group_label => $group_options ) : ?>
										<?php if ( ! empty( $group_label ) ) : ?>
											<optgroup label="<?php echo esc_attr( $group_label ); ?>">
										<?php endif; ?>
										<?php foreach ( $group_options as $model_option ) : ?>
											<option value="<?php echo esc_attr( $model_option['value'] ); ?>">
												<?php echo esc_html( $model_option['label'] ); ?>
											</option>
										<?php endforeach; ?>
										<?php if ( ! empty( $group_label ) ) : ?>
											</optgroup>
										<?php endif; ?>
									<?php endforeach; ?>
								</select>
								<p class="description">
									<?php echo esc_html__( 'Choose a specific model from connected AI providers, or leave as automatic to let WordPress decide.', 'plugin-check' ); ?>
								</p>
							</td>
						</tr>
					</tbody>
				</table>

				<p class="description">
					<strong><?php echo esc_html__( 'Note:', 'plugin-check' ); ?></strong>
					<br/>
					<?php echo esc_html__( 'This tool provides guidance only and is not definitive. It contains a prompt that is used to evaluate the similarity of a plugin name to other plugin names and ensure compliance with trademark regulations.', 'plugin-check' ); ?>
					<br/>
					<?php echo esc_html__( 'This analysis performs two AI checks for similarity and trademark conflicts, which may take a moment to complete.', 'plugin-check' ); ?>
				</p>
				<p class="submit">
					<button type="submit" class="button button-primary" id="plugin-check-namer-submit"><?php echo esc_html__( 'Evaluate name', 'plugin-check' ); ?></button>
					<span class="spinner plugin-check-namer-spinner" id="plugin-check-namer-spinner"></span>
				</p>
			</form>

			<div id="plugin-check-namer-error" class="notice notice-error plugin-check-namer-hidden"><p></p></div>

			<div id="plugin-check-namer-result" class="plugin-check-namer-hidden">
				<h2><?php echo esc_html__( 'Result', 'plugin-check' ); ?></h2>
				<div id="plugin-check-namer-verdict-container" class="plugin-check-namer-verdict-container plugin-check-namer-hidden">
					<p class="plugin-check-namer-verdict-item">
						<strong><?php echo esc_html__( 'Verdict:', 'plugin-check' ); ?></strong>
						<span id="plugin-check-namer-verdict"></span>
					</p>
					<p class="plugin-check-namer-verdict-item">
						<strong><?php echo esc_html__( 'Explanation:', 'plugin-check' ); ?></strong>
						<span id="plugin-check-namer-explanation"></span>
					</p>
				<p id="plugin-check-namer-timing" class="plugin-check-namer-meta plugin-check-namer-hidden">
					<strong><?php echo esc_html__( 'Analysis completed in:', 'plugin-check' ); ?></strong>
					<span id="plugin-check-namer-timing-value"></span>
				</p>
				<p id="plugin-check-namer-tokens" class="plugin-check-namer-meta plugin-check-namer-hidden">
					<strong><?php echo esc_html__( 'Tokens used:', 'plugin-check' ); ?></strong>
					<span id="plugin-check-namer-tokens-value"></span>
				</p>
			</div>
				<div id="plugin-check-namer-confusion-plugins" class="plugin-check-namer-confusion plugin-check-namer-hidden">
					<p><strong><?php echo esc_html__( 'Similar Existing Plugins', 'plugin-check' ); ?></strong></p>
					<div id="plugin-check-namer-confusion-plugins-list"></div>
				</div>

				<div id="plugin-check-namer-confusion-others" class="plugin-check-namer-confusion plugin-check-namer-hidden">
					<h3><?php echo esc_html__( 'Similar Existing Projects/Trademarks', 'plugin-check' ); ?></h3>
					<div id="plugin-check-namer-confusion-others-list"></div>
				</div>
			</div>
		</div>
		<?php
	}

	/**
	 * Handles the analysis form submission.
	 *
	 * @since 1.8.0
	 */
	public function handle_analyze() {
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_die( esc_html__( 'Insufficient permissions.', 'plugin-check' ) );
		}

		check_admin_referer( 'plugin_check_namer_analyze', 'plugin_check_namer_nonce' );

		$input = isset( $_POST['plugin_check_namer_input'] ) ? sanitize_text_field( wp_unslash( $_POST['plugin_check_namer_input'] ) ) : '';
		$input = trim( $input );

		$author = isset( $_POST['plugin_check_namer_author'] ) ? sanitize_text_field( wp_unslash( $_POST['plugin_check_namer_author'] ) ) : '';
		$author = trim( $author );

		$model_preference = $this->get_model_preference_from_request();
		$user_id          = get_current_user_id();

		if ( empty( $input ) ) {
			$this->handle_analyze_error( $user_id, '', new WP_Error( 'missing_input', __( 'Please enter a plugin name.', 'plugin-check' ) ) );
			return;
		}

		$ai_config = $this->get_ai_config( $model_preference );
		if ( is_wp_error( $ai_config ) ) {
			$this->handle_analyze_error( $user_id, $input, $ai_config );
			return;
		}

		$analysis = $this->run_name_analysis( $ai_config['model_preference'], $input, $author );

		if ( is_wp_error( $analysis ) ) {
			$this->handle_analyze_error( $user_id, $input, $analysis );
			return;
		}

		$this->store_result(
			$user_id,
			array(
				'input'    => $input,
				'analysis' => $analysis,
			)
		);
		wp_safe_redirect( $this->get_page_url() );
		exit;
	}

	/**
	 * Handles analyze error and redirects.
	 *
	 * @since 1.8.0
	 *
	 * @param int      $user_id User ID.
	 * @param string   $input   Input value.
	 * @param WP_Error $error   Error object.
	 */
	protected function handle_analyze_error( $user_id, $input, $error ) {
		$this->store_result(
			$user_id,
			array(
				'input' => $input,
				'error' => $error,
			)
		);
		wp_safe_redirect( $this->get_page_url() );
		exit;
	}

	/**
	 * Gets the page URL.
	 *
	 * @since 1.8.0
	 *
	 * @return string
	 */
	protected function get_page_url() {
		return add_query_arg( array( 'page' => self::MENU_SLUG ), admin_url( 'tools.php' ) );
	}
}