BuddyPress Reactions – Developer Guide

Get Started

Architecture Overview

Plugin Structure

buddypress-reactions/
├── admin/                  # Admin functionality
│   ├── class-buddypress-reactions-admin.php
│   ├── css/               # Admin styles
│   ├── js/                # Admin scripts
│   └── inc/               # Admin includes
├── includes/              # Core functionality
│   ├── class-buddypress-reactions.php
│   ├── class-buddypress-reactions-activator.php
│   ├── class-buddypress-reactions-deactivator.php
│   └── buddypress-reactions-function.php
├── public/                # Frontend functionality
│   ├── class-buddypress-reactions-public.php
│   ├── css/              # Frontend styles
│   └── js/               # Frontend scripts
├── emojis/               # Emoji assets
│   ├── svg/              # SVG files (1-200)
│   ├── json/             # Animation files
│   └── bp-reaction-emojis.json
└── docs/                 # Documentation

Core Classes

Main Plugin Class

class Buddypress_Reactions {
    protected $loader;
    protected $plugin_name;
    protected $version;
    
    public function __construct() {
        $this->version = BUDDYPRESS_REACTIONS_VERSION;
        $this->plugin_name = 'buddypress-reactions';
        $this->load_dependencies();
        $this->set_locale();
        $this->define_admin_hooks();
        $this->define_public_hooks();
    }
}

Database Schema

Tables

wp_bp_reactions_shortcodes

CREATE TABLE wp_bp_reactions_shortcodes (
    id mediumint(9) NOT NULL AUTO_INCREMENT,
    name varchar(150) DEFAULT NULL,
    post_type varchar(100) DEFAULT NULL,
    front_render int(11) DEFAULT 0,
    options longtext,
    PRIMARY KEY (id)
);

wp_bp_reactions_emojis

CREATE TABLE wp_bp_reactions_emojis (
    id int NOT NULL AUTO_INCREMENT,
    name varchar(500) DEFAULT NULL,
    type enum('inbuilt','custom') NOT NULL,
    format varchar(10) NOT NULL,
    PRIMARY KEY (id)
);

wp_bp_reactions_reacted_emoji

CREATE TABLE wp_bp_reactions_reacted_emoji (
    id int NOT NULL AUTO_INCREMENT,
    post_id int NOT NULL,
    post_type varchar(50) NOT NULL,
    user_id int DEFAULT NULL,
    emoji_id int DEFAULT NULL,
    bprs_id int DEFAULT NULL,
    PRIMARY KEY (id),
    KEY post_id (post_id),
    KEY user_id (user_id),
    KEY emoji_id (emoji_id)
);

Hooks and Filters

Actions

Frontend Actions

// Fired when reactions UI should be displayed
do_action('buddypress_reactions_after_activity_entry', $activity_id);

// Fired when a reaction is added
do_action('buddypress_reactions_after_add_reaction', $post_id, $post_type, $emoji_id, $user_id);

// Fired when a reaction is removed  
do_action('buddypress_reactions_after_remove_reaction', $post_id, $post_type, $emoji_id, $user_id);

Admin Actions

// Fired when emoji database is reindexed
do_action('buddypress_reactions_after_reindex', $emoji_count);

// Fired when settings are saved
do_action('buddypress_reactions_settings_saved', $settings);

Filters

Display Filters

// Filter emoji display URL
$emoji_url = apply_filters('buddypress_reactions_emoji_url', $url, $emoji_id, $type);

// Filter reaction button text
$button_text = apply_filters('buddypress_reactions_button_text', __('React', 'buddypress-reactions'), $post_type);

// Filter available emojis for a post type
$emojis = apply_filters('buddypress_reactions_available_emojis', $emojis, $post_type);

// Filter to load scripts/styles
$should_load = apply_filters('buddypress_reactions_load_scripts', $should_load);
$should_load = apply_filters('buddypress_reactions_load_styles', $should_load);

Data Filters

// Filter reaction data before saving
$reaction_data = apply_filters('buddypress_reactions_before_save', $reaction_data);

// Filter emoji name
$emoji_name = apply_filters('buddypress_reactions_emoji_name', $name, $emoji_id);

JavaScript API

Global Object

window.bpreactions = {
    ajaxUrl: 'admin-ajax.php',
    emojis_path: '/wp-content/plugins/buddypress-reactions/emojis/',
    version: '3.0.0',
    rest_url: '/wp-json/buddypress/v1/',
    nonce: 'security_nonce'
};

Core Functions

Adding a Reaction

 

