Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/changelog/2591-from-description
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: changed

Wrap blocked domains and keywords tables in collapsible details element.
19 changes: 19 additions & 0 deletions assets/css/activitypub-admin.css
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,25 @@ summary {
color: #2271b1;
}

.activitypub-site-block-details {
margin: 10px 0;
}

.activitypub-site-block-details summary {
padding: 8px 0;
color: inherit;
text-decoration: none;
}

.activitypub-site-block-details table {
max-width: 500px;
margin-top: 10px;
}

.activitypub-site-block-details td:last-child {
width: 80px;
}

.activitypub-settings-accordion {
border: 1px solid #c3c4c7;
}
Expand Down
232 changes: 139 additions & 93 deletions assets/js/activitypub-moderation-admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,39 @@
* ActivityPub Moderation Admin JavaScript
*/

(function( $ ) {
/* global activitypubModerationL10n, jQuery */

/**
* @param {Object} $ - jQuery
* @param {Object} wp - WordPress global object
* @param {Object} wp.i18n - Internationalization functions
* @param {Object} wp.a11y - Accessibility functions
* @param {Object} wp.ajax - AJAX functions
*/
(function( $, wp ) {
'use strict';

var __ = wp.i18n.__;
var _n = wp.i18n._n;
var sprintf = wp.i18n.sprintf;

/**
* Helper function to show a message using wp.a11y and alert
*
* @param {string} message - The message to display
*/
function showMessage( message ) {
if ( wp.a11y && wp.a11y.speak ) {
wp.a11y.speak( message, 'assertive' );
}
alert( message );
}

/**
* Helper function to validate domain format
*
* @param {string} domain - The domain to validate
* @return {boolean} Whether the domain is valid
*/
function isValidDomain( domain ) {
// Basic domain validation - must contain at least one dot and valid characters
Expand All @@ -16,6 +44,12 @@

/**
* Helper function to check if a term already exists in the UI
*
* @param {string} type - The type of block (domain or keyword)
* @param {string} value - The value to check
* @param {string} context - The context (user or site)
* @param {number|null} userId - The user ID (for user context)
* @return {boolean} Whether the term is already blocked
*/
function isTermAlreadyBlocked( type, value, context, userId ) {
var selector;
Expand All @@ -27,29 +61,110 @@
return $( selector ).length > 0;
}

/**
* Validate a blocked term value
*
* @param {string} type - The type of block (domain or keyword)
* @param {string} value - The value to validate
* @param {string} context - The context (user or site)
* @param {number|null} userId - The user ID (for user context)
* @return {boolean} Whether the value is valid
*/
function validateBlockedTerm( type, value, context, userId ) {
if ( ! value ) {
showMessage( __( 'Please enter a value to block.', 'activitypub' ) );
return false;
}

if ( type === 'domain' && ! isValidDomain( value ) ) {
showMessage( __( 'Please enter a valid domain (e.g., example.com).', 'activitypub' ) );
return false;
}

if ( isTermAlreadyBlocked( type, value, context, userId ) ) {
showMessage( __( 'This term is already blocked.', 'activitypub' ) );
return false;
}

return true;
}

/**
* Create a table row for a blocked term.
*
* @param {string} type - The type of block (domain or keyword)
* @param {string} value - The blocked value
* @param {string} context - The context (user or site)
* @return {jQuery} The constructed table row
*/
function createBlockedTermRow( type, value, context ) {
var $button = $( '<button>', {
type: 'button',
'class': 'button button-small remove-' + context + '-block-btn',
'data-type': type,
'data-value': value,
text: __( 'Remove', 'activitypub' )
} );

return $( '<tr>' ).append( $( '<td>' ).text( value ), $( '<td>' ).append( $button ) );
}

/**
* Helper function to add a blocked term to the UI
*/
function addBlockedTermToUI( type, value, context, userId ) {
var table;

if ( context === 'user' ) {
// For user moderation, add to the appropriate table
var container = $( '.activitypub-user-block-list[data-user-id="' + userId + '"]' );

var table = container.find( '.activitypub-blocked-' + type );
table = container.find( '.activitypub-blocked-' + type );
if ( table.length === 0 ) {
table = $( '<table class="widefat striped activitypub-blocked-' + type + '" role="presentation" style="max-width: 500px; margin: 15px 0;"><tbody></tbody></table>' );
container.find( '#new_user_' + type ).closest( '.add-user-block-form' ).before( table );
}
table.append( '<tr><td>' + value + '</td><td style="width: 80px;"><button type="button" class="button button-small remove-user-block-btn" data-type="' + type + '" data-value="' + value + '">Remove</button></td></tr>' );
table.find( 'tbody' ).append( createBlockedTermRow( type, value, context ) );
} else if ( context === 'site' ) {
// For site moderation, add to the appropriate table
var container = $( '#new_site_' + type ).closest( '.activitypub-site-block-list' );
var table = container.find( '.activitypub-site-blocked-' + type );
// For site moderation, add to the table inside the details element
var details = $( '.activitypub-site-block-details[data-type="' + type + '"]' );
table = details.find( '.activitypub-site-blocked-' + type );

if ( table.length === 0 ) {
table = $( '<table class="widefat striped activitypub-site-blocked-' + type + '" role="presentation" style="max-width: 500px; margin: 15px 0;"><tbody></tbody></table>' );
container.find( '.add-site-block-form' ).before( table );
// Create table inside the details element (after summary)
table = $( '<table class="widefat striped activitypub-site-blocked-' + type + '" role="presentation"><tbody></tbody></table>' );
details.find( 'summary' ).after( table );
}
table.append( '<tr><td>' + value + '</td><td style="width: 80px;"><button type="button" class="button button-small remove-site-block-btn" data-type="' + type + '" data-value="' + value + '">Remove</button></td></tr>' );

table.find( 'tbody' ).append( createBlockedTermRow( type, value, context ) );

updateSiteBlockSummary( type );
}
}

/**
* Helper function to update the site block summary count
*/
function updateSiteBlockSummary( type ) {
var details = $( '.activitypub-site-block-details[data-type="' + type + '"]' );
var table = details.find( '.activitypub-site-blocked-' + type );
var count = table.find( 'tbody tr' ).length || table.find( 'tr' ).length;
var summary = details.find( 'summary' );

if ( count === 0 ) {
// Empty state
var emptyText = type === 'domain'
? __( 'No blocked domains', 'activitypub' )
: __( 'No blocked keywords', 'activitypub' );
summary.text( emptyText );
details.attr( 'open', '' );
table.remove();
} else {
// Has items - use _n for proper pluralization
var text = type === 'domain'
? _n( '%s blocked domain', '%s blocked domains', count, 'activitypub' )
: _n( '%s blocked keyword', '%s blocked keywords', count, 'activitypub' );
summary.text( sprintf( text, count ) );
}
}

Expand All @@ -63,13 +178,11 @@

if ( button.length > 0 ) {
// Remove the parent table row
var parent = button.closest( 'tr' );
var container = parent.closest( 'table' );
parent.remove();
button.closest( 'tr' ).remove();

// If the container is now empty, remove it
if ( container.find( 'tr' ).length === 0 ) {
container.remove();
// Update the summary count for site blocks
if ( context === 'site' ) {
updateSiteBlockSummary( type );
}
}
}
Expand All @@ -94,33 +207,7 @@
var input = $( '#new_user_' + type );
var value = input.val().trim();

if ( ! value ) {
// Use wp.a11y.speak for better accessibility.
if ( wp.a11y && wp.a11y.speak ) {
wp.a11y.speak( activitypubModerationL10n.enterValue, 'assertive' );
} else {
alert( activitypubModerationL10n.enterValue );
}
return;
}

// Validate domain format if this is a domain block
if ( type === 'domain' && ! isValidDomain( value ) ) {
var message = activitypubModerationL10n.invalidDomain || 'Please enter a valid domain (e.g., example.com).';
if ( wp.a11y && wp.a11y.speak ) {
wp.a11y.speak( message, 'assertive' );
}
alert( message );
return;
}

// Check if the term is already blocked
if ( isTermAlreadyBlocked( type, value, 'user', userId ) ) {
var message = activitypubModerationL10n.alreadyBlocked || 'This term is already blocked.';
if ( wp.a11y && wp.a11y.speak ) {
wp.a11y.speak( message, 'assertive' );
}
alert( message );
if ( ! validateBlockedTerm( type, value, 'user', userId ) ) {
return;
}

Expand All @@ -136,12 +223,8 @@
input.val( '' );
addBlockedTermToUI( type, value, 'user', userId );
}).fail( function( response ) {
var message = response && response.message ? response.message : activitypubModerationL10n.addBlockFailed;
if ( wp.a11y && wp.a11y.speak ) {
wp.a11y.speak( message, 'assertive' );
} else {
alert( message );
}
var message = response && response.message ? response.message : __( 'Failed to add block.', 'activitypub' );
showMessage( message );
});
}

Expand All @@ -157,12 +240,8 @@
}).done( function() {
removeBlockedTermFromUI( type, value, 'user' );
}).fail( function( response ) {
var message = response && response.message ? response.message : activitypubModerationL10n.removeBlockFailed;
if ( wp.a11y && wp.a11y.speak ) {
wp.a11y.speak( message, 'assertive' );
} else {
alert( message );
}
var message = response && response.message ? response.message : __( 'Failed to remove block.', 'activitypub' );
showMessage( message );
});
}

