BuddyDev

Search

Replies

  • Participant
    Level: Guru
    Posts: 910
    Tosin on in reply to: Delete All Notifications Tool #55635

    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>&quot; . esc_sql( $table ) . &quot;</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>&quot; . esc_sql( $table ) . &quot;</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>&quot; . esc_sql( $table ) . &quot;</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

  • Participant
    Level: Guru
    Posts: 910
    Tosin on in reply to: Delete All Notifications Tool #55598

    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>&quot; . esc_sql( $table ) . &quot;</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>&quot; . esc_sql( $table ) . &quot;</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>&quot; . esc_sql( $table ) . &quot;</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>&quot; . esc_sql( $table ) . &quot;</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(); 
  • Participant
    Level: Guru
    Posts: 910
    Tosin on in reply to: Delete All Notifications Tool #55584

    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

  • Participant
    Level: Guru
    Posts: 910
    Tosin on in reply to: Buddyblog assign taxonomy on update #55425

    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 );
     
  • Participant
    Level: Guru
    Posts: 910
    Tosin on in reply to: Buddyblog assign taxonomy on update #55424

    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

  • Participant
    Level: Guru
    Posts: 910
    Tosin on in reply to: Buddyblog assign taxonomy on update #55403

    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

  • Participant
    Level: Guru
    Posts: 910
    Tosin on in reply to: Buddyblog assign taxonomy on update #55287

    Gentle reminder sir thanks

  • Participant
    Level: Guru
    Posts: 910

    Thanks Brajesh

    Your code worked perfectly.

  • Participant
    Level: Guru
    Posts: 910

    Thanks for the feedback ill stick to your advice

  • Participant
    Level: Guru
    Posts: 910

    Hi Barjesh

    Can you provide the same code(Limit message to one participant) for the legacy template