Skip to content

Commit d8dc081

Browse files
authored
Merge pull request #2 from norcross/feature/bulk-admin-ui
adding admin UI for running bulk update
2 parents 837f01f + cd0762e commit d8dc081

File tree

10 files changed

+349
-37
lines changed

10 files changed

+349
-37
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11

22
#### Version 1.0.0 - 2024/11/12
33
* initial release
4+
5+
#### Version 1.1.0 - 2024/11/18
6+
* adding admin UI button for running bulk update

README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,9 @@ Make sure no real IP addresses are stored in WP comments.
1010

1111
### Features
1212

13-
* a CLI command to bulk convert existing comment IP addresses to the masked
13+
* a CLI command and admin UI to bulk convert existing comment IP addresses to the masked
1414
* auto-filtering of new comments to use masked IP.
1515

1616
### To Do
1717

18-
* add admin UI piece to bulk convert existing comments
1918
* add whitelisting of IPs

includes/activate.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,5 @@ function activate() {
2323

2424
// Include our action so that we may add to this later.
2525
do_action( Core\HOOK_PREFIX . 'activate_process' );
26-
27-
// And flush our rewrite rules.
28-
flush_rewrite_rules();
2926
}
3027
register_activation_hook( Core\FILE, __NAMESPACE__ . '\activate' );

