Replies
Gentle Reminder sir,
<?php /** * Plugin Name: BuddyPress Notifications Management Tool * Description: Manually or automatically delete BuddyPress user notifications based on age or schedule. * Version: 2.3.2 * License: GPLv2 or later * Requires at least: 5.6 * Requires PHP: 7.4 * Requires Plugins: buddypress * Text Domain: bp-notifications-cleanup */ defined( 'ABSPATH' ) || exit; class BP_Delete_All_Notifications_Auto { private $opt_schedule = 'bp_delete_notifications_schedule'; private $opt_days = 'bp_delete_notifications_days'; const CRON_HOOK = 'bp_delete_all_notifications_cron'; const CHUNK_SIZE = 1000; const NONCE_ACTION = 'delete_all_bp_notifications_action'; const NONCE_NAME = 'delete_all_bp_notifications_nonce'; public function __construct() { add_action( 'admin_menu', array( $this, 'add_admin_menu' ) ); add_action( 'admin_post_delete_all_bp_notifications', array( $this, 'handle_delete_notifications' ) ); add_action( 'admin_notices', array( $this, 'admin_notices' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_confirmation_script' ) ); add_action( 'admin_init', array( $this, 'register_settings' ) ); add_action( self::CRON_HOOK, array( $this, 'cron_cleanup' ) ); add_filter( 'cron_schedules', array( $this, 'filter_cron_schedules' ) ); register_activation_hook( __FILE__, array( $this, 'activate' ) ); register_deactivation_hook( __FILE__, array( $this, 'deactivate' ) ); register_uninstall_hook( __FILE__, array( __CLASS__, 'uninstall' ) ); $this->maybe_schedule_cron(); // BuddyPress dependency notice add_action( 'admin_notices', array( $this, 'buddypress_missing_notice' ) ); } public function add_admin_menu() { if ( ! current_user_can( 'manage_options' ) ) { return; } // Only show if notifications component is active if ( ! $this->is_notifications_component_active() ) { return; } add_submenu_page( 'tools.php', __( 'BP Notification Cleanup', 'bp-notifications-cleanup' ), __( 'BP Notification Cleanup', 'bp-notifications-cleanup' ), 'manage_options', 'delete-bp-notifications', array( $this, 'admin_page_content' ) ); } public function admin_page_content() { if ( ! current_user_can( 'manage_options' ) ) { return; } $schedule = get_option( $this->opt_schedule, 'none' ); $days = get_option( $this->opt_days, 30 ); ?> <div class="wrap"> <h1><?php esc_html_e( 'BuddyPress Notification Cleanup', 'bp-notifications-cleanup' ); ?></h1> <?php if ( ! $this->is_notifications_component_active() ) : ?> <div class="notice notice-error"> <p><?php esc_html_e( 'BuddyPress Notifications component is not active.', 'bp-notifications-cleanup' ); ?></p> </div> <?php else : ?> <div class="card"> <h2><?php esc_html_e( 'Manual Delete', 'bp-notifications-cleanup' ); ?></h2> <p><?php esc_html_e( 'This will permanently delete all notifications for all users.', 'bp-notifications-cleanup' ); ?></p> <p><?php esc_html_e( 'Total Notifications:', 'bp-notifications-cleanup' ); ?> <strong><?php echo esc_html( number_format_i18n( $this->get_notifications_count() ) ); ?></strong> </p> <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" id="bp-delete-notifications-form"> <?php wp_nonce_field( self::NONCE_ACTION, self::NONCE_NAME ); ?> <input type="hidden" name="action" value="delete_all_bp_notifications"> <p> <input type="submit" class="button button-danger" value="<?php esc_attr_e( 'Delete All Notifications', 'bp-notifications-cleanup' ); ?>"> </p> </form> </div> <hr> <div class="card"> <h2><?php esc_html_e( 'Auto Cleanup Settings', 'bp-notifications-cleanup' ); ?></h2> <form method="post" action="options.php"> <?php settings_fields( 'bp_notifications_cleanup_settings' ); do_settings_sections( 'bp_notifications_cleanup' ); submit_button( __( 'Save Settings', 'bp-notifications-cleanup' ) ); ?> </form> <p> <?php esc_html_e( 'Schedule:', 'bp-notifications-cleanup' ); ?> <strong><?php echo esc_html( ucfirst( $schedule ) ); ?></strong> </p> <p> <?php esc_html_e( 'Retention Days:', 'bp-notifications-cleanup' ); ?> <strong><?php echo esc_html( $days ); ?></strong> (<?php esc_html_e( 'Notifications older than this will be deleted.', 'bp-notifications-cleanup' ); ?>) </p> <?php if ( 'none' !== $schedule ) : ?> <p> <?php esc_html_e( 'Next scheduled cleanup:', 'bp-notifications-cleanup' ); ?> <strong> <?php $next = wp_next_scheduled( self::CRON_HOOK ); if ($next) { $format = get_option('date_format') . ' ' . get_option('time_format'); $timestamp = $next + (get_option('gmt_offset') * HOUR_IN_SECONDS); echo esc_html(date_i18n($format, $timestamp)); } else { esc_html_e('Not scheduled', 'bp-notifications-cleanup'); } ?> </strong> </p> <?php endif; ?> </div> <?php endif; ?> </div> <?php } public function handle_delete_notifications() { if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'Permission denied', 'bp-notifications-cleanup' ) ); } check_admin_referer( self::NONCE_ACTION, self::NONCE_NAME ); $nonce = wp_create_nonce( 'notifications_deleted' ); $redirect = admin_url( 'tools.php?page=delete-bp-notifications' ); try { $this->perform_chunked_deletion(); $redirect = add_query_arg( array( 'deleted' => 1, '_wpnonce' => $nonce ), $redirect ); } catch ( Exception $e ) { error_log( 'BP Notification Cleanup Error: ' . $e->getMessage() ); $redirect = add_query_arg( array( 'error' => 1 ), $redirect ); } wp_safe_redirect( $redirect ); exit; } private function perform_chunked_deletion() { global $wpdb; $table = $wpdb->prefix . 'bp_notifications'; if ( ! $this->table_exists( $table ) ) { return; } $continue = true; $chunk_size = self::CHUNK_SIZE; while ( $continue ) { $query = "DELETE FROM <code>" . esc_sql( $table ) . "</code> LIMIT {$chunk_size}"; $rows_affected = $wpdb->query( $query ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching if ( $rows_affected === false ) { throw new Exception( 'Database error during chunked deletion' ); } if ( $rows_affected < $chunk_size ) { $continue = false; } // Prevent server overload if ( $rows_affected > 0 ) { usleep( 100000 ); // 0.1 second delay between chunks } } } public function cron_cleanup() { // Only run if notifications component is active if ( ! $this->is_notifications_component_active() ) { return; } global $wpdb; $table = $wpdb->prefix . 'bp_notifications'; if ( ! $this->table_exists( $table ) ) { return; } $days = absint( get_option( $this->opt_days, 30 ) ); if ( $days <= 0 ) { return; } $continue = true; $chunk_size = self::CHUNK_SIZE; while ( $continue ) { $query = $wpdb->prepare( "DELETE FROM <code>" . esc_sql( $table ) . "</code> WHERE date_notified < ( NOW() - INTERVAL %d DAY ) LIMIT %d", $days, $chunk_size ); $rows_affected = $wpdb->query( $query ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching if ( $rows_affected === false ) { error_log( 'BP Notification Cron Cleanup Error: Database query failed' ); break; } if ( $rows_affected < $chunk_size ) { $continue = false; } // Prevent server overload if ( $rows_affected > 0 ) { usleep( 100000 ); // 0.1 second delay between chunks } } } public function admin_notices() { if ( ! isset( $_GET['page'] ) || 'delete-bp-notifications' !== $_GET['page'] ) { return; } if ( isset( $_GET['deleted'] ) && isset( $_GET['_wpnonce'] ) && wp_verify_nonce( sanitize_key( $_GET['_wpnonce'] ), 'notifications_deleted' ) ) { echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__( 'All BuddyPress notifications deleted.', 'bp-notifications-cleanup' ) . '</p></div>'; } if ( isset( $_GET['error'] ) ) { echo '<div class="notice notice-error is-dismissible"><p>' . esc_html__( 'Error deleting notifications. Please check logs.', 'bp-notifications-cleanup' ) . '</p></div>'; } } public function buddypress_missing_notice() { // Skip if BuddyPress is active if ( function_exists( 'buddypress' ) ) { return; } // Skip if we're on our own settings page $screen = get_current_screen(); if ( $screen && isset( $screen->id ) && 'tools_page_delete-bp-notifications' === $screen->id ) { return; } echo '<div class="notice notice-error"><p>' . esc_html__( 'BuddyPress Delete Notifications requires BuddyPress to be installed and activated.', 'bp-notifications-cleanup' ) . '</p></div>'; } public function enqueue_confirmation_script( $hook ) { if ( 'tools_page_delete-bp-notifications' !== $hook ) { return; } $message = __( 'This will permanently delete ALL notifications. Continue?', 'bp-notifications-cleanup' ); wp_add_inline_script( 'jquery', 'jQuery(document).ready(function($){ $("#bp-delete-notifications-form").on("submit", function(e){ if(!confirm("' . esc_js( $message ) . '")) { e.preventDefault(); } }); });' ); } private function get_notifications_count() { global $wpdb; $table = $wpdb->prefix . 'bp_notifications'; if ( ! $this->table_exists( $table ) ) { return 0; } try { return (int) $wpdb->get_var( "SELECT COUNT(*) FROM <code>" . esc_sql( $table ) . "</code>" ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching } catch ( Exception $e ) { error_log( 'BP Notification Count Error: ' . $e->getMessage() ); return 0; } } public function register_settings() { register_setting( 'bp_notifications_cleanup_settings', $this->opt_schedule, array( 'type' => 'string', 'sanitize_callback' => array( $this, 'sanitize_schedule' ), 'default' => 'none', ) ); register_setting( 'bp_notifications_cleanup_settings', $this->opt_days, array( 'type' => 'integer', 'sanitize_callback' => array( $this, 'sanitize_retention_days' ), 'default' => 30, ) ); add_settings_section( 'bp_notifications_cleanup_section', __( 'Cleanup Options', 'bp-notifications-cleanup' ), null, 'bp_notifications_cleanup' ); add_settings_field( 'schedule', __( 'Cleanup Schedule', 'bp-notifications-cleanup' ), array( $this, 'schedule_field_cb' ), 'bp_notifications_cleanup', 'bp_notifications_cleanup_section' ); add_settings_field( 'days', __( 'Retention Days', 'bp-notifications-cleanup' ), array( $this, 'days_field_cb' ), 'bp_notifications_cleanup', 'bp_notifications_cleanup_section' ); } public function schedule_field_cb() { $v = get_option( $this->opt_schedule, 'none' ); ?> <select name="<?php echo esc_attr( $this->opt_schedule ); ?>"> <option value="none" <?php selected( $v, 'none' ); ?>> <?php esc_html_e( 'None', 'bp-notifications-cleanup' ); ?> </option> <option value="daily" <?php selected( $v, 'daily' ); ?>> <?php esc_html_e( 'Daily', 'bp-notifications-cleanup' ); ?> </option> <option value="weekly" <?php selected( $v, 'weekly' ); ?>> <?php esc_html_e( 'Weekly', 'bp-notifications-cleanup' ); ?> </option> <option value="monthly" <?php selected( $v, 'monthly' ); ?>> <?php esc_html_e( 'Monthly', 'bp-notifications-cleanup' ); ?> </option> </select> <?php } public function days_field_cb() { $v = get_option( $this->opt_days, 30 ); ?> <input type="number" name="<?php echo esc_attr( $this->opt_days ); ?>" value="<?php echo esc_attr( $v ); ?>" min="1" class="small-text" /> <p class="description"> <?php esc_html_e( 'Notifications older than this many days will be deleted during automatic cleanup.', 'bp-notifications-cleanup' ); ?> </p> <?php } public function sanitize_schedule( $v ) { $allowed = array( 'none', 'daily', 'weekly', 'monthly' ); $v = in_array( $v, $allowed, true ) ? $v : 'none'; $this->reschedule_cron( $v ); return $v; } public function sanitize_retention_days( $days ) { return max( 1, absint( $days ) ); } private function maybe_schedule_cron() { $schedule = get_option( $this->opt_schedule, 'none' ); if ( 'none' !== $schedule && ! wp_next_scheduled( self::CRON_HOOK ) ) { wp_schedule_event( time(), $schedule, self::CRON_HOOK ); } } private function reschedule_cron( $new_schedule ) { wp_clear_scheduled_hook( self::CRON_HOOK ); if ( 'none' !== $new_schedule ) { wp_schedule_event( time(), $new_schedule, self::CRON_HOOK ); } } public function filter_cron_schedules( $schedules ) { if ( ! isset( $schedules['monthly'] ) ) { $schedules['monthly'] = array( 'interval' => 30 * DAY_IN_SECONDS, 'display' => __( 'Once Monthly (30 days)', 'bp-notifications-cleanup' ) ); } return $schedules; } private function table_exists( $table ) { global $wpdb; return $wpdb->get_var( $wpdb->prepare( "SHOW TABLES LIKE %s", $wpdb->esc_like( $table ) ) ) === $table; } private function is_notifications_component_active() { return function_exists( 'bp_is_active' ) && bp_is_active( 'notifications' ); } public function activate() { global $wpdb; $table = $wpdb->prefix . 'bp_notifications'; if ( $this->table_exists( $table ) ) { // Check if index exists using SHOW INDEX $index_exists = $wpdb->get_var( $wpdb->prepare( "SHOW INDEX FROM <code>$table</code> WHERE Key_name = 'date_notified'" ) ); if ( ! $index_exists ) { $wpdb->query( "ALTER TABLE <code>$table</code> ADD INDEX <code>date_notified</code> (<code>date_notified</code>)" ); } } $schedule = get_option( $this->opt_schedule, 'none' ); $this->reschedule_cron( $schedule ); } public function deactivate() { wp_clear_scheduled_hook( self::CRON_HOOK ); } public static function uninstall() { delete_option( 'bp_delete_notifications_schedule' ); delete_option( 'bp_delete_notifications_days' ); wp_clear_scheduled_hook( self::CRON_HOOK ); } } new BP_Delete_All_Notifications_Auto();
Thanks
Thanks for the feedback and valable insight, kindly see updated code below
<?php /** * Plugin Name: BuddyPress Notifications Management Tool * Description: Manually or automatically delete BuddyPress user notifications based on age or schedule. * Version: 2.3.1 * License: GPLv2 or later * Requires at least: 5.6 * Requires PHP: 7.4 * Requires Plugins: buddypress */ defined( 'ABSPATH' ) || exit; class BP_Delete_All_Notifications_Auto { private $opt_schedule = 'bp_delete_notifications_schedule'; private $opt_days = 'bp_delete_notifications_days'; const CRON_HOOK = 'bp_delete_all_notifications_cron'; const CHUNK_SIZE = 1000; public function __construct() { add_action( 'admin_menu', array( $this, 'add_admin_menu' ) ); add_action( 'admin_post_delete_all_bp_notifications', array( $this, 'handle_delete_notifications' ) ); add_action( 'admin_notices', array( $this, 'admin_notices' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_confirmation_script' ) ); add_action( 'admin_init', array( $this, 'register_settings' ) ); add_action( self::CRON_HOOK, array( $this, 'cron_cleanup' ) ); add_filter( 'cron_schedules', array( $this, 'filter_cron_schedules' ) ); register_activation_hook( __FILE__, array( $this, 'activate' ) ); register_deactivation_hook( __FILE__, array( $this, 'deactivate' ) ); register_uninstall_hook( __FILE__, array( __CLASS__, 'uninstall' ) ); $this->maybe_schedule_cron(); // BuddyPress dependency notice if ( ! function_exists( 'buddypress' ) ) { add_action( 'admin_notices', array( $this, 'buddypress_missing_notice' ) ); } } public function add_admin_menu() { // Only show if notifications component is active if ( ! $this->is_notifications_component_active() ) { return; } add_submenu_page( 'tools.php', __( 'BP Notification Cleanup', 'bp-notifications-cleanup' ), __( 'BP Notification Cleanup', 'bp-notifications-cleanup' ), 'manage_options', 'delete-bp-notifications', array( $this, 'admin_page_content' ) ); } public function admin_page_content() { if ( ! current_user_can( 'manage_options' ) ) { return; } $schedule = get_option( $this->opt_schedule, 'none' ); $days = get_option( $this->opt_days, 30 ); ?> <div class="wrap"> <h1><?php esc_html_e( 'BuddyPress Notification Cleanup', 'bp-notifications-cleanup' ); ?></h1> <?php if ( ! $this->is_notifications_component_active() ) : ?> <div class="notice notice-error"> <p><?php esc_html_e( 'BuddyPress Notifications component is not active.', 'bp-notifications-cleanup' ); ?></p> </div> <?php else : ?> <div class="card"> <h2><?php esc_html_e( 'Manual Delete', 'bp-notifications-cleanup' ); ?></h2> <p><?php esc_html_e( 'This will permanently delete all notifications for all users.', 'bp-notifications-cleanup' ); ?></p> <p><?php esc_html_e( 'Total Notifications:', 'bp-notifications-cleanup' ); ?> <strong><?php echo esc_html( $this->get_notifications_count() ); ?></strong> </p> <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" id="bp-delete-notifications-form"> <?php wp_nonce_field( 'delete_all_bp_notifications_action', 'delete_all_bp_notifications_nonce' ); ?> <input type="hidden" name="action" value="delete_all_bp_notifications"> <p> <input type="submit" class="button button-danger" value="<?php esc_attr_e( 'Delete All Notifications', 'bp-notifications-cleanup' ); ?>"> </p> </form> </div> <hr> <div class="card"> <h2><?php esc_html_e( 'Auto Cleanup Settings', 'bp-notifications-cleanup' ); ?></h2> <form method="post" action="options.php"> <?php settings_fields( 'bp_notifications_cleanup_settings' ); do_settings_sections( 'bp_notifications_cleanup' ); submit_button( __( 'Save Settings', 'bp-notifications-cleanup' ) ); ?> </form> <p> <?php esc_html_e( 'Schedule:', 'bp-notifications-cleanup' ); ?> <strong><?php echo esc_html( ucfirst( $schedule ) ); ?></strong> </p> <p> <?php esc_html_e( 'Retention Days:', 'bp-notifications-cleanup' ); ?> <strong><?php echo esc_html( $days ); ?></strong> (<?php esc_html_e( 'Notifications older than this will be deleted.', 'bp-notifications-cleanup' ); ?>) </p> </div> <?php endif; ?> </div> <?php } public function handle_delete_notifications() { if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'Permission denied', 'bp-notifications-cleanup' ) ); } check_admin_referer( 'delete_all_bp_notifications_action', 'delete_all_bp_notifications_nonce' ); $nonce = wp_create_nonce( 'notifications_deleted' ); $redirect = add_query_arg( array( 'deleted' => 1, '_wpnonce' => $nonce ), admin_url( 'tools.php?page=delete-bp-notifications' ) ); try { $this->perform_truncate(); } catch ( Exception $e ) { error_log( 'BP Notification Cleanup Error: ' . $e->getMessage() ); $redirect = add_query_arg( array( 'error' => 1 ), $redirect ); } wp_safe_redirect( $redirect ); exit; } public function perform_truncate() { global $wpdb; $table = $wpdb->prefix . 'bp_notifications'; if ( ! $this->table_exists( $table ) ) { return; } $wpdb->query( "TRUNCATE TABLE <code>" . esc_sql( $table ) . "</code>" ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching } public function cron_cleanup() { // Only run if notifications component is active if ( ! $this->is_notifications_component_active() ) { return; } global $wpdb; $table = $wpdb->prefix . 'bp_notifications'; if ( ! $this->table_exists( $table ) ) { return; } $days = absint( get_option( $this->opt_days, 30 ) ); if ( $days <= 0 ) { return; } $continue = true; $chunk_size = self::CHUNK_SIZE; while ( $continue ) { $query = $wpdb->prepare( "DELETE FROM <code>" . esc_sql( $table ) . "</code> WHERE date_notified < ( NOW() - INTERVAL %d DAY ) LIMIT %d", $days, $chunk_size ); $rows_affected = $wpdb->query( $query ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching if ( $rows_affected === false ) { error_log( 'BP Notification Cron Cleanup Error: Database query failed' ); break; } if ( $rows_affected < $chunk_size ) { $continue = false; } } } public function admin_notices() { if ( ! isset( $_GET['page'] ) || 'delete-bp-notifications' !== $_GET['page'] ) { return; } if ( isset( $_GET['deleted'] ) && isset( $_GET['_wpnonce'] ) && wp_verify_nonce( sanitize_key( $_GET['_wpnonce'] ), 'notifications_deleted' ) ) { echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__( 'All BuddyPress notifications deleted.', 'bp-notifications-cleanup' ) . '</p></div>'; } if ( isset( $_GET['error'] ) ) { echo '<div class="notice notice-error is-dismissible"><p>' . esc_html__( 'Error deleting notifications. Please check logs.', 'bp-notifications-cleanup' ) . '</p></div>'; } } public function buddypress_missing_notice() { // Skip if we're on our own settings page $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null; if ( $screen && isset( $screen->id ) && 'tools_page_delete-bp-notifications' === $screen->id ) { return; } echo '<div class="notice notice-error"><p>' . esc_html__( 'BuddyPress Delete Notifications requires BuddyPress to be installed and activated.', 'bp-notifications-cleanup' ) . '</p></div>'; } public function enqueue_confirmation_script( $hook ) { if ( 'tools_page_delete-bp-notifications' !== $hook ) { return; } $message = __( 'This will permanently delete ALL notifications. Continue?', 'bp-notifications-cleanup' ); wp_add_inline_script( 'jquery', 'jQuery("#bp-delete-notifications-form").on("submit", function(e){ if(!confirm("' . esc_js( $message ) . '")) { e.preventDefault(); } });' ); } private function get_notifications_count() { global $wpdb; $table = $wpdb->prefix . 'bp_notifications'; if ( ! $this->table_exists( $table ) ) { return 0; } try { return (int) $wpdb->get_var( "SELECT COUNT(*) FROM <code>" . esc_sql( $table ) . "</code>" ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching } catch ( Exception $e ) { error_log( 'BP Notification Count Error: ' . $e->getMessage() ); return 0; } } public function register_settings() { register_setting( 'bp_notifications_cleanup_settings', $this->opt_schedule, array( 'type' => 'string', 'sanitize_callback' => array( $this, 'sanitize_schedule' ), 'default' => 'none', ) ); register_setting( 'bp_notifications_cleanup_settings', $this->opt_days, array( 'type' => 'integer', 'sanitize_callback' => array( $this, 'sanitize_retention_days' ), 'default' => 30, ) ); add_settings_section( 'bp_notifications_cleanup_section', __( 'Cleanup Options', 'bp-notifications-cleanup' ), null, 'bp_notifications_cleanup' ); add_settings_field( 'schedule', __( 'Cleanup Schedule', 'bp-notifications-cleanup' ), array( $this, 'schedule_field_cb' ), 'bp_notifications_cleanup', 'bp_notifications_cleanup_section' ); add_settings_field( 'days', __( 'Retention Days', 'bp-notifications-cleanup' ), array( $this, 'days_field_cb' ), 'bp_notifications_cleanup', 'bp_notifications_cleanup_section' ); } public function schedule_field_cb() { $v = get_option( $this->opt_schedule, 'none' ); ?> <select name="<?php echo esc_attr( $this->opt_schedule ); ?>"> <option value="none" <?php selected( $v, 'none' ); ?>> <?php esc_html_e( 'None', 'bp-notifications-cleanup' ); ?> </option> <option value="daily" <?php selected( $v, 'daily' ); ?>> <?php esc_html_e( 'Daily', 'bp-notifications-cleanup' ); ?> </option> <option value="weekly" <?php selected( $v, 'weekly' ); ?>> <?php esc_html_e( 'Weekly', 'bp-notifications-cleanup' ); ?> </option> <option value="monthly" <?php selected( $v, 'monthly' ); ?>> <?php esc_html_e( 'Monthly', 'bp-notifications-cleanup' ); ?> </option> </select> <?php } public function days_field_cb() { $v = get_option( $this->opt_days, 30 ); ?> <input type="number" name="<?php echo esc_attr( $this->opt_days ); ?>" value="<?php echo esc_attr( $v ); ?>" min="1" /> <?php } public function sanitize_schedule( $v ) { $allowed = array( 'none', 'daily', 'weekly', 'monthly' ); $v = in_array( $v, $allowed, true ) ? $v : 'none'; $this->reschedule_cron( $v ); return $v; } public function sanitize_retention_days( $days ) { return max( 1, absint( $days ) ); } private function maybe_schedule_cron() { $schedule = get_option( $this->opt_schedule, 'none' ); if ( 'none' !== $schedule && ! wp_next_scheduled( self::CRON_HOOK ) ) { wp_schedule_event( time(), $schedule, self::CRON_HOOK ); } } private function reschedule_cron( $new_schedule ) { wp_clear_scheduled_hook( self::CRON_HOOK ); if ( 'none' !== $new_schedule ) { wp_schedule_event( time(), $new_schedule, self::CRON_HOOK ); } } public function filter_cron_schedules( $schedules ) { $schedules['monthly'] = array( 'interval' => 30 * DAY_IN_SECONDS, 'display' => __( 'Once Monthly (30 days)', 'bp-notifications-cleanup' ) ); return $schedules; } private function table_exists( $table ) { global $wpdb; $result = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(1) FROM information_schema.tables WHERE table_schema = %s AND table_name = %s LIMIT 1", DB_NAME, $table ) ); return (bool) $result; } private function is_notifications_component_active() { // Check if BuddyPress is active and notifications component is enabled return function_exists( 'bp_is_active' ) && bp_is_active( 'notifications' ); } public function activate() { global $wpdb; $table = $wpdb->prefix . 'bp_notifications'; if ( $this->table_exists( $table ) ) { $index_exists = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(1) FROM information_schema.statistics WHERE table_schema = %s AND table_name = %s AND index_name = 'date_notified'", DB_NAME, $table ) ); if ( ! $index_exists ) { $wpdb->query( "ALTER TABLE <code>" . esc_sql( $table ) . "</code> ADD INDEX <code>date_notified</code> (<code>date_notified</code>)" ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching } } $schedule = get_option( $this->opt_schedule, 'none' ); $this->reschedule_cron( $schedule ); } public function deactivate() { wp_clear_scheduled_hook( self::CRON_HOOK ); } public static function uninstall() { delete_option( 'bp_delete_notifications_schedule' ); delete_option( 'bp_delete_notifications_days' ); wp_clear_scheduled_hook( self::CRON_HOOK ); } } new BP_Delete_All_Notifications_Auto();
UPDATED CODE BELOW
<?php /** * Plugin Name: BuddyPress Notification Management Tool * Description: Manually or automatically delete BuddyPress notifications based on age or schedule. * Version: 2.1 * License: GPLv2 or later * Requires at least: 5.6 * Requires PHP: 7.4 * Requires Plugins: buddypress */ defined( 'ABSPATH' ) || exit; class BP_Delete_All_Notifications_Auto { private $opt_schedule = 'bp_delete_notifications_schedule'; private $opt_days = 'bp_delete_notifications_days'; const CRON_HOOK = 'bp_delete_all_notifications_cron'; public function __construct() { add_action( 'admin_menu', [ $this, 'add_admin_menu' ] ); add_action( 'admin_post_delete_all_bp_notifications', [ $this, 'handle_delete_notifications' ] ); add_action( 'admin_notices', [ $this, 'admin_notices' ] ); add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_confirmation_script' ] ); add_action( 'admin_init', [ $this, 'register_settings' ] ); add_action( self::CRON_HOOK, [ $this, 'cron_cleanup' ] ); add_filter( 'cron_schedules', [ $this, 'filter_cron_schedules' ] ); register_activation_hook( __FILE__, [ $this, 'activate' ] ); register_deactivation_hook( __FILE__, [ $this, 'deactivate' ] ); $this->maybe_schedule_cron(); } public function add_admin_menu() { if ( ! function_exists( 'buddypress' ) ) return; add_submenu_page( 'tools.php', 'BP Notification Cleanup', 'BP Notification Cleanup', 'manage_options', 'delete-bp-notifications', [ $this, 'admin_page_content' ] ); } public function admin_page_content() { if ( ! current_user_can( 'manage_options' ) ) return; $schedule = get_option( $this->opt_schedule, 'none' ); $days = get_option( $this->opt_days, 30 ); ?> <div class="wrap"> <h1>BuddyPress Notification Cleanup</h1> <?php if ( ! function_exists( 'buddypress' ) ) : ?> <div class="notice notice-error"><p>BuddyPress is not active.</p></div> <?php else : ?> <div class="card"> <h2>Manual Delete</h2> <p>This will permanently delete <strong>all</strong> notifications for all users.</p> <p>Total Notifications: <strong><?php echo esc_html( $this->get_notifications_count() ); ?></strong></p> <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" id="bp-delete-notifications-form"> <?php wp_nonce_field( 'delete_all_bp_notifications_action', 'delete_all_bp_notifications_nonce' ); ?> <input type="hidden" name="action" value="delete_all_bp_notifications"> <p><input type="submit" class="button button-danger" value="Delete All Notifications" onclick="return confirm('This cannot be undone. Proceed?')"></p> </form> </div> <hr> <div class="card"> <h2>Auto Cleanup Settings</h2> <form method="post" action="options.php"> <?php settings_fields( 'bp_notifications_cleanup_settings' ); do_settings_sections( 'bp_notifications_cleanup' ); submit_button( 'Save Settings' ); ?> </form> <p>Schedule: <strong><?php echo esc_html( ucfirst( $schedule ) ); ?></strong></p> <p>Retention Days: <strong><?php echo esc_html( $days ); ?></strong> (Notifications older than this will be deleted.)</p> </div> <?php endif; ?> </div> <?php } public function handle_delete_notifications() { if ( ! current_user_can( 'manage_options' ) ) wp_die( 'Permission denied' ); check_admin_referer( 'delete_all_bp_notifications_action', 'delete_all_bp_notifications_nonce' ); $this->perform_truncate(); wp_redirect( admin_url( 'tools.php?page=delete-bp-notifications&deleted=1' ) ); exit; } public function perform_truncate() { global $wpdb; $table = $wpdb->prefix . 'bp_notifications'; if ( $wpdb->get_var( $wpdb->prepare( "SHOW TABLES LIKE %s", $table ) ) === $table ) { $wpdb->query( "TRUNCATE TABLE $table" ); } } public function cron_cleanup() { global $wpdb; $table = $wpdb->prefix . 'bp_notifications'; if ( $wpdb->get_var( $wpdb->prepare( "SHOW TABLES LIKE %s", $table ) ) !== $table ) return; $days = absint( get_option( $this->opt_days, 30 ) ); if ( $days > 0 ) { $wpdb->query( $wpdb->prepare( "DELETE FROM {$table} WHERE date_notified < ( NOW() - INTERVAL %d DAY )", $days ) ); } } public function admin_notices() { if ( isset( $_GET['page'] ) && $_GET['page'] === 'delete-bp-notifications' && isset( $_GET['deleted'] ) ) { echo '<div class="notice notice-success is-dismissible"><p>All BuddyPress notifications deleted.</p></div>'; } } public function enqueue_confirmation_script( $hook ) { if ( $hook !== 'tools_page_delete-bp-notifications' ) return; wp_add_inline_script( 'jquery', ' jQuery("#bp-delete-notifications-form").on("submit", function(e){ if(!confirm("This will permanently delete ALL notifications. Continue?")) e.preventDefault(); }); ' ); } private function get_notifications_count() { global $wpdb; $table = $wpdb->prefix . 'bp_notifications'; return $wpdb->get_var( $wpdb->prepare( "SHOW TABLES LIKE %s", $table ) ) === $table ? (int) $wpdb->get_var( "SELECT COUNT(*) FROM $table" ) : 0; } public function register_settings() { register_setting( 'bp_notifications_cleanup_settings', $this->opt_schedule, [ 'type' => 'string', 'sanitize_callback' => [ $this, 'sanitize_schedule' ], 'default' => 'none', ] ); register_setting( 'bp_notifications_cleanup_settings', $this->opt_days, [ 'type' => 'integer', 'sanitize_callback' => 'absint', 'default' => 30, ] ); add_settings_section( 'bp_notifications_cleanup_section', 'Cleanup Options', null, 'bp_notifications_cleanup' ); add_settings_field( 'schedule', 'Cleanup Schedule', [ $this, 'schedule_field_cb' ], 'bp_notifications_cleanup', 'bp_notifications_cleanup_section' ); add_settings_field( 'days', 'Retention Days', [ $this, 'days_field_cb' ], 'bp_notifications_cleanup', 'bp_notifications_cleanup_section' ); } public function schedule_field_cb() { $v = get_option( $this->opt_schedule, 'none' ); echo '<select name="'.esc_attr($this->opt_schedule).'"> <option value="none"'.selected($v,'none',false).'>None</option> <option value="daily"'.selected($v,'daily',false).'>Daily</option> <option value="weekly"'.selected($v,'weekly',false).'>Weekly</option> <option value="monthly"'.selected($v,'monthly',false).'>Monthly</option> </select>'; } public function days_field_cb() { $v = get_option( $this->opt_days, 30 ); echo '<input type="number" name="'.esc_attr($this->opt_days).'" value="'.esc_attr($v).'" min="1" />'; } public function sanitize_schedule( $v ) { $allowed = [ 'none','daily','weekly','monthly' ]; $v = in_array( $v, $allowed, true ) ? $v : 'none'; $this->reschedule_cron( $v ); return $v; } private function maybe_schedule_cron() { $schedule = get_option( $this->opt_schedule, 'none' ); if ( $schedule !== 'none' && ! wp_next_scheduled( self::CRON_HOOK ) ) { wp_schedule_event( time(), $schedule, self::CRON_HOOK ); } } private function reschedule_cron( $new ) { wp_clear_scheduled_hook( self::CRON_HOOK ); if ( $new !== 'none' ) wp_schedule_event( time(), $new, self::CRON_HOOK ); } public function filter_cron_schedules( $s ) { $s['monthly'] = [ 'interval' => 30*DAY_IN_SECONDS, 'display' => 'Once Monthly' ]; return $s; } public function activate() { $this->maybe_schedule_cron(); } public function deactivate() { wp_clear_scheduled_hook( self::CRON_HOOK ); } } new BP_Delete_All_Notifications_Auto();
Thanks
This is the code that worked for me
/** * Assign "Sponsored Posts" content type if post is marked as sticky. */ function assign_sponsored_term_if_sticky( $post_id, $form_id, $post_data ) { // Check if post is sticky. if ( ! is_sticky( $post_id ) ) { return; } // Get the "Sponsored Posts" term in 'content_type' taxonomy. $term = get_term_by( 'name', 'Sponsored Posts', 'content_type' ); if ( ! $term || is_wp_error( $term ) ) { return; } // Append the term to the post without removing other terms. wp_set_post_terms( $post_id, array( $term->term_id ), 'content_type', true ); } add_action( 'bblpro_post_submitted', 'assign_sponsored_term_if_sticky', 20, 3 ); add_action( 'bblpro_post_updated', 'assign_sponsored_term_if_sticky', 20, 3 );
I dont know why this code below did not work
/** * Reassign "Sponsored Posts" term when a paid post is submitted or updated. */ function assign_sponsored_term_if_paid( $post_id, $form_id, $post_data ) { // Check if the post is paid. if ( ! bbl_ppp_get_post_order_id( $post_id ) ) { return; } // Get the "Sponsored Posts" term. $term = get_term_by( 'name', 'Sponsored Posts', 'content_type' ); if ( ! $term ) { return; } // Append the term without removing existing ones. wp_set_post_terms( $post_id, array( $term->term_id ), 'content_type', true ); } add_action( 'bblpro_post_submitted', 'assign_sponsored_term_if_paid', 20, 3 ); add_action( 'bblpro_post_updated', 'assign_sponsored_term_if_paid', 20, 3 );
Thanks Brajesh
This is the code I used can you confirm if its accurate
/** * Assign (Sponsored Post) category to BuddyBlog pay per post articles on checkout. */ function assign_sponsored_term_if_paid( $post_id, $form_id ) { if ( 'post' !== get_post_type( $post_id ) ) { return; } // Check if post is a paid post by checking if order ID exists. $order_id = get_post_meta( $post_id, '_bblpro_ppp_order_id', true ); if ( ! $order_id ) { return; } // Get the Sponsored Posts term. $term = get_term_by( 'name', 'Sponsored Posts', 'content_type' ); if ( ! $term || is_wp_error( $term ) ) { return; } // Assign Sponsored Posts term to the post (merge with existing). wp_set_post_terms( $post_id, array( $term->term_id ), 'content_type', true ); } add_action( 'bblpro_post_submitted', 'assign_sponsored_term_if_paid', 20, 2 ); add_action( 'bblpro_post_updated', 'assign_sponsored_term_if_paid', 20, 2 );
Thanks
1. I created a custom taxonomy called CONTENT TYPE where by (Sponsored Posts) is an option.
2. I now disabled the (Sponsored Posts) option in the buddyblog settings because I don’t want users to be select this option when posting.
3. I only want the (Sponsored Posts) option to be automatically assigned only to featured paid post using the code I shared above. IM USING PAY PER POST ADDON
THE PROBLEM
1. The problem now is with the retention/removal of the (Sponsored Posts) term to published paid post when users try to edit and republish a paid post.
2. If I edit a paid post the (Sponsored Posts) term is removed after clicking the (UPDATE) button.
3. When editing a paid post the term (Sponsored Posts) should not be removed as the term was initially assigned using the code I provided