diff --git a/assets/js/plugin-check-admin.js b/assets/js/plugin-check-admin.js
index 22227cc4e..0bc320444 100644
--- a/assets/js/plugin-check-admin.js
+++ b/assets/js/plugin-check-admin.js
@@ -38,6 +38,7 @@
'plugin-check__include-experimental'
);
const useAi = document.getElementById( 'plugin-check__use-ai' );
+ const useAiName = document.getElementById( 'plugin-check__use-ai-name' );
// Handle disabling the Check it button when a plugin is not selected.
function canRunChecks() {
@@ -92,6 +93,9 @@
if ( useAi ) {
useAi.disabled = true;
}
+ if ( useAiName ) {
+ useAiName.disabled = true;
+ }
if ( includeExperimental ) {
includeExperimental.disabled = true;
}
@@ -144,6 +148,9 @@
if ( useAi ) {
useAi.disabled = false;
}
+ if ( useAiName ) {
+ useAiName.disabled = false;
+ }
if ( includeExperimental ) {
includeExperimental.disabled = false;
}
@@ -596,6 +603,10 @@
includeExperimental && includeExperimental.checked ? 1 : 0
);
pluginCheckData.append( 'use-ai', useAi && useAi.checked ? 1 : 0 );
+ pluginCheckData.append(
+ 'use-ai-name',
+ useAiName && useAiName.checked ? 1 : 0
+ );
for ( let i = 0; i < data.checks.length; i++ ) {
pluginCheckData.append( 'checks[]', data.checks[ i ] );
@@ -669,6 +680,10 @@
includeExperimental && includeExperimental.checked ? 1 : 0
);
pluginCheckData.append( 'use-ai', useAi && useAi.checked ? 1 : 0 );
+ pluginCheckData.append(
+ 'use-ai-name',
+ useAiName && useAiName.checked ? 1 : 0
+ );
for ( let i = 0; i < categoriesList.length; i++ ) {
if ( categoriesList[ i ].checked ) {
@@ -957,6 +972,10 @@
includeExperimental && includeExperimental.checked ? 1 : 0
);
pluginCheckData.append( 'use-ai', useAi && useAi.checked ? 1 : 0 );
+ pluginCheckData.append(
+ 'use-ai-name',
+ useAiName && useAiName.checked ? 1 : 0
+ );
for ( let i = 0; i < typesList.length; i++ ) {
if ( typesList[ i ].checked ) {
diff --git a/assets/js/plugin-check-namer.js b/assets/js/plugin-check-namer.js
deleted file mode 100644
index f8e3d90cb..000000000
--- a/assets/js/plugin-check-namer.js
+++ /dev/null
@@ -1,432 +0,0 @@
-/**
- * Plugin Check Namer tool.
- */
-
-/* global pluginCheckNamer */
-
-( function () {
- 'use strict';
-
- function setText( el, text ) {
- if ( ! el ) {
- return;
- }
- el.textContent = text;
- }
-
- function setHtml( el, html ) {
- if ( ! el ) {
- return;
- }
- el.innerHTML = html;
- }
-
- function escapeHtml( text ) {
- const div = document.createElement( 'div' );
- div.textContent = text;
- return div.innerHTML;
- }
-
- document.addEventListener( 'DOMContentLoaded', function () {
- const form = document.getElementById( 'plugin-check-namer-form' );
- const input = document.getElementById( 'plugin_check_namer_input' );
-
- if ( ! form || ! input || ! window.pluginCheckNamer ) {
- return;
- }
-
- const authorInput = document.getElementById(
- 'plugin_check_namer_author'
- );
- const modelSelect = document.getElementById(
- 'plugin_check_namer_model'
- );
-
- const submitBtn = document.getElementById(
- 'plugin-check-namer-submit'
- );
- const spinner = document.getElementById( 'plugin-check-namer-spinner' );
-
- const resultWrap = document.getElementById(
- 'plugin-check-namer-result'
- );
- const verdictEl = document.getElementById(
- 'plugin-check-namer-verdict'
- );
- const explainEl = document.getElementById(
- 'plugin-check-namer-explanation'
- );
- const verdictContainer = document.getElementById(
- 'plugin-check-namer-verdict-container'
- );
- const confusionPluginsDiv = document.getElementById(
- 'plugin-check-namer-confusion-plugins'
- );
- const confusionPluginsList = document.getElementById(
- 'plugin-check-namer-confusion-plugins-list'
- );
- const confusionOthersDiv = document.getElementById(
- 'plugin-check-namer-confusion-others'
- );
- const confusionOthersList = document.getElementById(
- 'plugin-check-namer-confusion-others-list'
- );
- const timingDiv = document.getElementById(
- 'plugin-check-namer-timing'
- );
- const timingValue = document.getElementById(
- 'plugin-check-namer-timing-value'
- );
- const tokensDiv = document.getElementById(
- 'plugin-check-namer-tokens'
- );
- const tokensValue = document.getElementById(
- 'plugin-check-namer-tokens-value'
- );
- const errorDiv = document.getElementById( 'plugin-check-namer-error' );
- const errorEl = errorDiv ? errorDiv.querySelector( 'p' ) : null;
-
- function setLoading( isLoading ) {
- if ( spinner ) {
- spinner.classList.toggle( 'is-active', isLoading );
- }
- submitBtn.disabled = isLoading;
- }
-
- form.addEventListener( 'submit', function ( event ) {
- event.preventDefault();
-
- const name = ( input.value || '' ).trim();
- if ( ! name ) {
- setText( errorEl, pluginCheckNamer.messages.missingName );
- if ( errorEl ) {
- errorEl.classList.remove( 'plugin-check-namer-hidden' );
- }
- return;
- }
-
- // Clear previous results.
- if ( resultWrap ) {
- resultWrap.classList.add( 'plugin-check-namer-hidden' );
- }
- if ( verdictEl ) {
- setText( verdictEl, '' );
- }
- if ( explainEl ) {
- setHtml( explainEl, '' );
- }
- if ( errorDiv ) {
- errorDiv.classList.add( 'plugin-check-namer-hidden' );
- }
- if ( errorEl ) {
- setText( errorEl, '' );
- }
- if ( confusionPluginsDiv ) {
- confusionPluginsDiv.classList.add(
- 'plugin-check-namer-hidden'
- );
- }
- if ( confusionPluginsList ) {
- confusionPluginsList.innerHTML = '';
- }
- if ( confusionOthersDiv ) {
- confusionOthersDiv.classList.add( 'plugin-check-namer-hidden' );
- }
- if ( confusionOthersList ) {
- confusionOthersList.innerHTML = '';
- }
- if ( timingDiv ) {
- timingDiv.classList.add( 'plugin-check-namer-hidden' );
- }
- if ( timingValue ) {
- setText( timingValue, '' );
- }
- if ( tokensDiv ) {
- tokensDiv.classList.add( 'plugin-check-namer-hidden' );
- }
- if ( tokensValue ) {
- setText( tokensValue, '' );
- }
- if ( verdictContainer ) {
- verdictContainer.classList.add( 'plugin-check-namer-hidden' );
- }
-
- // Record start time.
- const startTime = Date.now();
-
- setLoading( true );
-
- const formData = new FormData();
- formData.append( 'action', 'plugin_check_namer_analyze' );
- formData.append( 'nonce', pluginCheckNamer.nonce );
- formData.append( 'plugin_name', name );
- if ( authorInput ) {
- formData.append(
- 'author_name',
- ( authorInput.value || '' ).trim()
- );
- }
- if ( modelSelect ) {
- formData.append(
- 'model_preference',
- ( modelSelect.value || '' ).trim()
- );
- }
-
- fetch( pluginCheckNamer.ajaxUrl, {
- method: 'POST',
- credentials: 'same-origin',
- body: formData,
- } )
- .then( function ( response ) {
- return response.json();
- } )
- .then( function ( payload ) {
- if ( ! payload || ! payload.success ) {
- throw new Error(
- payload && payload.data && payload.data.message
- ? payload.data.message
- : pluginCheckNamer.messages.genericError
- );
- }
-
- setText( verdictEl, payload.data.verdict || '' );
- setHtml( explainEl, payload.data.explanation || '' );
-
- // Set border color based on verdict.
- if ( verdictContainer ) {
- const verdict = (
- payload.data.verdict || ''
- ).toLowerCase();
- let borderColor = '#2271b1'; // Default blue.
-
- if ( verdict.indexOf( 'disallowed' ) !== -1 ) {
- borderColor = '#d63638'; // Red for disallowed.
- } else if (
- verdict.indexOf( 'good' ) !== -1 ||
- verdict.indexOf( 'low' ) !== -1 ||
- verdict.indexOf( 'no issues' ) !== -1
- ) {
- borderColor = '#00a32a'; // Green for good.
- } else if (
- verdict.indexOf( 'generally allowable' ) !== -1 ||
- verdict.indexOf( 'allowable' ) !== -1
- ) {
- borderColor = '#2271b1'; // Blue for generally allowable.
- } else if (
- verdict.indexOf( 'review' ) !== -1 ||
- verdict.indexOf( 'medium' ) !== -1 ||
- verdict.indexOf( 'issues found' ) !== -1
- ) {
- borderColor = '#dba617'; // Yellow/orange for needs review.
- } else if (
- verdict.indexOf( 'problematic' ) !== -1 ||
- verdict.indexOf( 'high' ) !== -1
- ) {
- borderColor = '#d63638'; // Red for problematic.
- }
-
- verdictContainer.style.borderLeftColor = borderColor;
- verdictContainer.classList.remove(
- 'plugin-check-namer-hidden'
- );
- }
-
- // Calculate and display elapsed time.
- if ( timingDiv && timingValue ) {
- const endTime = Date.now();
- const elapsedSeconds = Math.round(
- ( endTime - startTime ) / 1000
- );
- timingValue.textContent =
- elapsedSeconds + ' ' + 'seconds';
- timingDiv.classList.remove(
- 'plugin-check-namer-hidden'
- );
- }
-
- // Display token usage if available.
- if (
- tokensDiv &&
- tokensValue &&
- payload.data.token_usage
- ) {
- const tokenUsage = payload.data.token_usage;
- let tokensText = '';
-
- // Add AI provider and model info if available.
- if ( payload.data.ai_info ) {
- const parts = [];
- if ( payload.data.ai_info.provider ) {
- parts.push( payload.data.ai_info.provider );
- }
- if ( payload.data.ai_info.model ) {
- parts.push( payload.data.ai_info.model );
- }
- if ( parts.length ) {
- tokensText = parts.join( ' / ' ) + ' - ';
- }
- }
-
- const totalTokens =
- tokenUsage.total_tokens ||
- ( tokenUsage.prompt_tokens &&
- tokenUsage.completion_tokens
- ? tokenUsage.prompt_tokens +
- tokenUsage.completion_tokens
- : null );
-
- if ( totalTokens ) {
- tokensText += totalTokens + ' total';
- }
-
- // Add breakdown with prompt and completion tokens.
- if (
- tokenUsage.prompt_tokens &&
- tokenUsage.completion_tokens
- ) {
- tokensText +=
- ' (' +
- tokenUsage.prompt_tokens +
- ' prompt + ' +
- tokenUsage.completion_tokens +
- ' completion)';
- }
-
- // Add similar name query tokens if available.
- if (
- tokenUsage.similar_name &&
- tokenUsage.similar_name.total_tokens
- ) {
- tokensText +=
- ' [Similar name query: ' +
- tokenUsage.similar_name.total_tokens +
- ' tokens]';
- }
-
- tokensValue.textContent = tokensText;
- tokensDiv.classList.remove(
- 'plugin-check-namer-hidden'
- );
- }
-
- // Display confusion_existing_plugins if available.
- if (
- confusionPluginsDiv &&
- confusionPluginsList &&
- payload.data.confusion_existing_plugins &&
- payload.data.confusion_existing_plugins.length > 0
- ) {
- confusionPluginsList.innerHTML = '';
- payload.data.confusion_existing_plugins.forEach(
- function ( plugin ) {
- const div = document.createElement( 'div' );
- div.className =
- 'plugin-check-namer-confusion-item';
- div.innerHTML =
- '' +
- escapeHtml( plugin.name || '' ) +
- '' +
- ( plugin.active_installations
- ? ' '
- : '' ) +
- ( plugin.owner_username
- ? ' '
- : '' ) +
- '
' +
- '' +
- escapeHtml( plugin.explanation || '' ) +
- '' +
- ( plugin.link
- ? '
' +
- escapeHtml( plugin.link ) +
- ''
- : '' );
- confusionPluginsList.appendChild( div );
- }
- );
- confusionPluginsDiv.classList.remove(
- 'plugin-check-namer-hidden'
- );
- } else if ( confusionPluginsDiv ) {
- confusionPluginsDiv.classList.add(
- 'plugin-check-namer-hidden'
- );
- }
-
- // Display confusion_existing_others if available.
- if (
- confusionOthersDiv &&
- confusionOthersList &&
- payload.data.confusion_existing_others &&
- payload.data.confusion_existing_others.length > 0
- ) {
- confusionOthersList.innerHTML = '';
- payload.data.confusion_existing_others.forEach(
- function ( item ) {
- const div = document.createElement( 'div' );
- div.className =
- 'plugin-check-namer-confusion-item plugin-check-namer-confusion-item-others';
- div.innerHTML =
- '' +
- escapeHtml( item.name || '' ) +
- '' +
- '
' +
- '' +
- escapeHtml( item.explanation || '' ) +
- '' +
- ( item.link
- ? '
' +
- escapeHtml( item.link ) +
- ''
- : '' );
- confusionOthersList.appendChild( div );
- }
- );
- confusionOthersDiv.classList.remove(
- 'plugin-check-namer-hidden'
- );
- } else if ( confusionOthersDiv ) {
- confusionOthersDiv.classList.add(
- 'plugin-check-namer-hidden'
- );
- }
-
- if ( resultWrap ) {
- resultWrap.classList.remove(
- 'plugin-check-namer-hidden'
- );
- }
- } )
- .catch( function ( err ) {
- setText(
- errorEl,
- err && err.message
- ? err.message
- : pluginCheckNamer.messages.genericError
- );
-
- if ( errorDiv ) {
- errorDiv.classList.remove(
- 'plugin-check-namer-hidden'
- );
- }
- } )
- .finally( function () {
- setLoading( false );
- } );
- } );
- } );
-} )();
diff --git a/includes/Admin/Admin_AJAX.php b/includes/Admin/Admin_AJAX.php
index eef695078..cb5afad13 100644
--- a/includes/Admin/Admin_AJAX.php
+++ b/includes/Admin/Admin_AJAX.php
@@ -226,12 +226,13 @@ public function clean_up_environment() {
public function get_checks_to_run() {
$this->check_request_validity();
- $categories = filter_input( INPUT_POST, 'categories', FILTER_DEFAULT, FILTER_FORCE_ARRAY );
- $categories = is_null( $categories ) ? array() : $categories;
- $checks = filter_input( INPUT_POST, 'checks', FILTER_DEFAULT, FILTER_FORCE_ARRAY );
- $checks = is_null( $checks ) ? array() : $checks;
- $use_ai = 1 === filter_input( INPUT_POST, 'use-ai', FILTER_VALIDATE_INT );
- $runner = $this->get_ajax_runner();
+ $categories = filter_input( INPUT_POST, 'categories', FILTER_DEFAULT, FILTER_FORCE_ARRAY );
+ $categories = is_null( $categories ) ? array() : $categories;
+ $checks = filter_input( INPUT_POST, 'checks', FILTER_DEFAULT, FILTER_FORCE_ARRAY );
+ $checks = is_null( $checks ) ? array() : $checks;
+ $use_ai = 1 === filter_input( INPUT_POST, 'use-ai', FILTER_VALIDATE_INT );
+ $use_ai_name = 1 === filter_input( INPUT_POST, 'use-ai-name', FILTER_VALIDATE_INT );
+ $runner = $this->get_ajax_runner();
if ( is_wp_error( $runner ) ) {
wp_send_json_error( $runner, 500 );
@@ -241,6 +242,7 @@ public function get_checks_to_run() {
$this->configure_runner( $runner );
$runner->set_categories( $categories );
$runner->set_use_ai( $use_ai );
+ $runner->set_use_ai_name( $use_ai_name );
$plugin_basename = $runner->get_plugin_basename();
$checks_to_run = $runner->get_checks_to_run();
@@ -295,6 +297,7 @@ public function run_checks() {
$include_experimental = 1 === filter_input( INPUT_POST, 'include-experimental', FILTER_VALIDATE_INT );
$use_ai = 1 === filter_input( INPUT_POST, 'use-ai', FILTER_VALIDATE_INT );
+ $use_ai_name = 1 === filter_input( INPUT_POST, 'use-ai-name', FILTER_VALIDATE_INT );
$types = filter_input( INPUT_POST, 'types', FILTER_DEFAULT, FILTER_FORCE_ARRAY );
$types = is_null( $types ) ? array( 'error', 'warning' ) : $types;
@@ -303,6 +306,7 @@ public function run_checks() {
$runner->set_check_slugs( $checks );
$runner->set_plugin( $plugin );
$runner->set_use_ai( $use_ai );
+ $runner->set_use_ai_name( $use_ai_name );
$results = $runner->run();
} catch ( Exception $error ) {
wp_send_json_error(
diff --git a/includes/Admin/Namer_Page.php b/includes/Admin/Namer_Page.php
deleted file mode 100644
index cb4b0d415..000000000
--- a/includes/Admin/Namer_Page.php
+++ /dev/null
@@ -1,460 +0,0 @@
-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 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();
- ?>
-
- get_error_message() ); - ?> -
-- - - -
- - - - -- - -
-- - -
- - -- -
+
+ +
diff --git a/tests/phpunit/tests/Checker/Checks/AI_Name_Check_Tests.php b/tests/phpunit/tests/Checker/Checks/AI_Name_Check_Tests.php new file mode 100644 index 000000000..29be2abc0 --- /dev/null +++ b/tests/phpunit/tests/Checker/Checks/AI_Name_Check_Tests.php @@ -0,0 +1,32 @@ +createMock( AJAX_Runner::class ); + $runner->method( 'should_use_ai_name' )->willReturn( false ); + + $reflection = new \ReflectionClass( Plugin_Request_Utility::class ); + $property = $reflection->getProperty( 'runner' ); + $property->setAccessible( true ); + $property->setValue( null, $runner ); + + $check = new AI_Name_Check(); + $context = new Check_Context( WP_PLUGIN_CHECK_MAIN_FILE ); + $result = new Check_Result( $context ); + + $check->run( $result ); + + $this->assertEmpty( $result->get_errors() ); + $this->assertEmpty( $result->get_warnings() ); + + $property->setValue( null, null ); + } +}