Expand Down Expand Up @@ -204,32 +283,7 @@
var input = $( '#new_site_' + type );
var value = input.val().trim();

if ( ! value ) {
if ( wp.a11y && wp.a11y.speak ) {
wp.a11y.speak( activitypubModerationL10n.enterValue, 'assertive' );
} else {
alert( activitypubModerationL10n.enterValue );
}
return;
}

// Validate domain format if this is a domain block
if ( type === 'domain' && ! isValidDomain( value ) ) {
var message = activitypubModerationL10n.invalidDomain || 'Please enter a valid domain (e.g., example.com).';
if ( wp.a11y && wp.a11y.speak ) {
wp.a11y.speak( message, 'assertive' );
}
alert( message );
return;
}

// Check if the term is already blocked
if ( isTermAlreadyBlocked( type, value, 'site' ) ) {
var message = activitypubModerationL10n.alreadyBlocked || 'This term is already blocked.';
if ( wp.a11y && wp.a11y.speak ) {
wp.a11y.speak( message, 'assertive' );
}
alert( message );
if ( ! validateBlockedTerm( type, value, 'site', null ) ) {
return;
}

Expand All @@ -244,12 +298,8 @@
input.val( '' );
addBlockedTermToUI( type, value, 'site' );
}).fail( function( response ) {
var message = response && response.message ? response.message : activitypubModerationL10n.addBlockFailed;
if ( wp.a11y && wp.a11y.speak ) {
wp.a11y.speak( message, 'assertive' );
} else {
alert( message );
}
var message = response && response.message ? response.message : __( 'Failed to add block.', 'activitypub' );
showMessage( message );
});
}

