Skip to main content

Custom Plugins

ScentLok uses several custom Rhino Group plugins to extend WordPress and BigCommerce functionality.

Plugin Overview

PluginVersionPurpose
Suma Dealer Locator3.0.2Store finder with Google Maps
Suma Patches1.5.19WordPress core customizations
Suma BazaarVoice Integrator1.1.4Product review integration
Suma Analytics1.3.5E-commerce event tracking
Back in Stock Notifications1.3.0Product availability alerts
BigCommerce Middleware for Narvar1.2.20Order tracking integration

Suma Dealer Locator

Overview

Version: 3.0.2
Purpose: Store finder with Google Maps integration

Features

  • Google Maps integration with marker clustering
  • GeoIP location detection
  • Radius search with distance calculation
  • Category and tag filtering
  • Gutenberg, Elementor, and Visual Composer support
  • AJAX-powered search
  • Responsive mobile design

Shortcode Usage

// Basic dealer locator
[suma_dealer_locator]

// With custom radius
[suma_dealer_locator radius="50"]

// Filter by category
[suma_dealer_locator category="authorized-dealer"]

// Custom map height
[suma_dealer_locator height="600"]

Configuration

// Settings in wp-admin > Dealer Locator

// Google Maps API Key
define( 'SUMA_GOOGLE_MAPS_API_KEY', 'your-api-key-here' );

// Default search radius (miles)
add_filter( 'suma_dealer_locator/default_radius', function() {
return 25;
});

// Custom marker icon
add_filter( 'suma_dealer_locator/marker_icon', function() {
return get_stylesheet_directory_uri() . '/assets/images/marker.png';
});

Custom Dealer Post Type

// Register dealer location
$dealer_id = wp_insert_post([
'post_type' => 'dealer',
'post_title' => 'Outdoor Pro Shop',
'post_status' => 'publish',
'meta_input' => [
'address' => '123 Main St',
'city' => 'Springfield',
'state' => 'IL',
'zip' => '62701',
'phone' => '555-1234',
'website' => 'https://example.com',
'latitude' => '39.7817',
'longitude' => '-89.6501',
],
]);

Suma Patches

Overview

Version: 1.5.19
Purpose: WordPress core customizations and performance optimizations

Key Patches

Performance Optimizations:

// Disable emoji scripts
remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
remove_action( 'wp_print_styles', 'print_emoji_styles' );

// Disable embed scripts
add_action( 'wp_footer', function() {
wp_dequeue_script( 'wp-embed' );
});

// Optimize heartbeat
add_filter( 'heartbeat_settings', function( $settings ) {
$settings['interval'] = 60; // 60 seconds
return $settings;
});

Security Hardening:

// Remove WordPress version from head
remove_action( 'wp_head', 'wp_generator' );

// Disable XML-RPC
add_filter( 'xmlrpc_enabled', '__return_false' );

// Hide login errors
add_filter( 'login_errors', function() {
return 'Invalid credentials.';
});

Admin Customizations:

// Custom admin footer text
add_filter( 'admin_footer_text', function() {
return 'Powered by <a href="https://rhinogrp.com">Rhino Group</a>';
});

// Remove dashboard widgets
add_action( 'wp_dashboard_setup', function() {
remove_meta_box( 'dashboard_primary', 'dashboard', 'side' );
remove_meta_box( 'dashboard_quick_press', 'dashboard', 'side' );
});

BigCommerce Enhancements:

// Improve product import performance
add_filter( 'bigcommerce/import/batch_size', function() {
return 25; // Process 25 products per batch
});

// Custom product URL structure
add_filter( 'bigcommerce/product/permalink', function( $url, $post_id ) {
$category = get_the_terms( $post_id, 'bc_category' );
if ( $category ) {
return home_url( "/shop/{$category[0]->slug}/" . basename( $url ) );
}
return $url;
}, 10, 2 );

Suma BazaarVoice Integrator

Overview

Version: 1.1.4
Purpose: Product review integration with automated XML feed generation

Features

  • Automated product feed generation
  • Review display on product pages
  • Rating aggregation
  • Schema.org markup
  • Variant support

Configuration

// wp-config.php
define( 'BAZAARVOICE_CLIENT_NAME', 'scentlok' );
define( 'BAZAARVOICE_SITE_ID', 'main_site' );
define( 'BAZAARVOICE_API_KEY', 'your-api-key' );

Feed Generation

// Generate product feed for BazaarVoice
add_action( 'bazaarvoice/generate_feed', function() {
$products = get_posts([
'post_type' => 'bigcommerce_product',
'posts_per_page' => -1,
'post_status' => 'publish',
]);

$xml = new SimpleXMLElement( '<?xml version="1.0" encoding="UTF-8"?><Feed></Feed>' );

foreach ( $products as $product ) {
$product_node = $xml->addChild( 'Product' );
$product_node->addChild( 'ExternalId', get_post_meta( $product->ID, 'bc_id', true ) );
$product_node->addChild( 'Name', htmlspecialchars( $product->post_title ) );
$product_node->addChild( 'ProductPageUrl', get_permalink( $product->ID ) );
$product_node->addChild( 'ImageUrl', get_the_post_thumbnail_url( $product->ID, 'large' ) );

// Add category
$categories = get_the_terms( $product->ID, 'bc_category' );
if ( $categories ) {
$product_node->addChild( 'CategoryPath', $categories[0]->name );
}
}

// Save feed
$xml->asXML( ABSPATH . 'bazaarvoice-feed.xml' );
});

// Schedule daily feed generation
if ( ! wp_next_scheduled( 'bazaarvoice/generate_feed' ) ) {
wp_schedule_event( time(), 'daily', 'bazaarvoice/generate_feed' );
}

Display Reviews

// Add reviews to product page
add_action( 'bigcommerce/product/after_content', function( $post_id ) {
$bc_id = get_post_meta( $post_id, 'bc_id', true );
?>
<div class="bv-reviews" data-product-id="<?php echo esc_attr( $bc_id ); ?>">
<div id="BVRRContainer"></div>
</div>

<script>
$BV.ui('rr', 'show_reviews', {
productId: '<?php echo esc_js( $bc_id ); ?>'
});
</script>
<?php
});

Suma Analytics

Overview

Version: 1.3.5
Purpose: E-commerce event tracking for Google Analytics 4

Tracked Events

  • view_item_list - Product listing viewed
  • view_item - Product detail viewed
  • add_to_cart - Product added to cart
  • begin_checkout - Checkout initiated
  • purchase - Order completed

Implementation

// Track product view
add_action( 'bigcommerce/template/product/single', function( $post_id ) {
$product_data = [
'item_id' => get_post_meta( $post_id, 'bc_id', true ),
'item_name' => get_the_title( $post_id ),
'item_category' => get_product_category( $post_id ),
'price' => get_post_meta( $post_id, 'bc_price', true ),
];
?>
<script>
gtag('event', 'view_item', {
currency: 'USD',
value: <?php echo esc_js( $product_data['price'] ); ?>,
items: [<?php echo json_encode( $product_data ); ?>]
});
</script>
<?php
});

// Track add to cart
add_action( 'wp_ajax_suma_track_add_to_cart', function() {
$product_id = intval( $_POST['product_id'] );
$quantity = intval( $_POST['quantity'] );

$product_data = [
'item_id' => get_post_meta( $product_id, 'bc_id', true ),
'item_name' => get_the_title( $product_id ),
'price' => get_post_meta( $product_id, 'bc_price', true ),
'quantity' => $quantity,
];

wp_send_json_success( $product_data );
});

Back in Stock Notifications

Overview

Version: 1.3.0
Purpose: Product availability alerts with Klaviyo integration

Features

  • Email notification signup forms
  • Klaviyo list integration
  • WooCommerce stock monitoring
  • Automatic email sending when back in stock

Configuration

// wp-config.php
define( 'KLAVIYO_API_KEY', 'your-private-api-key' );
define( 'KLAVIYO_LIST_ID', 'back-in-stock-list-id' );

Display Form

// Show notification form on out-of-stock products
add_action( 'bigcommerce/product/after_price', function( $post_id ) {
$inventory = get_post_meta( $post_id, 'bc_inventory_level', true );

if ( $inventory <= 0 ) :
?>
<div class="back-in-stock-form">
<p class="text-red-600 font-semibold mb-4">Currently Out of Stock</p>
<form class="bis-form" data-product-id="<?php echo esc_attr( $post_id ); ?>">
<input
type="email"
name="email"
placeholder="Enter your email"
required
class="w-full px-4 py-2 border rounded-md mb-2"
/>
<button type="submit" class="btn-primary w-full py-2 rounded-md">
Notify When Available
</button>
</form>
</div>
<?php
endif;
});

Subscribe Customer

// Handle form submission
add_action( 'wp_ajax_bis_subscribe', 'handle_bis_subscribe' );
add_action( 'wp_ajax_nopriv_bis_subscribe', 'handle_bis_subscribe' );