// Trigger reaction via JavaScript
function addReaction(postId, postType, emojiId, bprsId) {
    jQuery.ajax({
        url: bpreactions.ajaxUrl,
        type: 'POST',
        dataType: 'json',
        data: {
            action: 'bpr_create_user_react_emoji_ajax',
            post_id: postId,
            post_type: postType,
            emoji_id: emojiId,
            bprs_id: bprsId,
            ajax_nonce: jQuery('#_ajax_nonce').val()
        },
        success: function(response) {
            if (response.status === 'success') {
                // Update UI
                jQuery('#bp-reactions-' + postType + '-' + postId).html(response.container);
            }
        }
    });
}

Event Listeners

// Listen for reaction clicks
jQuery(document).on('click', '.emoji-pick', function() {
    var emojiId = jQuery(this).data('emoji-id');
    var postId = jQuery(this).data('post-id');
    var postType = jQuery(this).data('type');
    var bprsId = jQuery(this).data('bprs-id');
    
    // Handle reaction
});

// Listen for reaction removal
jQuery(document).on('click', '.bp-activity-react-button', function() {
    // Handle removal
});

Animation API

// Initialize Lottie animations
var animation = bodymovin.loadAnimation({
    container: element,
    path: bpreactions.emojis_path + 'json/' + emojiId + '.json',
    renderer: 'svg',
    loop: true,
    autoplay: true
});

AJAX Handlers

Add Reaction

add_action('wp_ajax_bpr_create_user_react_emoji_ajax', 'handle_create_reaction');

function handle_create_reaction() {
    // Verify nonce
    if (!wp_verify_nonce($_POST['ajax_nonce'], 'bp-reactions')) {
        wp_send_json_error('Security check failed');
    }
    
    // Get data
    $emoji_id = intval($_POST['emoji_id']);
    $post_id = intval($_POST['post_id']);
    $post_type = sanitize_text_field($_POST['post_type']);
    $bprs_id = intval($_POST['bprs_id']);
    
    // Process reaction
    // Return response
    wp_send_json_success(array(
        'status' => 'success',
        'container' => $html_output
    ));
}

Remove Reaction

add_action('wp_ajax_bpr_remove_user_react_emoji_ajax', 'handle_remove_reaction');

Display Reactions

add_action('wp_ajax_bpr_display_user_react_emoji_ajax', 'handle_display_reactions');

Creating Custom Integrations

Adding Reactions to Custom Post Types

// 1. Register post type support
function add_reactions_to_custom_post_type() {
    global $wpdb;
    
    // Insert shortcode for custom post type
    $wpdb->insert(
        $wpdb->prefix . 'bp_reactions_shortcodes',
        array(
            'name' => 'Custom Post Reaction',
            'post_type' => 'my_custom_post',
            'front_render' => 1,
            'options' => json_encode(array(
                'emojis' => array(64, 190, 36, 5, 42, 29, 26, 7),
                'animation' => 'true'
            ))
        )
    );
}
add_action('init', 'add_reactions_to_custom_post_type');

// 2. Display reactions
function display_custom_post_reactions($content) {
    if (get_post_type() === 'my_custom_post') {
        $reactions = do_shortcode('[bp_reactions id="4"]');
        $content .= $reactions;
    }
    return $content;
}
add_filter('the_content', 'display_custom_post_reactions');

Custom Emoji Sets

// Filter emojis for specific post type
function custom_emoji_set($emojis, $post_type) {
    if ($post_type === 'forum_topic') {
        // Return different emoji set for forums
        return array(64, 65, 190, 187, 26, 132, 152, 194);
    }
    return $emojis;
}
add_filter('buddypress_reactions_available_emojis', 'custom_emoji_set', 10, 2);

Custom Reaction Notifications

// Add custom notification handling
function custom_reaction_notification($post_id, $post_type, $emoji_id, $user_id) {
    if ($post_type === 'activity') {
        // Send custom notification
        bp_notifications_add_notification(array(
            'user_id' => $author_id,
            'item_id' => $post_id,
            'secondary_item_id' => $user_id,
            'component_name' => 'custom_reactions',
            'component_action' => 'new_reaction',
            'date_notified' => bp_core_current_time(),
            'is_new' => 1
        ));
    }
}
add_action('buddypress_reactions_after_add_reaction', 'custom_reaction_notification', 10, 4);

Extending Functionality

Adding New Reaction Types

class Custom_Reaction_Type {
    public function __construct() {
        add_filter('buddypress_reactions_post_types', array($this, 'add_post_type'));
        add_action('wp_ajax_custom_reaction', array($this, 'handle_reaction'));
    }
    
