PluginProbe
Fluent Support – Helpdesk & Customer Support Ticket System / 2.4.5
Fluent Support – Helpdesk & Customer Support Ticket System v2.4.5
2.4.5 2.4.0 2.3.2 2.3.1 2.3.0 2.2.1 2.2.0 trunk 1.10.0 1.10.1 1.10.2 1.10.3 1.10.4 1.10.5 1.4.0 1.4.1 1.4.2 1.4.5 1.4.6 1.4.7 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 All 69 releases
fluent-support / app / Modules / MCP / Support / CustomerMetaEnricher.php

CustomerMetaEnricher.php in Fluent Support – Helpdesk & Customer Support Ticket System 2.4.5, at app/Modules/MCP/Support/CustomerMetaEnricher.php

112 lines 3.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentSupport\App\Modules\MCP\Support;
4
5 use FluentSupport\App\Modules\PermissionManager;
6
7 /**
8 * Optional one-line customer summary for MCP lists, supplied by integrations
9 * through a batch filter. It can carry billing/CRM PII, so it's gated on
10 * fst_sensitive_data like AdminSensitivePolicy.
11 */
12 class CustomerMetaEnricher
13 {
14 const MAX_LEN = 120;
15
16 /**
17 * [customer_id => line] for one list page. One filter call per page.
18 *
19 * @param iterable $tickets The page of Ticket models (customer eager-loaded).
20 * @param array $context Extra context passed to the filter (e.g. agent_id).
21 * @return array<int,string>
22 */
23 public static function resolve($tickets, array $context = [])
24 {
25 if (!PermissionManager::userCan('fst_sensitive_data')) {
26 return [];
27 }
28
29 $customers = [];
30 foreach ($tickets as $ticket) {
31 if ($ticket->customer_id && $ticket->relationLoaded('customer') && $ticket->customer) {
32 $customers[$ticket->customer_id] = $ticket->customer;
33 }
34 }
35
36 if (!$customers) {
37 return [];
38 }
39
40 $customerIds = array_keys($customers);
41
42 /**
43 * Filter: fluent_support/mcp_customer_list_meta
44 *
45 * Inject a short, plain-text status line per customer for MCP list
46 * surfaces, e.g. "Paying · 8,016 sent/30d · domain verified".
47 *
48 * Contract:
49 * - Batch: fired once per page. Use one query for all IDs, not one per customer.
50 * Loaded Customer models are passed to save a lookup.
51 * - Cheap: runs on every list render. No remote calls; cache anything expensive.
52 * - Plain text: tags and control chars are stripped and values capped at 120 chars.
53 * Non-scalar values and unknown IDs are dropped.
54 * - Additive: merge into the incoming map, keep existing keys.
55 *
56 * @param array<int,string> $lines Default []. Merge, don't overwrite.
57 * @param int[] $customerIds Deduped customer IDs on this page.
58 * @param array $customers [customer_id => loaded Customer model].
59 * @param array $context ['surface' => string, 'agent_id' => int|null].
60 * @return array<int,string> [customer_id => plain-text line].
61 */
62 $raw = apply_filters(
63 'fluent_support/mcp_customer_list_meta',
64 [],
65 $customerIds,
66 $customers,
67 $context
68 );
69
70 return self::sanitize($raw, $customerIds);
71 }
72
73 /**
74 * Coerce the untrusted filter output into a safe [int => plain string] map.
75 *
76 * @param mixed $raw
77 * @param int[] $allowedIds
78 * @return array<int,string>
79 */
80 private static function sanitize($raw, array $allowedIds)
81 {
82 if (!is_array($raw)) {
83 return [];
84 }
85
86 $allowed = array_flip(array_map('intval', $allowedIds));
87 $clean = [];
88
89 foreach ($raw as $id => $value) {
90 $id = (int) $id;
91
92 if (!isset($allowed[$id]) || !is_scalar($value)) {
93 continue;
94 }
95
96 $value = wp_strip_all_tags((string) $value, true);
97 $value = preg_replace('/[\x00-\x1F\x7F]+/u', ' ', $value);
98 $value = trim(preg_replace('/\s+/u', ' ', $value));
99
100 if ($value === '') {
101 continue;
102 }
103
104 $clean[$id] = function_exists('mb_substr')
105 ? mb_substr($value, 0, self::MAX_LEN)
106 : substr($value, 0, self::MAX_LEN);
107 }
108
109 return $clean;
110 }
111 }
112