Untitled Session
2026-05-28 03:52 UTC · 35 messages · cli
You2026-05-28 03:55 UTC
Create the automation engine PHP files for the Profe CRM WordPress plugin at /var/www/html/wp-content/plugins/profe-crm/includes/. These handle the automation funnels, triggers, actions, segment builder, and runner.
Files to create:
1. class-automation.php
2. class-automation-step.php
3. class-automation-runner.php
4. class-triggers.php
5. class-actions.php
6. class-segment-builder.php
Donna2026-05-28 03:55 UTC
Tool2026-05-28 03:55 UTC
{"total_count": 8, "files": ["/var/www/html/wp-content/plugins/profe-crm/includes/class-activity.php", "/var/www/html/wp-content/plugins/profe-crm/includes/class-contact-meta.php", "/var/www/html/wp-content/plugins/profe-crm/includes/class-contact.php", "/var/www/html/wp-content/plugins/profe-crm/in...
Tool2026-05-28 03:55 UTC
{"total_count": 9, "files": ["/var/www/html/wp-content/plugins/profe-crm/includes/class-activity.php", "/var/www/html/wp-content/plugins/profe-crm/includes/class-contact-meta.php", "/var/www/html/wp-content/plugins/profe-crm/includes/class-contact.php", "/var/www/html/wp-content/plugins/profe-crm/in...
Donna2026-05-28 03:55 UTC
Tool2026-05-28 03:55 UTC
{"content": " 1|<?php\n 2|/**\n 3| * Main plugin class. Singleton bootstrap that initializes all components.\n 4| */\n 5|\n 6|defined('ABSPATH') || exit;\n 7|\n 8|class Profe_CRM {\n 9|\n 10| private static $instance = null;\n 11|\n 12| /** @var Pro...
Tool2026-05-28 03:55 UTC
{"content": " 1|<?php\n 2|/**\n 3| * Database schema for Profe CRM.\n 4| * Creates all custom tables on activation.\n 5| * All tables use the wp_ prefix via $wpdb->prefix.\n 6| */\n 7|\n 8|defined('ABSPATH') || exit;\n 9|\n 10|class Profe_CRM_Database {\n 11...
Tool2026-05-28 03:55 UTC
{"content": " 1|<?php\n 2|/**\n 3| * LabelRelation model for Profe CRM.\n 4| * Manages the polymorphic wp_profe_crm_label_relations pivot table.\n 5| */\n 6|\n 7|defined('ABSPATH') || exit;\n 8|\n 9|class Profe_CRM_Label_Relation {\n 10|\n 11| /**\n 12...
Tool2026-05-28 03:55 UTC
{"content": " 1|<?php\n 2|/**\n 3| * ContactNotes model for Profe CRM.\n 4| * Manages the wp_profe_crm_contact_notes table.\n 5| */\n 6|\n 7|defined('ABSPATH') || exit;\n 8|\n 9|class Profe_CRM_Contact_Notes {\n 10|\n 11| /**\n 12| * Add a note to ...
Tool2026-05-28 03:55 UTC
{"content": " 1|<?php\n 2|/**\n 3| * ContactMeta model for Profe CRM.\n 4| * Manages the wp_profe_crm_contact_meta table.\n 5| */\n 6|\n 7|defined('ABSPATH') || exit;\n 8|\n 9|class Profe_CRM_Contact_Meta {\n 10|\n 11| /**\n 12| * Get a single meta...
Tool2026-05-28 03:55 UTC
{"content": " 1|<?php\n 2|/**\n 3| * Contact model for Profe CRM.\n 4| * Manages the wp_profe_crm_contacts table with CRUD, search, label, activity, and meta helpers.\n 5| */\n 6|\n 7|defined('ABSPATH') || exit;\n 8|\n 9|class Profe_CRM_Contact {\n 10|\n 11|...
Tool2026-05-28 03:55 UTC
{"content": " 1|<?php\n 2|/**\n 3| * Activity model for Profe CRM.\n 4| * Manages the wp_profe_crm_activities table (engagement event log).\n 5| */\n 6|\n 7|defined('ABSPATH') || exit;\n 8|\n 9|class Profe_CRM_Activity {\n 10|\n 11| /**\n 12| * Tra...
Tool2026-05-28 03:55 UTC
{"content": " 1|<?php\n 2|/**\n 3| * Label model for Profe CRM.\n 4| * Manages the wp_profe_crm_labels table.\n 5| */\n 6|\n 7|defined('ABSPATH') || exit;\n 8|\n 9|class Profe_CRM_Label {\n 10|\n 11| /**\n 12| * Get a label by primary key.\n 13|...
Tool2026-05-28 03:55 UTC
{"content": " 1|<?php\n 2|/**\n 3| * Plugin Name: Profe CRM\n 4| * Description: Member relationship management for the Profe community platform.\n 5| * Contact management, labels, segmentation, automation funnels,\n 6| * activity tracking, and REST A...
Donna2026-05-28 03:55 UTC
Now I have a complete picture of the codebase. Let me create all 6 files.
Tool2026-05-28 03:55 UTC
{"bytes_written": 16738, "dirs_created": true, "lint": {"status": "skipped", "message": "No linter for .php files"}}
Donna2026-05-28 03:55 UTC
Tool2026-05-28 03:55 UTC
{"bytes_written": 10896, "dirs_created": true, "lint": {"status": "skipped", "message": "No linter for .php files"}}
Donna2026-05-28 03:55 UTC
Tool2026-05-28 03:55 UTC
{"bytes_written": 7673, "dirs_created": true, "lint": {"status": "skipped", "message": "No linter for .php files"}}
Donna2026-05-28 03:55 UTC
Tool2026-05-28 03:55 UTC
{"bytes_written": 15860, "dirs_created": true, "lint": {"status": "skipped", "message": "No linter for .php files"}}
Donna2026-05-28 03:55 UTC
Tool2026-05-28 03:55 UTC
{"bytes_written": 27109, "dirs_created": true, "lint": {"status": "skipped", "message": "No linter for .php files"}}
Donna2026-05-28 03:55 UTC
Tool2026-05-28 03:55 UTC
{"bytes_written": 33932, "dirs_created": true, "lint": {"status": "skipped", "message": "No linter for .php files"}}
Donna2026-05-28 03:55 UTC
Tool2026-05-28 03:55 UTC
{"output": "-rw-r--r-- 1 root root 15860 May 28 03:53 class-actions.php\n-rw-r--r-- 1 root root 27109 May 28 03:54 class-automation-runner.php\n-rw-r--r-- 1 root root 10896 May 28 03:53 class-automation-step.php\n-rw-r--r-- 1 root root 16738 May 28 03:52 class-automation.php\n-rw-r--r-- 1 root root ...
Donna2026-05-28 03:55 UTC
Tool2026-05-28 03:55 UTC
{"output": "=== class-automation.php ===\nNo syntax errors detected in class-automation.php\n=== class-automation-step.php ===\nNo syntax errors detected in class-automation-step.php\n=== class-automation-runner.php ===\nNo syntax errors detected in class-automation-runner.php\n=== class-triggers.ph...
Donna2026-05-28 03:55 UTC
Tool2026-05-28 03:55 UTC
{"total_count": 13, "matches": [{"path": "/var/www/html/wp-content/plugins/profe-crm/includes/class-label-relation.php", "line": 9, "content": "class Profe_CRM_Label_Relation {"}, {"path": "/var/www/html/wp-content/plugins/profe-crm/includes/class-contact.php", "line": 9, "content": "class Profe_CRM...
Donna2026-05-28 03:55 UTC
Tool2026-05-28 03:55 UTC
{"total_count": 6, "matches": [{"path": "/var/www/html/wp-content/plugins/profe-crm/profe-crm.php", "line": 34, "content": "require_once PROFE_CRM_PLUGIN_DIR . 'includes/class-automation.php';"}, {"path": "/var/www/html/wp-content/plugins/profe-crm/profe-crm.php", "line": 35, "content": "require_onc...
Donna2026-05-28 03:55 UTC
All 6 files are created, pass PHP syntax validation, and the autoloader in `profe-crm.php` already references them correctly. Here's the summary:
---
## Summary
### Files Created
**1. `/var/www/html/wp-content/plugins/profe-crm/includes/class-automation.php`** (16.7 KB)
- **Class:** `Profe_CRM_Automation`
- **CRUD:** `get()`, `create()`, `update()`, `delete()`, `get_all()`, `count()`, `count_by_status()`
- **Step management:** `get_steps()`, `add_step()`, `update_step()`, `remove_step()`, `reorder_steps()`
- **Stats:** `get_run_count()`, `get_completed_count()`, `get_active_contacts()`
- All serialized fields (conditions, settings) handle `maybe_serialize()/maybe_unserialize()`
**2. `/var/www/html/wp-content/plugins/profe-crm/includes/class-automation-step.php`** (10.9 KB)
- **Class:** `Profe_CRM_Automation_Step`
- **CRUD:** `get()`, `create()`, `update()`, `delete()`
- **Validation:** `is_valid_action()` (9 valid actions), `is_valid_benchmark()` (3 valid benchmarks)
- **Schema provider:** `get_action_settings_schema()` returns field definitions for all action types
**3. `/var/www/html/wp-content/plugins/profe-crm/includes/class-automation-runner.php`** (27.1 KB)
- **Class:** `Profe_CRM_Automation_Runner`
- Core execution engine with full pipeline:
- `start_automation()` — creates run entries, processes first step
- `process_step()` — dispatches actions/benchmarks/wait steps
- `process_next_step()` / `advance_to_next_step()` — walks the step chain
- `process_delayed_steps()` — cron handler that processes `status='waiting'` runs whose delay has elapsed
- `cancel_automation()` / `complete_automation()`
- `get_contact_automations()`
- Benchmark listeners wired via `profe_crm_label_applied`, `profe_crm_label_removed`, `profe_crm_activity_recorded` hooks
- Fallback direct execution (`execute_action_direct()`) if the Actions registry isn't available yet
- Uses `class_exists()` guards throughout
**4. `/var/www/html/wp-content/plugins/profe-crm/includes/class-triggers.php`** (7.7 KB)
- **Class:** `Profe_CRM_Triggers`
- Registry pattern: `register()`, `get_registered()`, `get_trigger()`, `fire()`
- 9 default triggers registered: `user_registered`, `contact_label_applied`, `contact_label_removed`, `studio_joined`, `studio_left`, `reputation_threshold`, `activity_occurred`, `contact_meta_updated`, `user_logged_in`
- `fire()` queries all ACTIVE automations matching the trigger name and starts them via the Runner
- Includes basic condition evaluation framework
**5. `/var/www/html/wp-content/plugins/profe-crm/includes/class-actions.php`** (15.9 KB)
- **Class:** `Profe_CRM_Actions`
- Registry + executor pattern: `register()`, `get_registered()`, `get_action()`, `execute()`
- 9 default actions with settings schemas: `apply_label`, `remove_label`, `send_notification`, `add_note`, `change_status`, `http_webhook`, `update_meta`, `remove_from_automation`, `wait`
- Each action has a dedicated private executor method:
- `apply_label`/`remove_label` → `Profe_CRM_Label_Relation::attach/detach()`
- `send_notification` → `bp_notifications_add_notification()` with fallback to ContactNotes
- `add_note` → `Profe_CRM_Contact_Notes::add()` (returns note_id as string)
- `change_status` → direct `$wpdb->update()` on contacts table
- `http_webhook` → `wp_remote_post()` with contact data payload
- `update_meta` → `Profe_CRM_Contact_Meta::set()`
- Extensible via `profe_crm_execute_action` filter
**6. `/var/www/html/wp-content/plugins/profe-crm/includes/class-segment-builder.php`** (33.9 KB)
- **Class:** `Profe_CRM_Segment_Builder`
- 5-layer filter query engine using EXISTS subqueries (no cartesian joins):
- **General** — contact field filters with operators: `=`, `!=`, `>`, `<`, `>=`, `<=`, `LIKE`, `NOT LIKE`, `IN`, `NOT IN`, `BETWEEN`, `IS NULL`, `IS NOT NULL`
- **Labels** — `in` (any), `not_in` (none), `in_all` (has all), `not_in_all` (missing at least one)
- **Custom Fields** (meta) — `=`, `!=`, `LIKE`, `NOT LIKE`, `IN`, `NOT IN`, `EXISTS`, `NOT EXISTS`
- **Activities** — `in_last_days`, `before_date`, `after_date`, `never`, `count_gt`, `count_lt`
- **Automation** — `entered`, `not_entered`, `completed`, `active`, `cancelled`
- `query()` returns contact IDs, `count()` returns integer
- Segment CRUD: `save_segment()`, `get_segment()`, `get_segments()`, `update_segment()`, `delete_segment()`
- `query_segment()` loads a saved segment, decodes filters JSON, and runs the query
- Automatic unique slug generation, JSON encoding/decoding for filters
### Key Design Decisions
- All classes follow existing plugin conventions (no namespaces, `Profe_CRM_` prefix, `$wpdb->global`, `current_time('mysql')`)
- All user input goes through `$wpdb->prepare()` — no raw SQL with concatenated values
- `class_exists()` guards protect against missing model classes during early initialization
- The Runner lazy-loads references to Triggers/Actions from the main CRM instance via `init` hook
- Benchmark automations use WordPress action hooks to advance waiting runs when conditions are met