Skip to content
129 changes: 125 additions & 4 deletions assets/js/plugin-check-admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,46 @@
return false;
}

/**
* Counts the total number of errors and warnings in the aggregated results.
*
* @since 1.9.0
*
* @return {Object} Object with errorCount and warningCount properties.
*/
function countResults() {
let errorCount = 0;
let warningCount = 0;

// Count errors.
for ( const file of Object.keys( aggregatedResults.errors ) ) {
const lines = aggregatedResults.errors[ file ] || {};

for ( const line of Object.keys( lines ) ) {
const columns = lines[ line ] || {};

for ( const column of Object.keys( columns ) ) {
errorCount += ( columns[ column ] || [] ).length;
}
}
}

// Count warnings.
for ( const file of Object.keys( aggregatedResults.warnings ) ) {
const lines = aggregatedResults.warnings[ file ] || {};

for ( const line of Object.keys( lines ) ) {
const columns = lines[ line ] || {};

for ( const column of Object.keys( columns ) ) {
warningCount += ( columns[ column ] || [] ).length;
}
}
}

return { errorCount, warningCount };
}

function defaultString( key ) {
if (
pluginCheck.strings &&
Expand Down Expand Up @@ -544,10 +584,91 @@
* @param {boolean} isSuccessMessage Whether the message is a success message.
*/
function renderResultsMessage( isSuccessMessage ) {
const messageType = isSuccessMessage ? 'success' : 'error';
const messageText = isSuccessMessage
? pluginCheck.successMessage
: pluginCheck.errorMessage;
// Count errors and warnings to determine notice severity and compose the message.
const { errorCount, warningCount } = isSuccessMessage
? { errorCount: 0, warningCount: 0 }
: countResults();

// Derive notice type from actual counts: errors → error, warnings-only → warning, none → success.
let messageType;
if ( errorCount > 0 ) {
messageType = 'error';
} else if ( warningCount > 0 ) {
messageType = 'warning';
} else {
messageType = 'success';
}

let messageText;

if ( isSuccessMessage ) {
messageText = pluginCheck.successMessage;
} else {
Comment on lines 586 to +606
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

messageType is derived only from isSuccessMessage, so a warnings-only run will still render notice-error even when errorCount is 0 (but warningCount > 0). Consider deriving the notice type from the counted totals (e.g., success when both 0, warning when only warnings, error when errors > 0) so the notice styling matches the actual result severity.

Copilot uses AI. Check for mistakes.
/**
* Substitutes printf-style placeholders in a translated string.
* Handles both simple (%d, %s) and positional (%1$d, %2$s) placeholders.
*
* @param {string} template The translated format string.
* @param {...string} args Replacement values.
* @return {string} Formatted string with placeholders replaced.
*/
function sprintfReplace( template, ...args ) {
let i = 0;
return template.replace(
/%(\d+\$)?[ds]/g,
function ( _match, pos ) {
const index = pos ? parseInt( pos, 10 ) - 1 : i++;
return args[ index ] !== undefined
? args[ index ]
: _match;
}
);
}

// Build the individual count parts with proper plural/singular forms.
let errorPart = '';
if ( errorCount > 0 ) {
errorPart = sprintfReplace(
errorCount === 1
? pluginCheck.errorString
: pluginCheck.errorsString,
errorCount
);
}

let warningPart = '';
if ( warningCount > 0 ) {
warningPart = sprintfReplace(
warningCount === 1
? pluginCheck.warningString
: pluginCheck.warningsString,
warningCount
);
}

// Assemble the final sentence from fully translatable PHP-provided templates
// so that word order and connector phrases can be adapted for all languages.
if ( errorPart && warningPart ) {
messageText = sprintfReplace(
pluginCheck.summaryBothTemplate,
errorPart,
warningPart
);
} else if ( errorPart ) {
messageText = sprintfReplace(
pluginCheck.summarySingleTemplate,
errorPart
);
} else if ( warningPart ) {
messageText = sprintfReplace(
pluginCheck.summarySingleTemplate,
warningPart
);
} else {
// Fallback to default message if somehow no errors/warnings.
messageText = pluginCheck.errorMessage;
}
}

resultsContainer.innerHTML =
renderTemplate( 'plugin-check-results-complete', {
Expand Down
12 changes: 12 additions & 0 deletions includes/Admin/Admin_Page.php
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,18 @@ public function enqueue_scripts() {
'actionExportResults' => Admin_AJAX::ACTION_EXPORT_RESULTS,
'successMessage' => __( 'No errors found.', 'plugin-check' ),
'errorMessage' => __( 'Errors were found.', 'plugin-check' ),
/* translators: %d: Number of errors found. */
'errorString' => __( '%d error', 'plugin-check' ),
/* translators: %d: Number of errors found. */
'errorsString' => __( '%d errors', 'plugin-check' ),
/* translators: %d: Number of warnings found. */
'warningString' => __( '%d warning', 'plugin-check' ),
/* translators: %d: Number of warnings found. */
'warningsString' => __( '%d warnings', 'plugin-check' ),
/* translators: 1: Formatted error count string (e.g. "3 errors"), 2: Formatted warning count string (e.g. "2 warnings"). */
'summaryBothTemplate' => __( '%1$s and %2$s found.', 'plugin-check' ),
/* translators: %s: Formatted issue count string (e.g. "3 errors" or "2 warnings"). */
'summarySingleTemplate' => __( '%s found.', 'plugin-check' ),
'strings' => array(
'exportCsv' => __( 'Export CSV', 'plugin-check' ),
'exportJson' => __( 'Export JSON', 'plugin-check' ),
Expand Down
Loading