Untitled Session
2026-06-03 16:49 UTC · 88 messages · cli
You2026-06-03 16:52 UTC
Exhaustive deep dive of the entire FluentCRM codebase (PHP + JS + CSS) on testing.jaimisaunders.com. Find EVERY paywall/upgrade/pro gate — every mechanism that blocks, nags, or prompts for payment. Report everything found with file paths, line numbers, gate mechanisms, and whether they're already handled.
Donna2026-06-03 16:52 UTC
I'll start by reading the existing mu-plugin and then systematically search every layer of the codebase.
Tool2026-06-03 16:52 UTC
{"content": " 1|<?php\n 2|/**\n 3| * Plugin Name: FluentCRM Pro Unlock\n 4| * Description: Unlocks all Pro features in FluentCRM for testing purposes.\n 5| * Version: 2.0\n 6| */\n 7|\n 8|defined('ABSPATH') || exit;\n 9|\n 10|// === 1. Define the primary Pro co...
Tool2026-06-03 16:52 UTC
{"output": "total 76\ndrwxr-xr-x 10 www-data www-data 4096 Jun 2 14:30 .\ndrwxr-xr-x 10 www-data www-data 4096 Jun 2 14:30 ..\ndrwxr-xr-x 3 www-data www-data 4096 Jun 2 14:30 Api\n-rw-r--r-- 1 www-data www-data 102 Jun 2 14:30 App.php\n-rw-r--r-- 1 www-data www-data 4106 Jun 2 14:30 Co...
Tool2026-06-03 16:52 UTC
{"output": "app\nassets\nboot\ncomposer.json\nconfig\ndatabase\nfluent-crm.php\nincludes\nindex.php\nlanguage\nreadme.txt\nvendor", "exit_code": 0, "error": null}
Donna2026-06-03 16:52 UTC
Now let me run all the systematic searches in parallel:
Tool2026-06-03 16:52 UTC
{"output": "/var/www/html/wp-content/plugins/fluent-crm/app/Views/emails/web_preview/Template.php:13: <?php if (!defined('FLUENTCAMPAIGN')): ?>\n/var/www/html/wp-content/plugins/fluent-crm/app/Views/emails/simple/Template.php:91: ...
Tool2026-06-03 16:52 UTC
{"output": "/var/www/html/wp-content/plugins/fluent-crm/app/Views/external/manage_subscription_form.php:37: <?php if (apply_filters('fluent_crm/show_unsubscribe_on_pref', false)): ?>\n/var/www/html/wp-content/plugins/fluent-crm/app/Http/Controllers/DocsController.php:60: $canAutoInstal...
Tool2026-06-03 16:52 UTC
{"output": "/var/www/html/wp-content/plugins/fluent-crm/app/Http/Controllers/WebhookBounceController.php:70: $code = fluentcrm_get_option('_fc_bounce_key');\n/var/www/html/wp-content/plugins/fluent-crm/app/Http/Controllers/SettingsController.php:339: $securityCode = fluentcrm_get_optio...
Tool2026-06-03 16:52 UTC
{"output": "/var/www/html/wp-content/plugins/fluent-crm/app/Http/Controllers/AiController.php:966: if (function_exists('wp_ai_client_prompt')) {\n/var/www/html/wp-content/plugins/fluent-crm/app/Http/Controllers/CampaignAnalyticsController.php:224: $paidStatuses = function_exists('w...
Tool2026-06-03 16:52 UTC
{"output": "/var/www/html/wp-content/plugins/fluent-crm/assets/GenericPromo.js:1:import{P as r}from\"./PromoCard.js?ver=3.1.0\";import{r as o,c as e,o as a,d as s}from\"./vendor.js?ver=3.1.0\";import{_ as i}from\"./fc-bits-ui.js?ver=3.1.0\";const n={class:\"fc_promo_body\"};const t=i({name:\"Generic...
Tool2026-06-03 16:52 UTC
{"output": "/var/www/html/wp-content/plugins/fluent-crm/app/Http/Controllers/OptionsController.php:235: * also available in fcAdmin data in frontend\n/var/www/html/wp-content/plugins/fluent-crm/app/Modules/MCP/AbilitiesRegistrar.php:21: * push it from `fluentcampaign-pro` via the `fluent_crm...
Donna2026-06-03 16:52 UTC
Now let me read the critical backend files that contain the key gate mechanisms:
Tool2026-06-03 16:52 UTC
{"content": "", "total_lines": 0, "file_size": 0, "truncated": false, "is_binary": false, "is_image": false, "error": "File not found: /var/www/html/wp-content/plugins/fluent-crm/app/Hooks/Handlers/AdminMenu.php"}
Tool2026-06-03 16:52 UTC
{"content": "", "total_lines": 0, "file_size": 0, "truncated": false, "is_binary": false, "is_image": false, "error": "File not found: /var/www/html/wp-content/plugins/fluent-crm/app/Hooks/Handlers/AdminMenu.php"}
Tool2026-06-03 16:52 UTC
{"content": "", "total_lines": 0, "file_size": 0, "truncated": false, "is_binary": false, "is_image": false, "error": "File not found: /var/www/html/wp-content/plugins/fluent-crm/app/Hooks/Handlers/AdminMenu.php"}
[Tool loop warning: same_tool_failure_warning; count=3; read_file has failed 3 times ...
Donna2026-06-03 16:52 UTC
Let me read the file from inside the Docker container:
Tool2026-06-03 16:52 UTC
{"output": "<?php\n\nnamespace FluentCrm\\App\\Hooks\\Handlers;\n\nuse FluentCrm\\App\\Models\\Lists;\nuse FluentCrm\\App\\Models\\Tag;\nuse FluentCrm\\App\\Services\\Helper;\nuse FluentCrm\\App\\Services\\PermissionManager;\nuse FluentCrm\\App\\Services\\TransStrings;\nuse FluentCrm\\Framework\\Sup...
Tool2026-06-03 16:52 UTC
{"output": "1181 /var/www/html/wp-content/plugins/fluent-crm/app/Hooks/Handlers/AdminMenu.php", "exit_code": 0, "error": null}
Donna2026-06-03 16:52 UTC
Let me use docker exec to read the key sections of AdminMenu.php. I'll extract the getAdminVars() method and the FLUENTCAMPAIGN check:
Tool2026-06-03 16:52 UTC
{"output": "<?php\n }, 99999);\n\n $this->loadCss();\n\n wp_enqueue_script('dompurify', fluentCrmMix('libs/purify/purify.min.js'), [], $this->version, true);\n\n $inlineCss = Helper::generateThemePrefCss();\n wp_add_inline_style('fluentcrm_app_global', $inlineCss);...
Tool2026-06-03 16:52 UTC
{"output": "if (defined('FLUENTMAIL')) {\n $smtpSettings = get_option('fluentmail-settings', []);\n $mappings = (array) Arr::get($smtpSettings, 'mappings', []);\n if ($mappings) {\n $verifiedSenders = array_keys($mappings);\n }\n }\n\...
Tool2026-06-03 16:52 UTC
{"output": "$dashboardPermission,\n 'fluentcrm-admin#/funnels',\n array($this, 'render')\n );\n }\n\n do_action('fluent_crm/after_core_menu_items', $permissions, $isAdmin);\n\n if (in_array('fcrm_manage_settings', $permissions)) {\n\n ...
Donna2026-06-03 16:52 UTC
Let me read more critical areas:
Tool2026-06-03 16:52 UTC
{"output": "* @param array An array of custom bulk actions for contacts.\n */\n 'custom_contact_bulk_actions' => apply_filters('fluent_crm/custom_contact_bulk_actions', []),\n 'crm_editor_frame' => $blockEditorUrl,\n );\n\n i...
Tool2026-06-03 16:52 UTC
{"output": "668: $activatedFeatures = Helper::getActivatedFeatures();", "exit_code": 0, "error": null}
Tool2026-06-03 16:52 UTC
{"output": "'template_type' => 'raw_text_box',\n 'template_info' => '<h3>Raw HTML Template</h3><p>You can use any type of valid html and FluentCRM will send that without altering any design processing.</p>'\n ]\n ]);\n\n if (!defined('FLUENTCAMPAIGN')) {\n ...
Donna2026-06-03 16:52 UTC
Tool2026-06-03 16:52 UTC
{"output": "/var/www/html/wp-content/plugins/fluent-crm/assets/Exporter.js:1:import{k as e,av as t,az as s,aG as l,aD as o,aA as a,e as i,g as r}from\"./vendor-element-plus.js?ver=3.1.0\";import{s as c}from\"./data_config.js?ver=3.1.0\";import{G as d}from\"./GenericPromo.js?ver=3.1.0\";import{r as n...
Tool2026-06-03 16:52 UTC
{"output": "import{e,S as t,R as s}from\"../fc-bits.js?ver=3.1.0\";import{ak as a,bl as n,bm as d,bn as o,bo as i,bp as r,bq as b,br as l,bs as m,bt as w,bu as p}from\"../vendor.js?ver=3.1.0\";a.extend(n),a.extend(d),a.extend(o),a.extend(i),a.extend(r),window.dayjs=a,window.FLUENTCRM={Rest:s,Storage...
Tool2026-06-03 16:52 UTC
{"output": "/var/www/html/wp-content/plugins/fluent-crm/assets/Exporter.js:1:import{k as e,av as t,az as s,aG as l,aD as o,aA as a,e as i,g as r}from\"./vendor-element-plus.js?ver=3.1.0\";import{s as c}from\"./data_config.js?ver=3.1.0\";import{G as d}from\"./GenericPromo.js?ver=3.1.0\";import{r as n...
Donna2026-06-03 16:52 UTC
Let me check the critical remaining files:
Tool2026-06-03 16:52 UTC
{"output": "if ($this->funnelItemsRegistered) {\n return;\n }\n\n $this->funnelItemsRegistered = true;\n\n $this->initBlockActions();\n $this->initBenchMarkBlocks();\n $this->initTriggers();\n\n if (!defined('FLUENTCAMPAIGN_DIR_FILE')) {\n ...
Tool2026-06-03 16:52 UTC
{"output": "foreach ($sequenceMetrics as $sequenceMetric) {\n $funnelArrays[] = $sequenceMetric->action_name;\n }\n\n update_option($this->settingsKey, array_unique($funnelArrays), 'yes');\n }\n\n private function initTriggers()\n {\n new UserRegistrationTrig...
Tool2026-06-03 16:52 UTC
{"output": "if ($funnel->status != 'published') {\n return $this->sendError([\n 'message' => __('Automation status needs to be published', 'fluent-crm')\n ]);\n }\n\n if (!defined('FLUENTCAMPAIGN_DIR_FILE')) {\n return $this->sendError([\...
Tool2026-06-03 16:52 UTC
{"output": "Helper::sendDoubleOptin($subscriberIds);\n return $this->sendSuccess([\n 'last_contact_id' => $lastContactId,\n 'completed_contacts' => count($subscriberIds),\n 'message' => __('Double optin sent to selected contacts',...
Tool2026-06-03 16:52 UTC
{"output": "return [\n 'last_contact_id' => $lastContactId,\n 'completed_contacts' => count($subscriberIds),\n 'message' => __('Status has been changed for the selected subscribers', 'fluent-crm')\n ];\n } elseif ($actionNa...
Tool2026-06-03 16:52 UTC
{"output": "'value' => 'redirect'\n ]\n ],\n 'tag_based_redirect' => [\n 'type' => 'inline-checkbox',\n 'checkbox_label' => (defined('FLUENTCAMPAIGN')) ? __('Enable Tag based double optin redi...
Tool2026-06-03 16:52 UTC
{"output": "if ($optionKey == 'campaigns' || $optionKey == 'funnels' || $optionKey == 'email_sequences') {\n\n if ($optionKey == 'campaigns') {\n $objectModel = Campaign::select(['id', 'title', 'status'])->where('status', '!=', 'draft');\n } else if ($optionKey =...
Tool2026-06-03 16:52 UTC
{"output": "'id' => 'raw_html',\n 'label' => __('Raw HTML', 'fluent-crm'),\n 'image' => fluentCrmMix('images/html-editor.svg'),\n 'config' => [],\n 'use_gutenberg' => false,\n 'template_type'...
Tool2026-06-03 16:52 UTC
{"output": "'label' => __('Custom Fields', 'fluent-crm'),\n 'value' => 'custom_fields',\n 'children' => $children\n ];\n }\n\n if (!defined('FLUENTCAMPAIGN')) {\n $disabled = true;\n if (defined('WC_PLUGIN_FILE')) {\n...
Tool2026-06-03 16:52 UTC
{"output": "->orderBy('total', 'DESC')\n ->get();\n }\n\n private function maybeTransformSmartLinks($links)\n {\n if (!apply_filters('fluent_crm/has_smartlink', false)) {\n return $links;\n }\n\n foreach ($links as $index => $link) {\n $...
Donna2026-06-03 16:52 UTC
Tool2026-06-03 16:52 UTC
{"output": "'category' => 'Restrict Content Pro',\n 'label' => 'Membership Expired',\n 'icon' => 'fc-icon-rcp_membership_cancle',\n 'description' => 'This funnel runs when a membership expires',\n 'disabled' => true\n ...
Tool2026-06-03 16:52 UTC
{"output": "<?php\n\nnamespace FluentCrm\\App\\Services\\Funnel;\n\nuse FluentCrm\\App\\Services\\Helper;\n\nclass ProFunnelItems\n{\n public function __construct()\n {\n add_filter('fluentcrm_funnel_triggers', function ($allTriggers) {\n $triggers = $this->getProTriggers();\...
Tool2026-06-03 16:52 UTC
{"output": "import{_ as a,I as o}from\"./fc-bits-ui.js?ver=3.1.0\";import{r as e,c as s,o as r,L as n,b as t,F as c,d as i,P as l,O as p,n as d}from\"./vendor.js?ver=3.1.0\";const u={name:\"PromoCard\",components:{Icons:o},props:{heading:{type:String,default:\"This is a pro feature\"},description:{t...
Tool2026-06-03 16:52 UTC
{"output": "import{P as r}from\"./PromoCard.js?ver=3.1.0\";import{r as o,c as e,o as i,d as s}from\"./vendor.js?ver=3.1.0\";import{_ as a}from\"./fc-bits-ui.js?ver=3.1.0\";const n={class:\"fcrm_promo_body\"};const t=a({name:\"GenericPromo\",components:{PromoCard:r},props:{heading:{type:String,requir...
Donna2026-06-03 16:52 UTC
Tool2026-06-03 16:52 UTC
{"output": "<td align=\"left\" valign=\"top\" id=\"footer_section\" style=\"mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;height: 100%;margin: 0;padding: 0;width: 100%;border-top: 0;\">\n <div style=\"margin-top: 80px; text-align: left;\">\n ...
Tool2026-06-03 16:52 UTC
{"output": "<td valign=\"top\" width=\"600\" style=\"width:600px;\">\n <![endif]-->\n <table align=\"left\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style=\"max-width: 100%;min-width: 100%;border-collapse: ...
Tool2026-06-03 16:52 UTC
{"output": "", "exit_code": 0, "error": null}
Tool2026-06-03 16:52 UTC
{"output": "", "exit_code": 0, "error": null}
Tool2026-06-03 16:52 UTC
{"output": "/var/www/html/wp-content/plugins/fluent-crm/assets/admin/css/app3.css:1:@charset \"UTF-8\";:root{--fc-primary-bg: #FFFFFF;--fc-secondary-bg: #F5F7FA;--fc-light-bg: #E1E4EA;--fc-deep-bg: #222530;--fc-weak-bg-25: #F9FAFB;--fc-ai-background: #efebff;--fc-ai-color: #8762F0;--fc-primary-text:...
Tool2026-06-03 16:52 UTC
{"output": "foreach ($allLists as $list) {\n $formattedListNames[] = $list->title . ' (' . $list->id . ')';\n $formattedLists[] = $list->id;\n }\n }\n }\n\n if (!defined('FLUENTCAMPAIGN')) {\n \\WP_CLI::error('F...
Donna2026-06-03 16:52 UTC
Let me check a few more critical areas:
Tool2026-06-03 16:52 UTC
{"output": "if (defined('WP_CLI') && WP_CLI) {\n \\WP_CLI::add_command('fluent_crm', '\\FluentCrm\\App\\Hooks\\CLI\\Commands');\n}\n\nadd_action('admin_notices', function () {\n if (defined('FLUENTCAMPAIGN_FRAMEWORK_VERSION') && FLUENTCAMPAIGN_FRAMEWORK_VERSION < 3) {\n echo '<div class...
Tool2026-06-03 16:52 UTC
{"output": "112: if (!defined('FLUENTCAMPAIGN')) {\n332: if (!defined('FLUENTCAMPAIGN')) {\n552: if (!defined('FLUENTCAMPAIGN')) {\n702: if (!defined('FLUENTCAMPAIGN')) {\n819: if (defined('FLUENTCAMPAIGN')) {\n843: if (defined('FLUENTCAMPAIGN_PLUGIN_URL')) ...
Tool2026-06-03 16:52 UTC
{"output": "", "exit_code": 0, "error": null}
Tool2026-06-03 16:52 UTC
{"output": "0", "exit_code": 0, "error": null}
Tool2026-06-03 16:52 UTC
{"output": "", "exit_code": 1, "error": null, "exit_code_meaning": "No matches found (not an error)"}
Tool2026-06-03 16:52 UTC
{"output": "", "exit_code": 0, "error": null}
Donna2026-06-03 16:52 UTC
Tool2026-06-03 16:52 UTC
{"output": "", "exit_code": 0, "error": null}
Tool2026-06-03 16:52 UTC
{"output": "", "exit_code": 0, "error": null}
Tool2026-06-03 16:52 UTC
{"output": "Modules\nadminbar-search.js\napp.js\nboot.js\ncss\nglobal_admin.js\nindex.php\nsetup-wizard.js\nvisual-editor", "exit_code": 0, "error": null}
Tool2026-06-03 16:52 UTC
{"output": "/var/www/html/wp-content/plugins/fluent-crm/assets/Exporter.js\n/var/www/html/wp-content/plugins/fluent-crm/assets/ReadableRecipientTagger.js\n/var/www/html/wp-content/plugins/fluent-crm/assets/_LinkMetrics.js\n/var/www/html/wp-content/plugins/fluent-crm/assets/libs/multiple-select/multi...
Donna2026-06-03 16:52 UTC
Tool2026-06-03 16:52 UTC
<persisted-output>
This tool result was too large (52,312 characters, 51.1 KB).
Full output saved to: /tmp/hermes-results/call_00_iJ0uwCRU79sHHhZSn2Cj5428.txt
Use the read_file tool with offset and limit to access specific sections of this output.
Preview (first 1500 chars):
{"output": "1:import{_ ...
Tool2026-06-03 16:52 UTC
{"output": "1:import{S as e,R as t,_ as s}from\"../../../fc-bits.js?ver=3.1.0\";import{$ as a,ae as l,an as i,aU as n,ax as o,aV as r,aW as c,E as d,k as _,e as u,aB as p,g as h,a6 as m,aj as g,aL as f,aK as b,ay as y,B as v,i as C,aG as w,az as k,aX as F,a4 as $,J as L,ac as V,a7 as S,u as A,c as P...
Tool2026-06-03 16:52 UTC
{"output": "1:import{D as e,c as t,S as i,ak as n,aU as s,$ as o,aW as l,aV as c,E as a,e as r,ae as _,at as d,ao as u,P as p,n as h,h as f,i as m,j as g,k as b,aN as k,aT as v,aw as y,ay as w,aK as C,aL as $,ax as S,B as x,a3 as T,aZ as B,aO as V,a_ as q,az as A,aP as F,aJ as j,av as E,aB as I,aD a...
Tool2026-06-03 16:52 UTC
{"output": "1:import{aZ as e,a_ as o,ay as t,k as r,aR as s,aw as i,e as n,ax as l,ap as a}from\"../../../vendor-element-plus.js?ver=3.1.0\";import{a as d,F as c}from\"../../../FieldEditor.js?ver=3.1.0\";import{r as u,c as _,o as p,b as m,K as f,d as v,w as h,O as b,P as k,a as g,Q as y,L as j,as as...
Tool2026-06-03 16:52 UTC
{"output": "1:import{ay as t,at as e,j as a,k as s,E as i,h as n,i as o,aC as r,aB as l,aA as c,aD as d,aj as m,a4 as h,ag as _,y as u,aY as f,aJ as p,g}from\"../../../vendor-element-plus.js?ver=3.1.0\";import{r as b,K as y,c as v,o as C,d as w,a as x,w as k,Q as L,as as $,b as T,P as S,a2 as E,O as...
Tool2026-06-03 16:52 UTC
<persisted-output>
This tool result was too large (52,751 characters, 51.5 KB).
Full output saved to: /tmp/hermes-results/call_05_3fG6GZQcG1QyuA0jMKc38161.txt
Use the read_file tool with offset and limit to access specific sections of this output.
Preview (first 1500 chars):
{"output": "1:import{k ...
Tool2026-06-03 16:52 UTC
<persisted-output>
This tool result was too large (52,227 characters, 51.0 KB).
Full output saved to: /tmp/hermes-results/call_06_4sXy5IpAQfKpY3IWGHlM2086.txt
Use the read_file tool with offset and limit to access specific sections of this output.
Preview (first 1500 chars):
{"output": "1:import{aQ...
Tool2026-06-03 16:52 UTC
{"output": "1:import{k as e,az as a,ax as t,aw as s,aE as l,aF as n,e as d,aK as o,aL as i,ay as r,aT as _,aD as c,aA as m,aB as u,aO as f}from\"../../../vendor-element-plus.js?ver=3.1.0\";import{_ as p,I as g}from\"../../../fc-bits-ui.js?ver=3.1.0\";import{I as v}from\"../../../ItemCopier.js?ver=3....
Donna2026-06-03 16:52 UTC
Let me also check how `has_campaign_pro` is defined globally across components, and check the remaining files:
Tool2026-06-03 16:52 UTC
{"output": "", "exit_code": 0, "error": null}
Tool2026-06-03 16:52 UTC
{"output": "1:import{a3 as e,a6 as t,a4 as s,D as i,ay as o,k as n,e as a,az as l,aT as r}from\"../../../../vendor-element-plus.js?ver=3.1.0\";import{P as d}from\"../../../../PaginationBar.js?ver=3.1.0\";import{C as c}from\"../../../../Confirm.js?ver=3.1.0\";import{F as h}from\"../../../../_FormBuil...
Tool2026-06-03 16:52 UTC
{"output": "1:import{_ as e}from\"../../../../fc-bits.js?ver=3.1.0\";import{P as t,aH as a,aI as i,j as s,h as l,i as o,ay as n,aO as r,k as c,W as d,aJ as m,E as u,Z as _,bk as p,bl as h,aD as g,aA as f,g as b,o as v,c as w,_ as y,T as k,a6 as $,G as C,z as S,aP as E,aE as V,aF as T,ax as P,az as x...
Tool2026-06-03 16:52 UTC
{"output": "1:import{a4 as e,P as t,aZ as a,a_ as l,k as i,h as s,i as o,E as m,j as p,ay as r,av as d,aD as n,aA as _,aw as c,e as h,ax as u,ap as v}from\"../../../../vendor-element-plus.js?ver=3.1.0\";import{E as g}from\"../../../../BlockComposer.js?ver=3.1.0\";import{I as f}from\"../../../../_For...
Tool2026-06-03 16:52 UTC
{"output": "", "exit_code": 0, "error": null}
Tool2026-06-03 16:52 UTC
{"output": "import{c as C,o as e,L as o,O as s,b as t,P as r}from\"./vendor.js?ver=3.1.0\";import{_ as a}from\"./fc-bits-ui.js?ver=3.1.0\";const n={name:\"ProBadge\",props:{text:{type:String,default:\"Pro\"},hideIcon:{type:Boolean,default:!1}}},i={class:\"fcrm_pro_badge\"},c={key:0,class:\"icon\"};c...
Tool2026-06-03 16:52 UTC
{"output": "/var/www/html/wp-content/plugins/fluent-crm/app/Modules/MCP/Tools/ContextTools.php:288: 'is_pro' => !empty($item['is_pro']),\n/var/www/html/wp-content/plugins/fluent-crm/app/Services/Libs/Mailer/BaseHandler.php:262: * Replaces the old isProcessing() + processing() two-...
Donna2026-06-03 16:52 UTC
Let me check one more vital piece - how the ProFunnelItems blocks are marked with `is_pro`:
Tool2026-06-03 16:52 UTC
{"output": "'disabled' => true,\n 'svg' => '<svg width=\"16px\" height=\"16px\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 137 137\" fill=\"none\">\n <path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M68.5 137C106.332 137 137 106.332 137 68.5C137 30.6685 106.332 0 68.5...
Tool2026-06-03 16:52 UTC
{"output": "foreach ($items as $key => $item) {\n $name = is_string($key) ? $key : ($item[$keyField] ?? null);\n if (!$name) {\n continue;\n }\n $out[] = [\n 'key' => $name,\n 'label' => $item['label'] ?? $i...
Tool2026-06-03 16:52 UTC
{"output": "];\n }\n\n if (defined('WPPAYFORM_VERSION')) {\n $validProviders['payform'] = [\n 'title' => __('Paymattic Purchase History', 'fluent-crm'),\n 'name' => __('Paymattic', 'fluent-crm')\n ];\n }\n\n if (defined...
Donna2026-06-03 16:52 UTC
Now I have comprehensive data. Let me compile the complete exhaustive report.
---
## EXHAUSTIVE REPORT: FluentCRM Paywall/Upgrade/Pro Gates
### SUMMARY OF THE GATING ARCHITECTURE
The gating system centers on a single PHP constant: **`FLUENTCAMPAIGN`** (and related: `FLUENTCAMPAIGN_FRAMEWORK_VERSION`, `FLUENTCAMPAIGN_DIR_FILE`). The mu-plugin defines `FLUENTCAMPAIGN=true`, `FLUENTCAMPAIGN_FRAMEWORK_VERSION=3`, and later `FLUENTCAMPAIGN_DIR_FILE` (at `init` priority 5). This unlocks ~95% of gates. Remaining unhandled issues are subtle.
---
## GATE CATEGORY 1: PHP BACKEND GATES (FLUENTCAMPAIGN constant)
### Gate 1.1: `FunnelHandler` — ProFunnelItems loading
**File:** `/var/www/html/wp-content/plugins/fluent-crm/app/Hooks/Handlers/FunnelHandler.php`
**Line:** 100
**Mechanism:** `if (!defined('FLUENTCAMPAIGN_DIR_FILE')) { new \FluentCrm\App\Services\Funnel\ProFunnelItems(); }`
**What it gates:** All Pro funnel triggers (WooCommerce, PMPro, MemberPress, LearnDash, etc.) and Pro funnel blocks (conditional actions, sequence emails, webhooks, etc.)
**Already handled:** ✅ YES — mu-plugin defines `FLUENTCAMPAIGN_DIR_FILE` at `init` priority 5, which is AFTER the `registerFunnelItems()` call (which runs at `init` priority 1). The check `!defined('FLUENTCAMPAIGN_DIR_FILE')` evaluates to `true` at priority 1, so ProFunnelItems IS loaded. This is the correct deferred approach.
### Gate 1.2: ProFunnelItems — All Pro triggers marked `disabled=true`
**File:** `/var/www/html/wp-content/plugins/fluent-crm/app/Services/Funnel/ProFunnelItems.php`
**Lines:** ~140-650+
**Mechanism:** Every Pro trigger has `'disabled' => true`. The triggers include: WooCommerce (new order, completed, refunded, subscription), PMPro (enrolled, expired), MemberPress, LearnDash, LifterLMS, EDD, Restrict Content Pro, TutorLMS, Wishlist Member, SureCart, Abandoned Cart, AffiliateWP, etc.
**Already handled:** ✅ PARTIALLY — The mu-plugin has a filter at line 70-82 that only handles PMPro triggers (`pmpro_after_change_membership_level`, `pmpro_membership_post_membership_expiry`). **All other Pro triggers remain `disabled=true`**. This is a significant gap.
### Gate 1.3: ProFunnelItems — All Pro blocks marked `is_pro=true`
**File:** `/var/www/html/wp-content/plugins/fluent-crm/app/Services/Funnel/ProFunnelItems.php`
**Lines:** ~340-500+
**Mechanism:** Every Pro block has `'is_pro' => true`. Blocks include: Has In Selected Lists, Has Selected Tags, Link Click benchmark, Send Campaign Email, Cancel Automations, Cancel Sequence Emails, Set Sequence Emails, Update Contact Property, Add Notes & Activity, Outgoing Webhook, End This Funnel Here, Check Contact's Properties, Create WordPress User, Update WP User Meta, Change WP User Role, EDD benchmarks, WooCommerce purchased checks, etc.
**Already handled:** ✅ YES — The blocks' `is_pro` flag is read by the JS frontend (BlockChoice component), but the rendering in the funnel editor checks `window.fcAdmin.addons.fluentcampaign` which is true. However, the `disabled` triggers' `upgrade` buttons link to `crm_pro_url`.
### Gate 1.4: FunnelController — Automation sync (cleanup)
**File:** `/var/www/html/wp-content/plugins/fluent-crm/app/Http/Controllers/FunnelController.php`
**Line:** 1342
**Mechanism:** `if (!defined('FLUENTCAMPAIGN_DIR_FILE')) { return sendError('requires latest version of FluentCRM Pro'); }`
**What it gates:** The `syncAutomationSteps` cleanup feature
**Already handled:** ✅ YES — `FLUENTCAMPAIGN_DIR_FILE` is defined by mu-plugin.
### Gate 1.5: SubscriberController — `add_to_email_sequence` bulk action
**File:** `/var/www/html/wp-content/plugins/fluent-crm/app/Http/Controllers/SubscriberController.php`
**Line:** 1237
**Mechanism:** `if (!defined('FLUENTCAMPAIGN')) { return $this->sendError(['message' => 'requires FluentCRM Pro']); }`
**What it gates:** Adding contacts to email sequences via bulk action
**Already handled:** ✅ YES — `FLUENTCAMPAIGN` is defined by mu-plugin.
### Gate 1.6: SubscriberController — `add_to_automation` bulk action
**File:** `/var/www/html/wp-content/plugins/fluent-crm/app/Http/Controllers/SubscriberController.php`
**Line:** 1400
**Mechanism:** `if (!defined('FLUENTCAMPAIGN')) { return $this->sendError(['message' => 'requires FluentCRM Pro']); }`
**What it gates:** Adding contacts to automation funnels via bulk action
**Already handled:** ✅ YES — `FLUENTCAMPAIGN` is defined.
### Gate 1.7: SettingsController — Tag-based double optin redirect
**File:** `/var/www/html/wp-content/plugins/fluent-crm/app/Http/Controllers/SettingsController.php`
**Line:** 194
**Mechanism:** `'disabled' => !defined('FLUENTCAMPAIGN')` — the setting field is disabled without Pro
**What it gates:** Tag-based double optin redirect feature
**Already handled:** ✅ YES — `FLUENTCAMPAIGN` is defined, so `disabled` is `false`.
### Gate 1.8: Helper.php — Email sequence options in cascade selection
**File:** `/var/www/html/wp-content/plugins/fluent-crm/app/Services/Helper.php`
**Line:** 605 (in `getCascadeSelectionOptions`)
**Mechanism:** `if (!defined('FLUENTCAMPAIGN')) { return ['options' => []]; }` — returns empty options for email_sequences
**What it gates:** Email sequence selection dropdowns in the UI
**Already handled:** ✅ YES — `FLUENTCAMPAIGN` is defined.
### Gate 1.9: Helper.php — WooCommerce purchase history segment fields (Pro-only)
**File:** `/var/www/html/wp-content/plugins/fluent-crm/app/Services/Helper.php`
**Line:** ~1701
**Mechanism:** `if (!defined('FLUENTCAMPAIGN')) { ... add Pro-only fields like 'total_order_count' with label '(Pro Required)' ... }`
**What it gates:** WooCommerce-based segment filtering fields
**Already handled:** ✅ YES — `FLUENTCAMPAIGN` is defined, so the Pro-only fields are available normally.
### Gate 1.10: Helper.php — PMPro purchase history provider
**File:** `/var/www/html/wp-content/plugins/fluent-crm/app/Services/Helper.php`
**Line:** 790
**Mechanism:** `if (defined('PMPRO_VERSION') && defined('FLUENTCAMPAIGN'))` — PMPro purchase history requires both PMPro active AND FluentCRM Pro
**Already handled:** ✅ YES — `FLUENTCAMPAIGN` is defined.
### Gate 1.11: Helper.php — Visual Builder template (paywall version)
**File:** `/var/www/html/wp-content/plugins/fluent-crm/app/Services/Helper.php`
**Line:** 605-612 (in `getEmailDesignTemplates`)
**Mechanism:** `if (!defined('FLUENTCAMPAIGN')) { $templates['visual_builder'] = [template_type => 'visual_builder_demo']; }` — without Pro, adds a "Visual Builder" option that is only a demo (not functional)
**Already handled:** ✅ YES — `FLUENTCAMPAIGN` is defined, so the demo template entry is NOT added. When `FLUENTCAMPAIGN` IS defined, the template comes from the Pro plugin itself.
### Gate 1.12: OptionsController — Email sequence options
**File:** `/var/www/html/wp-content/plugins/fluent-crm/app/Http/Controllers/OptionsController.php`
**Line:** 543
**Mechanism:** `if (!defined('FLUENTCAMPAIGN')) { return ['options' => []]; }` — returns empty options
**What it gates:** Email sequence options for AJAX requests
**Already handled:** ✅ YES — `FLUENTCAMPAIGN` is defined.
### Gate 1.13: Helper.php — `getPurchaseHistoryProviders` PMPro check
**File:** `/var/www/html/wp-content/plugins/fluent-crm/app/Services/Helper.php`
**Line:** 790
**Mechanism:** `if (defined('PMPRO_VERSION') && defined('FLUENTCAMPAIGN'))` — PMPro purchase history requires both
**Already handled:** ✅ YES.
### Gate 1.14: CampaignUrlMetric — Smart Links filter gate
**File:** `/var/www/html/wp-content/plugins/fluent-crm/app/Models/CampaignUrlMetric.php`
**Line:** 306
**Mechanism:** `if (!apply_filters('fluent_crm/has_smartlink', false))` — the filter `fluent_crm/has_smartlink` returns false by default. The Pro plugin hooks this filter to return true.
**Already handled:** ❌ NO — The mu-plugin does NOT hook `fluent_crm/has_smartlink`. Smart Links will not be transformed/rendered. Need to add: `add_filter('fluent_crm/has_smartlink', '__return_true');`
### Gate 1.15: AdminMenu — `hasSmartLink()` method
**File:** `/var/www/html/wp-content/plugins/fluent-crm/app/Hooks/Handlers/AdminMenu.php`
**Lines:** 961-972
**Mechanism:** `if (!defined('FLUENTCAMPAIGN')) { return false; }` then checks for the `fc_smart_links` DB table existence
**What it gates:** The `has_smart_link` property in `fcAdmin` admin vars
**Already handled:** ✅ PARTIALLY — `FLUENTCAMPAIGN` is defined so it proceeds. But it also needs the `fc_smart_links` DB table to exist. Smart links would only work if the Pro plugin's DB tables were created.
### Gate 1.16: CLI Commands — Multiple FLUENTCAMPAIGN checks
**File:** `/var/www/html/wp-content/plugins/fluent-crm/app/Hooks/CLI/Commands.php`
**Lines:** 112, 332, 552, 702
**Mechanism:** Multiple `if (!defined('FLUENTCAMPAIGN')) { \WP_CLI::error('FluentCRM Pro is required'); }` checks for various CLI commands including sending emails, campaigns, sequences
**Already handled:** ✅ YES — `FLUENTCAMPAIGN` is defined.
### Gate 1.17: PurchaseHistory — PMPro check
**File:** `/var/www/html/wp-content/plugins/fluent-crm/app/Hooks/Handlers/PurchaseHistory.php`
**Line:** 682
**Mechanism:** `if (!defined('FLUENTCAMPAIGN'))` gates PMPro purchase history handler
**Already handled:** ✅ YES.
### Gate 1.18: AbandonCart — FluentCart automation trigger gate
**Files:** `FluentCartAutomationTrigger.php:62`, `AbandonCartRunner.php:241`
**Mechanism:** `if (!defined('FLUENTCAMPAIGN_DIR_FILE'))` — gates abandon cart automation triggers
**Already handled:** ✅ YES — `FLUENTCAMPAIGN_DIR_FILE` is defined.
### Gate 1.19: Actions.php — Update notice for outdated Pro
**File:** `/var/www/html/wp-content/plugins/fluent-crm/app/Hooks/actions.php`
**Lines:** 186-192
**Mechanism:** Admin notice when `FLUENTCAMPAIGN_FRAMEWORK_VERSION < 3` (version check)
**Already handled:** ✅ YES — mu-plugin defines version 3.
---
## GATE CATEGORY 2: PHP BACKEND FILTER GATES
### Gate 2.1: `fluentcrm_is_require_verify` filter
**File:** `AdminMenu.php:746` and mu-plugin line 37
**Mechanism:** `apply_filters('fluentcrm_is_require_verify', false)` — controls whether verification is required
**Already handled:** ✅ YES — mu-plugin has `add_filter('fluentcrm_is_require_verify', '__return_false')`.
### Gate 2.2: `fluentcrm_disable_email_open_tracking` filter
**File:** `Helper.php:657`
**Mechanism:** `!apply_filters('fluentcrm_disable_email_open_tracking', false)`
**Already handled:** ✅ YES — mu-plugin line 38 has `add_filter('fluentcrm_disable_email_open_tracking', '__return_false')`.
### Gate 2.3: `fluent_crm/has_smartlink` filter — Smart Links
**File:** `CampaignUrlMetric.php:306`
**Mechanism:** Returns false by default; Pro plugin hooks it
**Already handled:** ❌ NO — needs `add_filter('fluent_crm/has_smartlink', '__return_true')`.
### Gate 2.4: `fluent_crm/admin_vars` filter — Extensibility
**File:** `AdminMenu.php` (end of getAdminVars)
**Mechanism:** Filter used by Pro plugin to add more admin vars
**Already handled:** ✅ N/A — this is additive, not a gate.
---
## GATE CATEGORY 3: JS/VUE FRONTEND GATES
### Gate 3.1: `window.fcAdmin.addons.fluentcampaign` — Primary JS gating mechanism
**Source:** AdminMenu.php `getAdminVars()` → `'addons' => getActivatedFeatures()`
**Helper.php `getActivatedFeatures()`:** `'fluentcampaign' => defined('FLUENTCAMPAIGN_FRAMEWORK_VERSION')`
**Since:** mu-plugin defines `FLUENTCAMPAIGN_FRAMEWORK_VERSION=3`, this is `true`.
**JS access pattern (used in every component):**
```js
has_campaign_pro() {
var e,t;
return !!(null==(t=null==(e=window.fcAdmin)?void 0:e.addons)?void 0:t.fluentcampaign)
}
```
**Already handled:** ✅ YES — constant is defined.
### Gate 3.2: Export Contacts (Exporter.js)
**File:** `/var/www/html/wp-content/plugins/fluent-crm/assets/Exporter.js`
**Mechanism:** Render function checks `c.has_campaign_pro` — if false, shows GenericPromo card instead of export form
**Already handled:** ✅ YES — `has_campaign_pro` is true.
### Gate 3.3: Campaign Emails (resend, resend unopened, export)
**File:** `/var/www/html/wp-content/plugins/fluent-crm/assets/_LinkMetrics.js`
**Mechanism:** `this.has_campaign_pro` checks in `resendEmail()`, `resendUnopenedEmails()`, `exportCampaignEmails()` — if false, shows error notification
**Already handled:** ✅ YES.
### Gate 3.4: Built-in Template Import (BuiltinTemplateDrawer.js)
**File:** `/var/www/html/wp-content/plugins/fluent-crm/assets/BuiltinTemplateDrawer.js`
**Mechanism:** `e.has_campaign_pro` check — if false, the import button shows Pro modal instead of actually importing
**Already handled:** ✅ YES.
### Gate 3.5: Scripts RecipientTagger — Advanced Filters
**File:** `/var/www/html/wp-content/plugins/fluent-crm/assets/ReadableRecipientTagger.js`
**Mechanism:** `e.has_campaign_pro` in render — advanced filters section is hidden without Pro
**Already handled:** ✅ YES.
### Gate 3.6: Create Funnel Modal — Trigger selection with disabled Pro triggers
**File:** `/var/www/html/wp-content/plugins/fluent-crm/assets/admin/Modules/Funnels/Funnels.js` (CreateFunnelModal)
**Mechanism:** `has_campaign_pro` computed property, triggers with `disabled: true` show ProBadge and "Upgrade To Pro" link
**Already handled:** ✅ PARTIALLY — The triggers are loaded (ProFunnelItems loads them), but they are still marked `disabled`. The mu-plugin only un-disables PMPro triggers (via the `fluentcrm_funnel_triggers` filter). **All other Pro triggers remain disabled** with "Upgrade To Pro" buttons.
### Gate 3.7: Funnel Editor — Conditional Action gate
**File:** `/var/www/html/wp-content/plugins/fluent-crm/assets/admin/Modules/Funnels/FunnelEditor/Edit.js`
**Mechanism:** `handleConditionAdd()` checks `this.has_campaign_pro` — if false, shows alert "Require FluentCRM Pro"
**Already handled:** ✅ YES — `has_campaign_pro` is true.
### Gate 3.8: Funnel Editor — Block insertion with `is_pro` check
**File:** `/var/www/html/wp-content/plugins/fluent-crm/assets/admin/Modules/Funnels/FunnelEditor/Edit.js` (BlockChoice component)
**Mechanism:** `insert(e, t)` checks `if(t && t.is_pro)` — shows alert "Require FluentCRM Pro" with link to purchase
**Already handled:** ✅ YES — the `is_pro` flag exists on block definitions, but the blocks ARE loaded (because ProFunnelItems runs). The blocks would only be blocked if `is_pro` is checked AND Pro isn't active.
### Gate 3.9: Import Funnel (ImportFunnel.js)
**File:** `/var/www/html/wp-content/plugins/fluent-crm/assets/admin/Modules/Funnels/ImportFunnel.js`
**Mechanism:** Render function checks `a.has_campaign_pro` — if false, shows PromoCard instead of import UI
**Already handled:** ✅ YES.
### Gate 3.10: Settings → Advanced Features — Campaign Archive Shortcode
**File:** `/var/www/html/wp-content/plugins/fluent-crm/assets/v3app/src/Modules/Settings/_AdvancedFeatures.js`
**Mechanism:** `o.has_campaign_pro` check — campaign archive shortcode is only shown when Pro is active
**Already handled:** ✅ YES.
### Gate 3.11: Settings → AddOns — Multiple Pro-feature gates
**File:** `/var/www/html/wp-content/plugins/fluent-crm/assets/admin/Modules/Settings/AddOns.js`
**Mechanisms:**
- `has_campaign_pro` check for Frontend Portal feature — "Upgrade to Pro" button shown if false
- `has_campaign_pro` check for fetching SMS module settings
- `has_campaign_pro` check for Campaign Archive shortcode display
- `has_campaign_pro` check for "Recommended Plugins → FluentCRM Pro" section
**Already handled:** ✅ YES.
### Gate 3.12: Profile Emails — Send Email button
**File:** `/var/www/html/wp-content/plugins/fluent-crm/assets/admin/Modules/Profile/Parts/ProfileEmails.js`
**Mechanism:** `e.has_campaign_pro && ...` — Send Email button only rendered when Pro is active
**Already handled:** ✅ YES.
### Gate 3.13: Profile Emails — Resend Email
**File:** `/var/www/html/wp-content/plugins/fluent-crm/assets/admin/Modules/Profile/Parts/ProfileEmails.js`
**Mechanism:** `if(!this.has_campaign_pro) return this.$notify.error(...)` — prevents resending without Pro
**Already handled:** ✅ YES.
### Gate 3.14: Profile Emails — Email Sequences and Automations sections
**File:** `/var/www/html/wp-content/plugins/fluent-crm/assets/admin/Modules/Profile/Parts/ProfileEmails.js`
**Mechanism:** Render check `e.has_campaign_pro ? (show sequences + automations) : (hide)` — entire Email Sequences and Automations sub-sections hidden without Pro
**Already handled:** ✅ YES.
### Gate 3.15: Profile Notes — Export Notes
**File:** `/var/www/html/wp-content/plugins/fluent-crm/assets/admin/Modules/Profile/Parts/ProfileNotes.js`
**Mechanism:** `if(!this.has_campaign_pro) return this.$notify.error(this.$t("Notes export feature is only available on pro version"))`
**Already handled:** ✅ YES.
### Gate 3.16: Edit Template — Export Template
**File:** `/var/www/html/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/Templates/EditTemplate.js`
**Mechanism:** `if(!this.has_campaign_pro) return this.$notify.error(this.$t("Template_Export_Alert"))`
**Already handled:** ✅ YES.
### Gate 3.17: Profile SMS — has_campaign_pro data property
**File:** `/var/www/html/wp-content/plugins/fluent-crm/assets/v3app/src/Modules/Profile/Parts/ProfileSMS.js`
**Mechanism:** `data() { return { has_campaign_pro: false } }` — hardcoded false in the data, but this is overridden by the component's `has_campaign_pro` computed
**Already handled:** ✅ The computed `has_campaign_pro` (from global) overrides it.
### Gate 3.18: GenericPromo.js and GenericPromo2.js — The promo card components
**Files:** `/var/www/html/wp-content/plugins/fluent-crm/assets/GenericPromo.js`, `GenericPromo2.js`, `PromoCard.js`
**Mechanism:** These are the fallback UI components shown when `has_campaign_pro` is false. They display "This is a pro feature" text and a "Upgrade to Pro" button linking to `appVars.crm_pro_url`.
**Already handled:** ✅ These are the consequence of gates, not gates themselves. If `has_campaign_pro` is true, they are never rendered.
### Gate 3.19: ProBadge.js
**File:** `/var/www/html/wp-content/plugins/fluent-crm/assets/ProBadge.js`
**Mechanism:** Simple UI badge showing "Pro" text with a crown icon. Used on disabled funnel triggers.
**Already handled:** ✅ Visual indicator only.
---
## GATE CATEGORY 4: EMAIL TEMPLATE FOOTER BRANDING
### Gate 4.1-4.5: Email templates show "Powered By FluentCRM" footer
**Files:**
- `app/Views/emails/web_preview/Template.php:13`
- `app/Views/emails/simple/Template.php:91`
- `app/Views/emails/block_editor/Template.php:114`
- `app/Views/emails/plain/Template.php:94`
- `app/Views/emails/raw_classic/Template.php:36`
- `app/Views/emails/classic/Template.php:92`
**Mechanism:** `<?php if(!defined('FLUENTCAMPAIGN')): ?><p>Powered By <a href="...">FluentCRM</a></p><?php endif; ?>`
**What it gates:** Without Pro, email footers include a "Powered By FluentCRM" link.
**Already handled:** ✅ YES — `FLUENTCAMPAIGN` is defined, so branding is hidden.
---
## GATE CATEGORY 5: CSS GATES
### Gate 5.1: Pro modal body styling
**File:** `/var/www/html/wp-content/plugins/fluent-crm/assets/admin/css/style.css`
**Lines:** 9, 15, 19 (scoped to `.promo_block--centered[data-v-...]`)
**Mechanism:** CSS for the promo/upgrade card UI component
**Already handled:** ✅ This is just styling, not a functional gate.
---
## GATE CATEGORY 6: THE `is_pro` BLOCK FLAG IN FUNNEL BLOCKS
### Gate 6.1: All 19 Pro funnel blocks (in ProFunnelItems.php getProBlocks)
**Tags:** Every Pro block has `'is_pro' => true`. When the JS BlockChoice component renders these, it checks `t.is_pro` and shows a ProBadge.
**Already handled:** ✅ The blocks ARE loaded because ProFunnelItems is instantiated. The `is_pro` flag triggers an alert in the `insert()` method, but since Pro IS active, the insert proceeds.
---
## GATE CATEGORY 7: CRM PRO URL/BUTTONS
### Gate 7.1: `crm_pro_url` in admin vars
**File:** `AdminMenu.php:745`
**Mechanism:** `'crm_pro_url' => 'https://fluentcrm.com/?utm_source=plugin&utm_medium=admin&utm_campaign=promo'`
**Used by:** All PromoCard components for their "Upgrade to Pro" buttons
**Already handled:** ✅ This is just a URL — since `has_campaign_pro` is true, users don't see it.
---
## GATE CATEGORY 8: MISSING HANDLERS (GAPS IN THE MU-PLUGIN)
### GAP 1: Pro triggers remain `disabled=true` (except PMPro)
**File:** `ProFunnelItems.php` lines ~140-650+
**Issue:** ALL Pro triggers (WooCommerce, MemberPress, LearnDash, EDD, LifterLMS, etc.) have `disabled => true`. The mu-plugin only overrides PMPro triggers. Users cannot select WooCommerce/other Pro triggers for new funnels.
**Fix needed:** Add to mu-plugin:
```php
add_filter('fluentcrm_funnel_triggers', function($triggers) {
foreach ($triggers as $key => $trigger) {
if (isset($trigger['disabled'])) {
$triggers[$key]['disabled'] = false;
}
}
return $triggers;
}, 1000);
```
### GAP 2: Smart Links not enabled
**Files:** `CampaignUrlMetric.php:306`, `AdminMenu.php:961-972`
**Issue:** `fluent_crm/has_smartlink` filter returns false; `hasSmartLink()` checks for DB table.
**Fix needed:**
```php
add_filter('fluent_crm/has_smartlink', '__return_true');
```
### GAP 3: License/activation status — already handled
**File:** mu-plugin lines 41-46
**Already handled:** ✅ Sets `_fluentcrm_license_status` to `valid` and `_fluentcrm_license_key` to `testing-license-key`.
### GAP 4: `fluent_crm/disable_fluentmail_suggest` filter
**Already handled:** ✅ Not actively needed — defined in mu-plugin via `FLUENTMAIL` constant or via filter.
### GAP 5: Visual Builder template
**Already handled:** ✅ With `FLUENTCAMPAIGN` defined, the `visual_builder_demo` fake template is NOT added to the list.
---
## COMPLETE LIST OF HANDLED vs. UNHANDLED GATES
### ✅ HANDLED by mu-plugin (v2.0):
1. All `defined('FLUENTCAMPAIGN')` checks (7+ locations)
2. All `defined('FLUENTCAMPAIGN_FRAMEWORK_VERSION')` checks (via `getActivatedFeatures()`)
3. All `defined('FLUENTCAMPAIGN_DIR_FILE')` checks (via deferred init priority 5)
4. PMPro disabled triggers (2 triggers un-disabled)
5. `fluentcrm_is_require_verify` filter
6. `fluentcrm_disable_email_open_tracking` filter
7. License status/key options
8. "Powered By FluentCRM" email footer branding
9. All JS `has_campaign_pro` computed property checks (because `window.fcAdmin.addons.fluentcampaign` is `true`)
### ❌ NOT HANDLED (gaps to fix):
1. **ALL non-PMPro Pro triggers remain `disabled=true`** — WooCommerce, MemberPress, LearnDash, LifterLMS, EDD, Restrict Content Pro, TutorLMS, Wishlist Member, SureCart, AffiliateWP, Abandoned Cart (~25+ triggers)
2. **Smart Links filter** — `fluent_crm/has_smartlink` returns false
3. **Smart Links DB table** — `fc_smart_links` table doesn't exist (would require Pro plugin activation or manual table creation)
### 🟡 PARTIALLY HANDLED:
1. PMPro triggers — the mu-plugin's filter at line 70-82 does handle these, but it's fragile (relies on trigger keys matching) and runs at priority 1000 which is late but workable.
---
## SUMMARY STATISTICS
| Gate Category | Total | Handled | Not Handled |
|---|---|---|---|
| PHP constant checks (`FLUENTCAMPAIGN`) | ~20 | 20 | 0 |
| PHP filter gates | ~4 | 2 | 2 |
| JS/Vue `has_campaign_pro` checks | ~15 | 15 | 0 |
| Disabled Pro triggers | ~25+ | 2 (PMPro) | ~23+ |
| `is_pro` block flags | ~19 | 19 | 0 |
| Email branding footers | ~6 | 6 | 0 |
| CSS promo styling | ~3 | 3 | 0 |
| **TOTAL** | **~92** | **~67** | **~25** |
The core architecture of this mu-plugin is sound — the `FLUENTCAMPAIGN` constant unlocks all the major PHP backends and JS frontend gates. The two remaining gaps are: (1) adding a global `disabled` override for ALL Pro triggers (not just PMPro), and (2) enabling the Smart Links filter.