    public function add_post_type($post_types) {
        $post_types[] = 'custom_type';
        return $post_types;
    }
    
    public function handle_reaction() {
        // Custom reaction logic
    }
}
new Custom_Reaction_Type();

Creating Reaction Widgets

class My_Reaction_Widget extends WP_Widget {
    public function __construct() {
        parent::__construct(
            'my_reaction_widget',
            __('My Reaction Widget', 'buddypress-reactions')
        );
    }
    
    public function widget($args, $instance) {
        global $wpdb;
        
        // Get top reactions
        $top_reactions = $wpdb->get_results("
            SELECT emoji_id, COUNT(*) as count 
            FROM {$wpdb->prefix}bp_reactions_reacted_emoji 
            GROUP BY emoji_id 
            ORDER BY count DESC 
            LIMIT 5
        ");
        
        // Display widget
        echo $args['before_widget'];
        foreach ($top_reactions as $reaction) {
            $emoji_url = get_buddypress_reaction_emoji($reaction->emoji_id, 'svg');
            echo '<img src="' . esc_url($emoji_url) . '" /> ';
            echo '<span>' . $reaction->count . '</span>';
        }
        echo $args['after_widget'];
    }
}

REST API Integration

// Register REST routes
function register_reactions_rest_routes() {
    register_rest_route('buddypress-reactions/v1', '/reactions/(?P<id>\d+)', array(
        'methods' => 'GET',
        'callback' => 'get_post_reactions',
        'permission_callback' => '__return_true'
    ));
    
    register_rest_route('buddypress-reactions/v1', '/react', array(
        'methods' => 'POST',
        'callback' => 'add_reaction_rest',
        'permission_callback' => 'is_user_logged_in'
    ));
}
add_action('rest_api_init', 'register_reactions_rest_routes');

Best Practices

 

Performance Optimization

  1. Use Caching
// Cache reaction counts
$cache_key = 'reactions_' . $post_id . '_' . $post_type;
$reactions = wp_cache_get($cache_key, 'bp_reactions');

if (false === $reactions) {
    $reactions = $wpdb->get_results($query);
    wp_cache_set($cache_key, $reactions, 'bp_reactions', HOUR_IN_SECONDS);
}
  1. Batch Database Operations
// Use single query for multiple operations
$wpdb->query("START TRANSACTION");
try {
    // Multiple operations
    $wpdb->query("COMMIT");
} catch (Exception $e) {
    $wpdb->query("ROLLBACK");
}

Security Best Practices

  1. Always Verify Nonces
if (!wp_verify_nonce($_POST['nonce'], 'bp-reactions')) {
    wp_die('Security check failed');
}
  1. Sanitize All Input
$post_id = absint($_POST['post_id']);
$post_type = sanitize_text_field($_POST['post_type']);
$emoji_id = absint($_POST['emoji_id']);
  1. Check Capabilities
if (!is_user_logged_in()) {
    wp_send_json_error('Login required');
}

if (!current_user_can('read')) {
    wp_send_json_error('Insufficient permissions');
}

Error Handling

try {
    // Reaction operation
    $result = add_reaction($post_id, $emoji_id, $user_id);
    
    if (is_wp_error($result)) {
        throw new Exception($result->get_error_message());
    }
    
    wp_send_json_success(array('message' => 'Reaction added'));
    
} catch (Exception $e) {
    wp_send_json_error($e->getMessage());
}

Debugging

// Enable debug logging
if (defined('WP_DEBUG') && WP_DEBUG) {
    error_log('BuddyPress Reactions: ' . print_r($data, true));
}

// Custom debug function
function bpr_debug($data, $label = '') {
    if (!defined('WP_DEBUG') || !WP_DEBUG) {
        return;
    }
    
    $backtrace = debug_backtrace();
    $file = $backtrace[0]['file'];
    $line = $backtrace[0]['line'];
    
    error_log("BPR Debug [$label] at $file:$line - " . print_r($data, true));
}

Testing

Unit Testing Example

class Test_Reactions extends WP_UnitTestCase {
    public function test_add_reaction() {
        $user_id = $this->factory->user->create();
        wp_set_current_user($user_id);
        
        $post_id = $this->factory->post->create();
        $emoji_id = 64; // Thumbs up
        
        $result = add_reaction($post_id, 'post', $emoji_id, $user_id);
        
        $this->assertTrue($result);
        $this->assertEquals(1, get_reaction_count($post_id, 'post'));
    }
}

Support and Resources

Last updated: August 5, 2025