WPResidence CRM Overview for Developers
This article is the developer counterpart to the CRM Overview guide. It maps the WPResidence real estate CRM plugin structure — bootstrap, page template routing, database activation, enqueues, and dashboard navigation — so you can extend or debug the plugin from a child theme or custom add-on without guessing.
All references are from wp-content/plugins/wpestate-crm/, plugin version 5.2.2, text domain wpestate-crm.
Plugin Constants and Bootstrap
The main plugin file wpestate-crm.php defines five constants up front (lines 16-20):
WPESTATE_CRM_URL
WPESTATE_CRM_DIR_URL
WPESTATE_CRM_PATH
WPESTATE_CRM_BASE
WPESTATE_CRM_VERSION = '5.2.2'
Use WPESTATE_CRM_PATH and WPESTATE_CRM_DIR_URL from a child theme or add-on to include plugin assets.
Registered Hooks
| Hook | Callback | Purpose |
|---|---|---|
plugins_loaded |
wpestate_crm_check_plugin_functionality_loaded() |
Re-runs activation when version changes |
wp_loaded |
wpresidence_create_crm_helper_content() |
Auto-creates the 9 CRM pages on first load |
register_activation_hook |
wpestate_crm_functionality() |
Creates tables, seeds settings, schedules cron |
register_deactivation_hook |
wpestate_crm_deactivate() |
Clears CRM cron events |
wp_enqueue_scripts |
Frontend enqueue block (lines 51-179) | Conditionally loads CSS/JS on CRM pages |
Activation Routine
wpestate_crm_functionality() at lines 331-376 of wpestate-crm.php performs the full activation sequence:
wpestate_crm_create_tables()— creates 8 custom DB tables viadbDelta.wpestate_crm_seed_defaults()— writes default option values.wpestate_crm_migrate_cpt_data()— migrates legacy CPT rows into the custom tables.- Adds the
manage_crmcapability to the administrator role. - Seeds the permissions matrix for each role.
- Migrates HubSpot settings from the Redux option store.
- Seeds 27 default automation rules.
- Schedules three WP-Cron events:
wpestate_crm_process_automationswpestate_crm_daily_notificationswpestate_crm_weekly_digest
- Updates the
wpestate_crm_db_versionoption.
Forcing Re-activation
If the plugin file is replaced (for example, via SFTP or a manual upgrade) the activation hook does not fire. wpestate_crm_check_plugin_functionality_loaded() on plugins_loaded compares the stored wpestate_crm_db_version to WPESTATE_CRM_VERSION and re-runs the activation routine when they differ — safe to call repeatedly because dbDelta and all seeders are idempotent.
Page Template Registration
The dashboard is implemented as nine WordPress page templates loaded from the plugin directory. Two filters wire this up:
theme_page_templatesat line 1100 — adds the templates to the WordPress page template dropdown.page_templateat line 1127 — maps each template slug to its physical file in/page-templates/.
The slug-to-file map is returned by wpestate_crm_page_template_slugs() (lines 1127-1149).
The Nine Templates
| Slug | File |
|---|---|
wpestate-crm-dashboard.php |
CRM Overview home |
wpestate-crm-dashboard_contacts.php |
Contacts list |
wpestate-crm-dashboard_leads.php |
Leads list |
wpestate-crm-dashboard_enquiries.php |
Enquiries list |
wpestate-crm-dashboard_deals.php |
Deals pipeline |
wpestate-crm-dashboard_tasks.php |
Tasks list |
wpestate-crm-dashboard_activity.php |
Activity timeline |
wpestate-crm-dashboard_stats.php |
Stats and charts |
wpestate-crm-dashboard_automations.php |
Automations (admin-only) |
Auto-Creation of Pages
wpresidence_create_crm_helper_content() (lines 1159-1257) runs on wp_loaded. It compares a stored setup version (currently 2.1) against the code version and, when they differ, creates any missing CRM pages with the correct page template assignments.
Navigation Menu
The sidebar submenu is defined in the theme — not the plugin — in libs/dashboard_functions/dashboard_functions.php, lines 1717-1759:
$values_crm_dropdown = array(
0 => ['label' => 'Enquiries', 'link' => 'wpestate-crm-dashboard_enquiries.php'],
1 => ['label' => 'Contacts', 'link' => 'wpestate-crm-dashboard_contacts.php'],
2 => ['label' => 'Leads', 'link' => 'wpestate-crm-dashboard_leads.php'],
3 => ['label' => 'Deals', 'link' => 'wpestate-crm-dashboard_deals.php'],
4 => ['label' => 'Tasks', 'link' => 'wpestate-crm-dashboard_tasks.php'],
5 => ['label' => 'Activity', 'link' => 'wpestate-crm-dashboard_activity.php'],
6 => ['label' => 'Stats', 'link' => 'wpestate-crm-dashboard_stats.php'],
7 => ['label' => 'Automations', 'link' => 'wpestate-crm-dashboard_automations.php', 'cap' => 'administrator'],
);
Entries with a cap key are filtered with current_user_can() before rendering, which is how the Automations section stays hidden from non-admins. Active-section detection (lines 1842-1851) applies the user_tab_active class when the current template slug starts with wpestate-crm-dashboard.
Mobile Toggle
wpestate_crm_mobile_menu_toggle() in wpestate-crm.php (lines 257-293) toggles the .crm-submenu-open class. Default state: submenu hidden on mobile.
Asset Enqueuing
Frontend enqueues run on wp_enqueue_scripts and are gated on the current page template slug starting with wpestate-crm-dashboard (line 59):
| Asset | Version | Condition |
|---|---|---|
crm-dashboard.css |
2.1.2 | All CRM dashboard pages |
crm-dashboard.js |
2.1.2 | All CRM dashboard pages |
| jQuery UI Datepicker | core | Always |
| SortableJS | 1.15.6 | Deals page only |
| Chart.js | 4.4.7 | Stats page only |
Localized JS Object
wp_localize_script() (lines 122-178) exposes a global wpestate_crm_vars with:
ajaxurl—admin-ajax.phpURL.rest_urlandrest_nonce— for REST requests.nonces— a map of 17 action-specific nonces (lines 130-158).date_format— PHP format converted to jQuery UI format.currency— read from theme optionwp_estate_currency_label_main.i18n— 14 translatable strings for labels and confirmations.
Admin-side assets (crm_script.js, crm_style.css) are enqueued separately at lines 295-306 with the localized object wpestate_crm_script_vars.
Directory Layout
| Folder | Contents |
|---|---|
libs/ |
CRUD modules (one per entity), settings, permissions, security, notifications, Twilio, HubSpot subfolder, CSV, webhooks, matching, automations, migration, DB setup. |
page-templates/ |
The nine dashboard page templates. |
templates/ |
Partials used by the page templates (add/edit forms, list unit rows, detail panels). |
post-types/ |
Two legacy CPTs: contacts.php and leads.php. Present for backward compatibility; primary storage is the custom tables. |
css/ js/ images/ languages/ |
Static assets. |
logs/ |
Runtime logs written by the plugin. |
Access Control
Two layers gate access to the dashboard:
- Template guard: every CRM page template calls
wpestate_dashboard_header_permissions()as its first meaningful line. Non-logged-in visitors are redirected. - Data scope:
wpestate_return_agent_list()determines whose contacts/leads/deals the current user can see. Administrators get the full list; agents get their own IDs.
Extending the CRM
Common extension points:
- Add a new dashboard page. Register your own template via the
theme_page_templatesandpage_templatefilters using the same pattern as the plugin. - Add a menu entry. Filter the
$values_crm_dropdownstructure in your child theme by overriding the parent function, or inject a link into the sidebar via theme hooks. - Hook into activation. Register your own
register_activation_hookafter the CRM plugin so your setup runs after its tables exist. - Use plugin constants. Reference files via
WPESTATE_CRM_PATHrather than hard-coded paths.
Related Integrations
For the HubSpot integration architecture — bi-directional sync, field mapping, and the events that trigger pushes — see the dedicated HubSpot CRM integration guide.