includes/bulk-process.php

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
<?php
2+
/**
3+
* The specific functions for the bulk action.
4+
*
5+
* @package ScrubCommentAuthorIP
6+
*/
7+
8+
// Call our namepsace.
9+
namespace Norcross\ScrubCommentAuthorIP\BulkProcess;
10+
11+
// Set our alias items.
12+
use Norcross\ScrubCommentAuthorIP as Core;
13+
use Norcross\ScrubCommentAuthorIP\Helpers as Helpers;
14+
use Norcross\ScrubCommentAuthorIP\Database as Database;
15+
16+
/**
17+
* Start our engines.
18+
*/
19+
add_action( 'admin_init', __NAMESPACE__ . '\run_admin_bulk_request' );
20+
add_action( 'admin_notices', __NAMESPACE__ . '\admin_bulk_action_notice' );
21+
add_filter( 'removable_query_args', __NAMESPACE__ . '\admin_removable_args' );
22+
23+
/**
24+
* Check for the bulk request coming from the admin.
25+
*
26+
* @return void
27+
*/
28+
function run_admin_bulk_request() {
29+
30+
// Confirm we requested this action.
31+
$confirm_action = filter_input( INPUT_GET, 'ip-scrub-run-bulk', FILTER_SANITIZE_SPECIAL_CHARS ); // phpcs:ignore -- the nonce check is happening after this.
32+
33+
// Make sure it is what we want.
34+
if ( empty( $confirm_action ) || 'yes' !== $confirm_action ) {
35+
return;
36+
}
37+
38+
// Make sure we have a nonce.
39+
$confirm_nonce = filter_input( INPUT_GET, 'ip-scrub-nonce', FILTER_SANITIZE_SPECIAL_CHARS ); // phpcs:ignore -- the nonce check is happening after this.
40+
41+
// Handle the nonce check.
42+
if ( empty( $confirm_nonce ) || ! wp_verify_nonce( $confirm_nonce, 'ip_scrub_bulk' ) ) {
43+
44+
// Let them know they had a failure.
45+
wp_die( esc_html__( 'There was an error validating the nonce.', 'scrub-comment-author-ip' ), esc_html__( 'Scrub Comment IP Bulk Action', 'scrub-comment-author-ip' ), [ 'back_link' => true ] );
46+
}
47+
48+
// Now get the IDs of the comments we have.
49+
$fetch_comments = Database\get_ids_for_update();
50+
51+
// If none exist, say so.
52+
if ( empty( $fetch_comments ) ) {
53+
54+
// Now set my redirect link.
55+
$redirect_link = Helpers\fetch_settings_url( ['ip-scrub-bulk-error' => 'no-comments', 'ip-scrub-bulk-success' => 'no'] );
56+
57+
// Do the redirect.
58+
wp_safe_redirect( $redirect_link );
59+
exit;
60+
}
61+
62+
// Handle the WP_Error return on it's own.
63+
if ( is_wp_error( $fetch_comments ) ) {
64+
65+
// Now set my redirect link.
66+
$redirect_link = Helpers\fetch_settings_url( ['ip-scrub-bulk-error' => 'query-error', 'ip-scrub-bulk-success' => 'no'] );
67+
68+
// Do the redirect.
69+
wp_safe_redirect( $redirect_link );
70+
exit;
71+
}
72+
73+
// Attempt the update.
74+
$attempt_update = Database\replace_batch_comment_ips( $fetch_comments );
75+
76+
// Bail if the update did.
77+
if ( empty( $attempt_update ) || is_wp_error( $attempt_update ) ) {
78+
79+
// Now set my redirect link.
80+
$redirect_link = Helpers\fetch_settings_url( ['ip-scrub-bulk-error' => 'update-error', 'ip-scrub-bulk-success' => 'no'] );
81+
82+
// Do the redirect.
83+
wp_safe_redirect( $redirect_link );
84+
exit;
85+
}
86+
87+
// Now set my redirect link.
88+
$redirect_link = Helpers\fetch_settings_url( ['ip-scrub-bulk-count' => count( $fetch_comments ), 'ip-scrub-bulk-success' => 'yes'] );
89+
90+
// Do the redirect.
91+
wp_safe_redirect( $redirect_link );
92+
exit;
93+
}
94+
95+
/**
96+
* Check for the result of bulk action.
97+
*
98+
* @return void
99+
*/
100+
function admin_bulk_action_notice() {
101+
102+
// Confirm we requested this action.
103+
$confirm_result = filter_input( INPUT_GET, 'ip-scrub-bulk-success', FILTER_SANITIZE_SPECIAL_CHARS ); // phpcs:ignore -- no need for a nonce check.
104+
105+
// Make sure it is what we want.
106+
if ( empty( $confirm_result ) ) {
107+
return;
108+
}
109+
110+
// Handle the success first.
111+
if ( 'yes' === $confirm_result ) {
112+
113+
// Set the counts.
114+
$set_counts = filter_input( INPUT_GET, 'ip-scrub-bulk-count', FILTER_SANITIZE_NUMBER_INT );
115+
116+
// Set our notice text.
117+
$set_notice = sprintf( _n( 'Success! %d comment was updated.', 'Success! %d comments were updated.', absint( $set_counts ), 'scrub-comment-author-ip' ), absint( $set_counts ) );
118+
119+
// Set the wrapper around it.
120+
echo '<div class="notice notice-success is-dismissible">';
121+
122+
// Display the actual message.
123+
echo '<p><strong>' . wp_kses_post( $set_notice ) . '</strong></p>';
124+
125+
// Close the wrapper.
126+
echo '</div>';
127+
128+
// And be done.
129+
return;
130+
}
131+
132+
// Handle the errors now.
133+
$set_error = filter_input( INPUT_GET, 'ip-scrub-bulk-error', FILTER_SANITIZE_SPECIAL_CHARS );
134+
135+
// If we have no comments, show a warning.
136+
if ( 'no-comments' === $set_error ) {
137+
138+
// Set the notice text.
139+
$set_notice = __( 'There are no comments requiring update at this time.', 'scrub-comment-author-ip' );
140+
141+
// Set the wrapper around it.
142+
echo '<div class="notice notice-warning is-dismissible">';
143+
144+
// Display the actual message.
145+
echo '<p><strong>' . wp_kses_post( $set_notice ) . '</strong></p>';
146+
147+
// Close the wrapper.
148+
echo '</div>';
149+
150+
// And finish.
151+
return;
152+
}
153+
154+
// Handle the rest of the possible error messages.
155+
switch ( $set_error ) {
156+
157+
case 'query-error' :
158+
$set_notice = __( 'There was an error attempting to retrieve the comments. Please check your error logs.', 'scrub-comment-author-ip' );
159+
break;
160+
161+
case 'update-error' :
162+
$set_notice = __( 'There was an error attempting to update the comments. Please check your error logs.', 'scrub-comment-author-ip' );
163+
break;
164+
165+
default :
166+
$set_notice = __( 'There was an unknown error. Please check your error logs.', 'scrub-comment-author-ip' );
167+
break;
168+
}
169+
170+
// Set the wrapper around it.
171+
echo '<div class="notice notice-error is-dismissible">';
172+
173+
// Display the actual message.
174+
echo '<p><strong>' . wp_kses_post( $set_notice ) . '</strong></p>';
175+
176+
// Close the wrapper.
177+
echo '</div>';
178+
179+
// Nothing left to display.
180+
}
181+
182+
/**
183+
* Add our custom strings to the vars.
184+
*
185+
* @param array $args The existing array of args.
186+
*
187+
* @return array $args The modified array of args.
188+
*/
189+
function admin_removable_args( $args ) {
190+
191+
// Set an array of the args we wanna exclude.
192+
$remove = [
193+
'ip-scrub-bulk-error',
194+
'ip-scrub-bulk-count',
195+
'ip-scrub-bulk-success',
196+
];
197+
198+
// Include my new args and return.
199+
return wp_parse_args( $remove, $args );
200+
}

includes/database.php

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,38 @@
1515
// And pull in any other namespaces.
1616
use WP_Error;
1717

