Developer Guide - Getting Started
SkunkCRM is built with extensibility in mind. This guide will help you get started with developing extensions, integrations, and customizations for SkunkCRM.
Architecture Overview
SkunkCRM follows WordPress plugin best practices and provides multiple extension points:
- Hooks & Filters: Over 100 action and filter hooks for maximum extensibility
- REST API: Comprehensive REST API for integrations
- Database Access: Direct database access with custom tables
- Class System: Object-oriented architecture with dependency injection
Quick Start
1. Basic Hook Usage
The simplest way to extend SkunkCRM is through WordPress hooks:
// Add to your theme's functions.php or custom plugin // Log when contacts are created add_action('skunkcrm_after_contact_create', function($contact_id, $data) { error_log("New contact created: {$data['name']} (ID: $contact_id)"); }); // Modify contact data before saving add_filter('skunkcrm_contact_data_before_save', function($data, $contact_id) { // Auto-format phone numbers if (isset($data['phone'])) { $data['phone'] = preg_replace('/[^0-9]/', '', $data['phone']); $data['phone'] = preg_replace('/(\d{3})(\d{3})(\d{4})/', '($1) $2-$3', $data['phone']); } return $data; }, 10, 2); // Add custom contact statuses add_filter('skunkcrm_contact_status_options', function($statuses) { $statuses['vip'] = 'VIP Customer'; $statuses['partner'] = 'Business Partner'; return $statuses; });
2. REST API Integration
::rest-api-banner::
3. Custom Plugin Structure
Create a structured plugin to extend SkunkCRM:
<?php /** * Plugin Name: SkunkCRM Extension * Description: Custom extensions for SkunkCRM * Version: 1.0.0 */ // Prevent direct access if (!defined('ABSPATH')) { exit; } class SkunkCRM_Extension { public function __construct() { add_action('plugins_loaded', [$this, 'init']); } public function init() { // Ensure SkunkCRM is active if (!class_exists('SkunkCRM')) { add_action('admin_notices', [$this, 'missing_skunkcrm_notice']); return; } // Initialize your extensions $this->init_hooks(); $this->init_rest_api(); $this->init_admin_pages(); } private function init_hooks() { // Contact management hooks add_action('skunkcrm_after_contact_create', [$this, 'on_contact_created'], 10, 2); add_filter('skunkcrm_contact_data_before_save', [$this, 'modify_contact_data'], 10, 2); // Automation hooks add_filter('skunkcrm_automation_action_types', [$this, 'add_custom_actions']); add_action('skunkcrm_before_automation_execute', [$this, 'log_automation'], 10, 3); } private function init_rest_api() { add_action('rest_api_init', [$this, 'register_custom_endpoints']); } private function init_admin_pages() { add_action('admin_menu', [$this, 'add_admin_menu']); } public function on_contact_created($contact_id, $data) { // Custom logic when contact is created $this->sync_to_external_system($contact_id, $data); } public function modify_contact_data($data, $contact_id) { // Add custom validation or data transformation if (isset($data['email']) && !empty($data['email'])) { $data['email_domain'] = substr(strrchr($data['email'], "@"), 1); } return $data; } public function add_custom_actions($actions) { $actions['send_to_crm'] = 'Send to External CRM'; $actions['create_calendar_event'] = 'Create Calendar Event'; return $actions; } public function register_custom_endpoints() { register_rest_route('skunkcrm-extension/v1', '/sync', [ 'methods' => 'POST', 'callback' => [$this, 'sync_endpoint'], 'permission_callback' => [$this, 'check_permissions'] ]); } public function sync_endpoint($request) { // Custom API endpoint $contact_id = $request->get_param('contact_id'); // Sync logic here $result = $this->sync_contact($contact_id); return new WP_REST_Response($result, 200); } public function missing_skunkcrm_notice() { echo '<div class="notice notice-error"><p>SkunkCRM Extension requires SkunkCRM to be installed and activated.</p></div>'; } private function sync_to_external_system($contact_id, $data) { // Implement external system integration } private function sync_contact($contact_id) { // Implement sync logic return ['success' => true, 'contact_id' => $contact_id]; } public function check_permissions() { return current_user_can('manage_options'); } } new SkunkCRM_Extension();
Common Extension Patterns
1. External System Integration
class SkunkCRM_Mailchimp_Integration { private $api_key; private $list_id; public function __construct($api_key, $list_id) { $this->api_key = $api_key; $this->list_id = $list_id; // Sync contacts to Mailchimp when created/updated add_action('skunkcrm_after_contact_create', [$this, 'sync_to_mailchimp'], 10, 2); add_action('skunkcrm_after_contact_update', [$this, 'update_in_mailchimp'], 10, 3); add_action('skunkcrm_after_contact_delete', [$this, 'remove_from_mailchimp'], 10, 2); } public function sync_to_mailchimp($contact_id, $data) { if (empty($data['email'])) { return; } $mailchimp_data = [ 'email_address' => $data['email'], 'status' => 'subscribed', 'merge_fields' => [ 'FNAME' => $this->get_first_name($data['name']), 'LNAME' => $this->get_last_name($data['name']), 'COMPANY' => $data['company'] ?? '', 'PHONE' => $data['phone'] ?? '' ], 'tags' => ['skunkcrm'] ]; $this->mailchimp_api_call("lists/{$this->list_id}/members", $mailchimp_data); } public function update_in_mailchimp($contact_id, $new_data, $old_data) { // Update subscriber in Mailchimp if (!empty($new_data['email'])) { $subscriber_hash = md5(strtolower($new_data['email'])); $this->mailchimp_api_call( "lists/{$this->list_id}/members/{$subscriber_hash}", ['merge_fields' => ['FNAME' => $this->get_first_name($new_data['name'])]], 'PATCH' ); } } private function mailchimp_api_call($endpoint, $data, $method = 'POST') { $url = "https://us1.api.mailchimp.com/3.0/{$endpoint}"; return wp_remote_request($url, [ 'method' => $method, 'headers' => [ 'Authorization' => 'Basic ' . base64_encode('user:' . $this->api_key), 'Content-Type' => 'application/json' ], 'body' => json_encode($data) ]); } }
2. Custom Automation Actions
class SkunkCRM_Custom_Automation_Actions { public function __construct() { add_filter('skunkcrm_automation_action_types', [$this, 'add_action_types']); add_filter('skunkcrm_custom_action_type', [$this, 'handle_custom_action'], 10, 4); } public function add_action_types($actions) { $actions['send_sms'] = 'Send SMS'; $actions['create_deal'] = 'Create Deal'; $actions['assign_to_user'] = 'Assign to User'; $actions['add_to_sequence'] = 'Add to Email Sequence'; return $actions; } public function handle_custom_action($result, $action_type, $config, $contact) { switch ($action_type) { case 'send_sms': return $this->send_sms($config, $contact); case 'create_deal': return $this->create_deal($config, $contact); case 'assign_to_user': return $this->assign_to_user($config, $contact); case 'add_to_sequence': return $this->add_to_sequence($config, $contact); } return $result; } private function send_sms($config, $contact) { if (empty($contact['phone'])) { return ['success' => false, 'error' => 'No phone number']; } $message = str_replace('{name}', $contact['name'], $config['message']); // Use Twilio, TextMagic, or other SMS service return $this->sms_service_send($contact['phone'], $message); } private function create_deal($config, $contact) { global $wpdb; $deal_data = [ 'title' => str_replace('{contact_name}', $contact['name'], $config['title']), 'amount' => $config['amount'] ?? 0, 'stage' => $config['stage'] ?? 'prospecting', 'contact_id' => $contact['id'], 'assigned_to' => $config['assigned_to'] ?? get_current_user_id(), 'created_at' => current_time('mysql') ]; $result = $wpdb->insert( $wpdb->prefix . 'skunk_deals', $deal_data, ['%s', '%f', '%s', '%d', '%d', '%s'] ); return [ 'success' => $result !== false, 'deal_id' => $wpdb->insert_id ]; } }
3. Contact Data Enhancement
class SkunkCRM_Data_Enrichment { public function __construct() { add_filter('skunkcrm_contact_data_before_save', [$this, 'enrich_contact_data'], 20, 2); add_action('skunkcrm_after_contact_create', [$this, 'background_enrichment'], 10, 2); } public function enrich_contact_data($data, $contact_id) { // Real-time enrichment for critical fields if (isset($data['email']) && !empty($data['email'])) { $domain = substr(strrchr($data['email'], "@"), 1); $data['email_domain'] = $domain; // Check if it's a business email $personal_domains = ['gmail.com', 'yahoo.com', 'hotmail.com', 'outlook.com']; $data['is_business_email'] = !in_array($domain, $personal_domains); } // Auto-format phone numbers if (isset($data['phone']) && !empty($data['phone'])) { $data['phone'] = $this->format_phone_number($data['phone']); } // Capitalize names if (isset($data['name'])) { $data['name'] = ucwords(strtolower($data['name'])); } return $data; } public function background_enrichment($contact_id, $data) { // Schedule background enrichment wp_schedule_single_event(time() + 60, 'skunkcrm_enrich_contact', [$contact_id]); } private function format_phone_number($phone) { // Remove all non-numeric characters $phone = preg_replace('/[^0-9]/', '', $phone); // Format as (XXX) XXX-XXXX for US numbers if (strlen($phone) === 10) { return preg_replace('/(\d{3})(\d{3})(\d{4})/', '($1) $2-$3', $phone); } return $phone; } } // Register the enrichment hook add_action('skunkcrm_enrich_contact', function($contact_id) { // Fetch additional data from external APIs $contact = SkunkCRM_Contacts::get_by_id($contact_id); if ($contact && !empty($contact['email'])) { // Example: Enrich with Clearbit, FullContact, etc. $enriched_data = fetch_from_clearbit($contact['email']); if ($enriched_data) { SkunkCRM_Contacts::update($contact_id, [ 'company' => $enriched_data['company'] ?? $contact['company'], 'title' => $enriched_data['title'] ?? null, 'linkedin' => $enriched_data['linkedin'] ?? null ]); } } });
Development Guidelines
1. Hook Usage Best Practices
- Use appropriate priority: Default is 10, use higher numbers to run later
- Check for plugin existence: Always verify SkunkCRM is active
- Handle errors gracefully: Don't break core functionality
- Follow WordPress standards: Use WordPress coding standards
2. Performance Considerations
- Avoid heavy operations in hooks: Use background processing for intensive tasks
- Cache external API calls: Implement proper caching mechanisms
- Use transients: For temporary data storage
- Database optimization: Use proper indexes and queries
3. Security Best Practices
- Sanitize all inputs: Use WordPress sanitization functions
- Validate permissions: Check user capabilities
- Escape outputs: Prevent XSS attacks
- Use nonces: For form submissions
4. Testing Your Extensions
// Example test for contact creation hook class Test_SkunkCRM_Extension extends WP_UnitTestCase { public function test_contact_creation_hook() { // Create a contact $contact_data = [ 'name' => 'Test Contact', 'email' => 'test@example.com', 'status' => 'prospect' ]; $contact_id = SkunkCRM_Contacts::create($contact_data); // Verify your hook ran $this->assertTrue(did_action('skunkcrm_after_contact_create') > 0); // Verify your modifications $contact = SkunkCRM_Contacts::get_by_id($contact_id); $this->assertEquals('example.com', $contact['email_domain']); } }
Next Steps
- Explore the Hooks & Filters Reference for complete documentation
- Check out the REST API Documentation for integration details
- Contact our support team for developer assistance and questions