Expand All @@ -264,12 +314,8 @@
}).done( function() {
removeBlockedTermFromUI( type, value, 'site' );
}).fail( function( response ) {
var message = response && response.message ? response.message : activitypubModerationL10n.removeBlockFailed;
if ( wp.a11y && wp.a11y.speak ) {
wp.a11y.speak( message, 'assertive' );
} else {
alert( message );
}
var message = response && response.message ? response.message : __( 'Failed to remove block.', 'activitypub' );
showMessage( message );
});
}

Expand Down Expand Up @@ -302,4 +348,4 @@
// Initialize when document is ready.
$( document ).ready( init );

})( jQuery );
})( jQuery, wp );
15 changes: 8 additions & 7 deletions includes/wp-admin/class-admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -342,22 +342,23 @@ public static function enqueue_moderation_scripts() {
\wp_enqueue_script(
'activitypub-moderation-admin',
ACTIVITYPUB_PLUGIN_URL . 'assets/js/activitypub-moderation-admin.js',
array( 'jquery', 'wp-util', 'wp-a11y' ),
array( 'jquery', 'wp-util', 'wp-a11y', 'wp-i18n' ),
ACTIVITYPUB_PLUGIN_VERSION,
true
);

\wp_set_script_translations(
'activitypub-moderation-admin',
'activitypub',
ACTIVITYPUB_PLUGIN_DIR . 'languages'
);

// Localize script with translations and nonces.
\wp_localize_script(
'activitypub-moderation-admin',
'activitypubModerationL10n',
array(
'enterValue' => \__( 'Please enter a value to block.', 'activitypub' ),
'addBlockFailed' => \__( 'Failed to add block.', 'activitypub' ),
'removeBlockFailed' => \__( 'Failed to remove block.', 'activitypub' ),
'alreadyBlocked' => \__( 'This term is already blocked.', 'activitypub' ),
'invalidDomain' => \__( 'Please enter a valid domain (e.g., example.com).', 'activitypub' ),
'nonce' => \wp_create_nonce( 'activitypub_moderation_settings' ),
'nonce' => \wp_create_nonce( 'activitypub_moderation_settings' ),
)
);
}
Expand Down
Loading