18+
/**
19+
* Just get a simple count.
20+
*
21+
* @return array
22+
*/
23+
function get_count_for_update() {
24+
25+
// Fetch the masked IP.
26+
$masked_ip = Helpers\fetch_masked_ip();
27+
28+
// Call the global class.
29+
global $wpdb;
30+
31+
// Set up our query.
32+
$query_args = $wpdb->prepare("
33+
SELECT COUNT(*)
34+
FROM $wpdb->comments
35+
WHERE comment_author_IP NOT LIKE '%s'
36+
", esc_attr( $masked_ip ) );
37+
38+
// Process the query.
39+
$query_run = $wpdb->get_var( $query_args ); // phpcs:ignore -- we are skipping the overhead of get_comments.
40+
41+
// Throw the error if we have one.
42+
if ( is_wp_error( $query_run ) ) {
43+
return new WP_Error( $query_run->get_error_code(), $query_run->get_error_message() );
44+
}
45+
46+
// Return the count, whatever it may be.
47+
return absint( $query_run );
48+
}
49+
1850
/**
1951
* Get all my comment IDs.
2052
*
@@ -28,18 +60,15 @@ function get_ids_for_update() {
2860
// Call the global class.
2961
global $wpdb;
3062

31-
// Set my table name.
32-
$table_name = $wpdb->prefix . 'comments';
33-
3463
// Set up our query.
3564
$query_args = $wpdb->prepare("
3665
SELECT comment_ID
37-
FROM $table_name
66+
FROM $wpdb->comments
3867
WHERE comment_author_IP NOT LIKE '%s'
3968
", esc_attr( $masked_ip ) );
4069

4170
// Process the query.
42-
$query_run = $wpdb->get_col( $query_args );
71+
$query_run = $wpdb->get_col( $query_args ); // phpcs:ignore -- we are skipping the overhead of get_comments.
4372

4473
// Throw the error if we have one.
4574
if ( is_wp_error( $query_run ) ) {
@@ -84,12 +113,9 @@ function replace_single_comment_ip( $comment_id = 0, $masked_ip = '' ) {
84113
// Call the global class.
85114
global $wpdb;
86115

87-
// Set my table name.
88-
$table_name = $wpdb->prefix . 'comments';
89-
90116
// Run the actual DB update.
91-
$update_row = $wpdb->update(
92-
$table_name,
117+
$update_row = $wpdb->update( // phpcs:ignore -- we dont want to trigger anything else here.
118+
$wpdb->comments,
93119
[ 'comment_author_IP' => $set_new_ip ],
94120
[ 'comment_ID' => absint( $comment_id ) ],
95121
[ '%s' ],
@@ -112,7 +138,7 @@ function replace_single_comment_ip( $comment_id = 0, $masked_ip = '' ) {
112138
*
113139
* @return mixed
114140
*/
115-
function replace_batch_comment_ips( $comment_ids = array() ) {
141+
function replace_batch_comment_ips( $comment_ids = [] ) {
116142

117143
// Bail if no comment IDs were provided.
118144
if ( empty( $comment_ids ) ) {
@@ -133,15 +159,12 @@ function replace_batch_comment_ips( $comment_ids = array() ) {
133159
// Call the global class.
134160
global $wpdb;
135161

136-
// Set my table name.
137-
$table_name = $wpdb->prefix . 'comments';
138-
139162
// Now loop the IDs and run the update.
140163
foreach ( $comment_ids as $comment_id ) {
141164

142165
// Run the actual DB update.
143-
$update_row = $wpdb->update(
144-
$table_name,
166+
$update_row = $wpdb->update( // phpcs:ignore -- we dont want to trigger anything else here.
167+
$wpdb->comments,
145168
[ 'comment_author_IP' => $masked_ip ],
146169
[ 'comment_ID' => absint( $comment_id ) ],
147170
[ '%s' ],

includes/deactivate.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,5 @@ function deactivate() {
2020

2121
// Include our action so that we may add to this later.
2222
do_action( Core\HOOK_PREFIX . 'deactivate_process' );
23-
24-
// And flush our rewrite rules.
25-
flush_rewrite_rules();
2623
}
2724
register_deactivation_hook( Core\FILE, __NAMESPACE__ . '\deactivate' );

includes/helpers.php

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ function maybe_scrub_enabled( $return_type = 'string' ) {
3636
// Check for the stored "yes" to return.
3737
return ! empty( $set_option ) && 'yes' === sanitize_text_field( $set_option ) ? true : false;
3838

39-
// And break.
39+
// Done.
4040
break;
4141

4242
// Handle my yes / no string return.
@@ -45,7 +45,7 @@ function maybe_scrub_enabled( $return_type = 'string' ) {
4545
// Check for the stored "yes" to return.
4646
return ! empty( $set_option ) && 'yes' === sanitize_text_field( $set_option ) ? 'yes' : 'no';
4747

48-
// And break.
48+
// Done.
4949
break;
5050
}
5151

@@ -66,3 +66,21 @@ function fetch_masked_ip() {
6666
// Confirm it's a valid IP before returning it.
6767
return ! empty( $masked_ip ) && filter_var( $masked_ip, FILTER_VALIDATE_IP ) ? $masked_ip : '127.0.0.1';
6868
}
69+
70+
/**
71+
* Get the URL for our settings page with any custom args.
72+
*
73+
* @param array $args The possible array of args.
74+
*
75+
* @return string
76+
*/
77+
function fetch_settings_url( $args = [] ) {
78+
79+
// If we have no args, just do the basic link.
80+
if ( empty( $args ) ) {
81+
return admin_url( 'options-discussion.php' );
82+
}
83+
84+
// Now return it in args.
85+
return add_query_arg( $args, admin_url( 'options-discussion.php' ) );
86+
}

0 commit comments

Comments
 (0)