Skip to content

Instantly share code, notes, and snippets.

@ideadude
Created October 28, 2023 16:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ideadude/024994004e4ba39529ff193f5b18f7c1 to your computer and use it in GitHub Desktop.
Save ideadude/024994004e4ba39529ff193f5b18f7c1 to your computer and use it in GitHub Desktop.
Script to tag and delete inactive users from WP/PMPro.
<?php
/**
* Tag inactive users.
* One time script. To run, visit /?delete_inactive_users=1 as an admin.
* An inactive user:
* - Has no logins within the last 2 years.
* - Has no paid orders.
* - Has no support tickets.
* ? Add inactive taxonomy to users? Then delete users in that taxonomy?
* Get users who are tagged for deletion:
* SELECT u.ID, u.user_email, u.user_registered
* FROM wp_users u
* LEFT JOIN wp_usermeta um ON u.ID = um.user_id
* AND um.meta_key = 'pmpro_inactive_user_to_delete'
* AND um.meta_value = 1;
*/
function my_tag_inactive_users() {
if ( !empty( $_REQUEST['tag_inactive_users'] ) && current_user_can( 'manage_options' ) ) {
global $wpdb;
if ( ! empty( $_REQUEST['limit'] ) ) {
$limit = intval( $_REQUEST['limit'] );
} else {
$limit = 10;
}
if ( ! empty( $_REQUEST['start'] ) ) {
$start = intval( $_REQUEST['start'] );
} else {
$start = 0;
}
if ( ! empty( $_REQUEST['delay'] ) ) {
$delay = intval( $_REQUEST['delay'] );
} else {
$delay = 5000;
}
if ( ! empty( $_REQUEST['auto'] ) ) {
$auto = 1;
} else {
$auto = 0;
}
echo "Starting with ID #" . $start . ". " . current_time( 'c' );
echo "<hr />";
$user_ids_to_check = $wpdb->get_col( "SELECT ID FROM $wpdb->users WHERE ID > $start ORDER BY ID LIMIT $limit" );
foreach( $user_ids_to_check as $user_id ) {
$user = get_userdata( $user_id );
echo "Checking #" . $user->ID . " " . esc_html( $user->user_email ) . ": ";
// Check if doesn't have subscriber role.
if ( ! in_array( 'subscriber', (array) $user->roles ) ) {
echo "User is not a subscriber. Skipping.<br />";
continue;
}
// Check for recent login.
$logins = pmpro_reports_get_values_for_user( 'logins', $user->ID );
if ( ! empty( $logins ) && ! empty( $logins['last'] ) && strtotime( $logins['last'] ) > strtotime( '-6 months' ) ) {
echo "User has logged in within the past 6 months. Skipping.<br />";
continue;
}
// Check for paid orders.
$paid_order = $wpdb->get_var( "SELECT id FROM $wpdb->pmpro_membership_orders WHERE user_id = " . $user->ID . " AND total > 0 LIMIT 1" );
if ( ! empty( $paid_order ) ) {
echo "User has a paid order. Skipping.<br />";
continue;
}
// Check membership levels. NOTE: Change these level IDs.
if ( pmpro_hasMembershipLevel( array( 20, 21, 6, 15, 16, 17, 18, 19 ), $user->ID ) ) {
echo "User has a premium membership level. Skipping.<br />";
continue;
}
// Check if is author of anything.
$posts = $wpdb->get_var( "SELECT ID FROM $wpdb->posts WHERE post_author = " . $user->ID . " LIMIT 1" );
if ( ! empty( $posts ) ) {
echo "User has authored something. Skipping.<br />";
continue;
}
// If we get here, tag them for deletion.
echo "Tagging for deletion.<br />";
wp_set_object_terms( $user->ID, 'inactive', 'my_user_activity' );
}
$new_start = $user->ID;
$reload_url = admin_url( '?tag_inactive_users=1&limit=' . $limit . "&start=" . $new_start . '&delay=' . $delay . '&auto=' . $auto );
if ( ! empty( $user_ids_to_check ) && ! empty( $_REQUEST['auto'] ) ) {
echo "<hr />";
echo "Loading " . $reload_url . " in " . $delay . " microseconds.";
?>
<script>
setTimeout( function() {
window.location.href = '<?php echo esc_url_raw( $reload_url );?>';
}, <?php echo $delay; ?> );
</script>
<?php
} else {
?>
<hr />
<p><a href="<?php echo esc_url_raw( $reload_url );?>">Click here to load the next batch.</a></p>
<?php
}
exit;
}
}
add_action( 'init', 'my_tag_inactive_users' );
/**
* Export users tagged as inactive.
*/
function my_export_inactive_users() {
global $wpdb;
if ( !empty( $_REQUEST['export_inactive_users'] ) && current_user_can( 'manage_options' ) ) {
function pmpro_enclose( $s ) {
return "\"" . str_replace( "\"", "\\\"", $s ) . "\"";
}
if ( ! empty( $_REQUEST['limit'] ) ) {
$limit = intval( $_REQUEST['limit'] );
} else {
$limit = 10000;
}
if ( ! empty( $_REQUEST['start'] ) ) {
$start = intval( $_REQUEST['start'] );
} else {
$start = 0;
}
$term = get_term_by( 'slug', 'inactive', 'my_user_activity' );
$sqlQuery = "SELECT u.* FROM $wpdb->term_relationships tr LEFT JOIN $wpdb->users u ON tr.object_id = u.ID WHERE tr.term_taxonomy_id = '" . $term->term_id . "' AND tr.object_id > $start ORDER BY tr.object_id LIMIT $limit";
$users = $wpdb->get_results( $sqlQuery );
echo "ID,user_login,user_nicename,user_email,user_url,user_registered,user_status,display_name\n";
foreach( $users as $user ) {
echo $user->ID . ','
. $user->user_login . ','
. $user->user_nicename . ','
. $user->user_email . ','
. $user->user_url . ','
. $user->user_registered .','
. $user->user_status . ','
. pmpro_enclose( $user->display_name )
. "\n";
}
exit;
}
}
add_action( 'init', 'my_export_inactive_users' );
/**
* Delete users tagged as inactive.
*/
function my_delete_inactive_users() {
global $wpdb;
if ( !empty( $_REQUEST['delete_inactive_users'] ) && current_user_can( 'manage_options' ) ) {
$term = get_term_by( 'slug', 'inactive', 'my_user_activity' );
if ( ! empty( $_REQUEST['limit'] ) ) {
$limit = intval( $_REQUEST['limit'] );
} else {
$limit = 10;
}
if ( ! empty( $_REQUEST['start'] ) ) {
$start = intval( $_REQUEST['start'] );
} else {
$start = 0;
}
if ( ! empty( $_REQUEST['delay'] ) ) {
$delay = intval( $_REQUEST['delay'] );
} else {
$delay = 5000;
}
if ( ! empty( $_REQUEST['auto'] ) ) {
$auto = 1;
} else {
$auto = 0;
}
if ( ! empty( $_REQUEST['delete'] ) ) {
$delete = 1;
} else {
$delete = 0;
}
$sqlQuery = "SELECT object_id FROM $wpdb->term_relationships WHERE term_taxonomy_id = '" . $term->term_id . "' AND object_id > $start ORDER BY object_id LIMIT $limit";
$user_ids_to_delete = $wpdb->get_col( $sqlQuery );
echo "<ul>";
foreach ( $user_ids_to_delete as $user_id ) {
$user = get_userdata( $user_id );
echo '<li>#' . $user_id . ' <a href="' . esc_url_raw( admin_url( 'user-edit.php?user_id=' . $user->ID ) ) . '">' . $user->display_name . ' (' . $user->user_email . ')' . '</a>';
if ( ! empty( $_REQUEST['delete'] ) ) {
// We are just deleting rows from these 3 tables. Should be quicker and avoid deleting
// PMPro orders or things we want. Those orders will be orphaned though.
// wp_delete_user( $user->ID );
$query1 = "DELETE FROM $wpdb->term_relationships WHERE term_taxonomy_id = '" . $term->term_id . "' AND object_id = '" . $user_id . "' LIMIT 1";
$r1 = $wpdb->query( $query1 );
$query2 = "DELETE FROM $wpdb->usermeta WHERE user_id = '" . $user->ID . "'";
$r2 = $wpdb->query( $query2 );
$query3 = "DELETE FROM $wpdb->users WHERE ID = '" . $user->ID . "' LIMIT 1";
$r3 = $wpdb->query( $query3 );
echo ' <strong>DELETED</strong>';
}
echo '</li>';
}
echo "</ul>";
$new_start = $user->ID;
$reload_url = admin_url( '?delete_inactive_users=1&limit=' . $limit . "&start=" . $new_start . '&delay=' . $delay . '&auto=' . $auto . '&delete=' . $delete );
if ( ! empty( $user_ids_to_delete ) && ! empty( $_REQUEST['auto'] ) ) {
echo "<hr />";
echo "Loading " . $reload_url . " in " . $delay . " microseconds seconds.";
?>
<script>
setTimeout( function() {
window.location.href = '<?php echo esc_url_raw( $reload_url );?>';
}, <?php echo $delay; ?> );
</script>
<?php
} else {
?>
<hr />
<p><a href="<?php echo esc_url_raw( $reload_url );?>">Click here to load the next batch.</a></p>
<?php
}
exit;
}
}
add_action( 'init', 'my_delete_inactive_users' );
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment