Custom Plugins
ScentLok uses several custom Rhino Group plugins to extend WordPress and BigCommerce functionality.
Plugin Overview
| Plugin | Version | Purpose |
|---|---|---|
| Suma Dealer Locator | 3.0.2 | Store finder with Google Maps |
| Suma Patches | 1.5.19 | WordPress core customizations |
| Suma BazaarVoice Integrator | 1.1.4 | Product review integration |
| Suma Analytics | 1.3.5 | E-commerce event tracking |
| Back in Stock Notifications | 1.3.0 | Product availability alerts |
| BigCommerce Middleware for Narvar | 1.2.20 | Order 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 viewedview_item- Product detail viewedadd_to_cart- Product added to cartbegin_checkout- Checkout initiatedpurchase- 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);