function handle_bis_subscribe() {
$email = sanitize_email( $_POST['email'] );
$product_id = intval( $_POST['product_id'] );

// Add to Klaviyo list
$klaviyo = new \Klaviyo\Client( KLAVIYO_API_KEY );

$profile = [
'email' => $email,
'properties' => [
'product_id' => get_post_meta( $product_id, 'bc_id', true ),
'product_name' => get_the_title( $product_id ),
'product_url' => get_permalink( $product_id ),
'notified_date' => current_time( 'mysql' ),
],
];

$klaviyo->lists->addSubscriber( KLAVIYO_LIST_ID, $profile );

wp_send_json_success([ 'message' => 'You will be notified when this product is back in stock!' ]);
}

BigCommerce Middleware for Narvar

Overview

Version: 1.2.20
Purpose: Order tracking and shipment integration

Features

  • Order webhook processing
  • Shipment data sync
  • Tracking number updates
  • Queue system with retry logic
  • Automated cron jobs

Configuration

// wp-config.php
define( 'NARVAR_API_KEY', 'your-narvar-api-key' );
define( 'NARVAR_RETAILER_MONIKER', 'scentlok' );
define( 'NARVAR_API_URL', 'https://api.narvar.com/v1' );

Webhook Processing

// Register order webhook
add_action( 'bigcommerce/webhook/order_created', function( $order_data ) {
// Queue for Narvar processing
queue_narvar_order( $order_data );
});

function queue_narvar_order( $order_data ) {
global $wpdb;

$wpdb->insert(
$wpdb->prefix . 'narvar_queue',
[
'order_id' => $order_data['id'],
'order_data' => json_encode( $order_data ),
'status' => 'pending',
'attempts' => 0,
'created_at' => current_time( 'mysql' ),
]
);
}

// Process queue (cron job)
add_action( 'narvar/process_queue', function() {
global $wpdb;

$queue_items = $wpdb->get_results(
"SELECT * FROM {$wpdb->prefix}narvar_queue
WHERE status = 'pending'
AND attempts < 5
LIMIT 10"
);

foreach ( $queue_items as $item ) {
try {
send_order_to_narvar( json_decode( $item->order_data, true ) );

$wpdb->update(
$wpdb->prefix . 'narvar_queue',
[ 'status' => 'complete' ],
[ 'id' => $item->id ]
);
} catch ( Exception $e ) {
$wpdb->update(
$wpdb->prefix . 'narvar_queue',
[ 'attempts' => $item->attempts + 1 ],
[ 'id' => $item->id ]
);
}
}
});

Send Order to Narvar

function send_order_to_narvar( $order_data ) {
$url = NARVAR_API_URL . '/orders';

$payload = [
'order_number' => $order_data['id'],
'order_date' => $order_data['date_created'],
'customer_email' => $order_data['billing_address']['email'],
'items' => array_map( function( $item ) {
return [
'sku' => $item['sku'],
'name' => $item['name'],
'quantity' => $item['quantity'],
];
}, $order_data['products'] ),
];

$response = wp_remote_post( $url, [
'headers' => [
'Authorization' => 'Bearer ' . NARVAR_API_KEY,
'Content-Type' => 'application/json',
],
'body' => json_encode( $payload ),
]);

if ( is_wp_error( $response ) ) {
throw new Exception( $response->get_error_message() );
}

$status_code = wp_remote_retrieve_response_code( $response );
if ( $status_code !== 200 ) {
throw new Exception( "Narvar API error: {$status_code}" );
}
}

Plugin Dependencies

Required PHP Extensions

# Check PHP extensions
php -m | grep -E 'curl|json|mbstring|xml|gd'

Composer Packages

{
"require": {
"php": ">=8.1",
"guzzlehttp/guzzle": "^7.5",
"monolog/monolog": "^3.0"
}
}

Debugging

Enable Plugin Debug Mode

// wp-config.php
define( 'SUMA_DEBUG', true );
define( 'WP_DEBUG_LOG', true );

// Check logs
tail -f wp-content/debug.log

Common Issues

Dealer Locator not showing locations:

# Check Google Maps API key
wp option get suma_dealer_locator_settings

# Verify dealer posts exist
wp post list --post_type=dealer

BazaarVoice reviews not loading:

// Check browser console for BV errors
console.log($BV);

// Verify product ID matches
console.log(document.querySelector('[data-product-id]').dataset.productId);