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
← All changes | app/Models/Customer.php +26 -49 2.4.0 → 2.4.5 View file →
@@ -58,11 +58,12 @@
58 58 ];
59 59 }
60 60
61 61 /**
62 - * maybeCreateCustomer method will update existing customer or create new
63 - * This method will get request to create customer, this will check existence, if exist it will update otherwise it will create new.
64 - * @param $customerData
62 + * Find the customer for this data or create one. An existing record is only
63 + * updated when the data carries a user_id or remote_uid.
64 + *
65 + * @param array $customerData
65 66 * @return false|Customer
66 67 */
67 68 public static function maybeCreateCustomer($customerData)
68 69 {
@@ -88,28 +89,25 @@
88 89 }
89 90
90 91 $customerData = self::explodeFullName($customerData);
91 92
92 - // we have to create customer
93 93 $customer = self::create($customerData);
94 94
95 - /*
96 - * Action on customer create
97 - *
98 - * @since v1.0.0
99 - * @param object $customer
100 - */
101 - do_action('fluent_support/customer_created', $customer);
95 + if ($customer && $customer->exists) {
96 + /*
97 + * Action on customer create
98 + *
99 + * @since v1.0.0
100 + * @param object $customer
101 + */
102 + do_action('fluent_support/customer_created', $customer);
103 + }
102 104
103 105 return $customer;
104 106 }
105 107
106 - // An existing row may never be claimed for a WordPress account it is not
107 - // already linked to. Binding happens when the row is first created, when
108 - // the account registers (ProfileInfoService::onWPUserRegister), or when
109 - // an agent sets it explicitly from the customer profile. Allowing an
110 - // email match to write user_id here is what made a portal takeover
111 - // persistent.
108 + // Never link an existing row to a different WP account by email match.
109 + // Linking happens on create, on registration, or by an agent.
112 110 $incomingUserId = (int) Arr::get($customerData, 'user_id');
113 111
114 112 if ($incomingUserId && $incomingUserId !== (int) $customer->user_id) {
115 113 unset($customerData['user_id']);
@@ -114,24 +112,11 @@
114 112 if ($incomingUserId && $incomingUserId !== (int) $customer->user_id) {
115 113 unset($customerData['user_id']);
116 114 }
117 115
118 - // Intake may not move the contact address of a record that belongs to a
119 - // WordPress account. Every caller here builds its payload from an
120 - // address it has not verified -- most of them from the signed-in
121 - // account's own email, which WordPress lets that account change with no
122 - // confirmation at all.
123 - //
124 - // Without this, ProfileInfoService::onWPProfileUpdate holding the
125 - // address is worth nothing: it refuses the unverified change, and then
126 - // the next POST to /customer-portal/tickets resolves the same record
127 - // through here and writes the new address anyway. It also produced
128 - // duplicate rows on an address belonging to somebody else, without the
129 - // collision check the profile_update path applies.
130 - //
131 - // The address moves through the two paths that prove something instead:
132 - // EmailClaimService, where the customer opens a link sent to the address
133 - // being claimed, and an agent editing the record directly.
116 + // The incoming email is unverified (WP lets users change theirs without
117 + // confirmation), so it must not overwrite a linked customer's address.
118 + // That only changes via EmailClaimService or an agent edit.
134 119 if ((int) $customer->user_id) {
135 120 unset($customerData['email']);
136 121 }
137 122
@@ -145,9 +130,8 @@
145 130 }
146 131
147 132
148 133 /**
149 - * One2Many: Customer has to many Click Tickets
150 134 * @return Model Collection
151 135 */
152 136 public function tickets()
153 137 {
@@ -160,11 +144,12 @@
160 144 );
161 145 }
162 146
163 147 /**
164 - * getCustomerFromData method will return customer information by user id or email address
165 - * @param $customerData
166 - * @return false
148 + * Look up by remote_uid, then user_id, then email.
149 + *
150 + * @param array $customerData
151 + * @return false|Customer
167 152 */
168 153 public static function getCustomerFromData($customerData)
169 154 {
170 155 $remoteUid = Arr::get($customerData, 'remote_uid');
@@ -178,18 +163,12 @@
178 163 return $customer;
179 164 }
180 165 }
181 166
182 - // A WordPress user id is the only identity that can be trusted here. An
183 - // account email is editable by its own holder with no verification: the
184 - // REST users endpoint calls wp_update_user() directly and skips the
185 - // confirmation flow that profile.php runs. An email must therefore never
186 - // be able to reach a customer row that belongs to somebody else.
187 - // Both lookups order by id so the row that wins is always the oldest
188 - // match rather than whatever the storage engine returns first. The email
189 - // column carries a plain index, not a unique one, so duplicates are
190 - // possible and an inbound reply must keep threading onto the original
191 - // record. Helper::getCurrentPerson() orders for the same reason.
167 + // When a user id is given, don't fall back to email: a WP account's email
168 + // is unverified (the REST users endpoint skips confirmation), so it must
169 + // not reach another person's row. Email isn't unique, so take the oldest
170 + // match to keep replies threading onto the original record.
192 171 if ($userId) {
193 172 return self::where('user_id', $userId)->orderBy('id', 'ASC')->first() ?: false;
194 173 }
195 174
@@ -200,9 +179,8 @@
200 179 return false;
201 180 }
202 181
203 182 /**
204 - * getTicketCounts method will return the number of tickets by a customer
205 183 * @return mixed
206 184 */
207 185 public function getTicketCounts()
208 186 {
@@ -209,9 +187,8 @@
209 187 return Ticket::where('customer_id', $this->id)->count();
210 188 }
211 189
212 190 /**
213 - * getResponseCounts will return the number of responses by a customer
214 191 * @return mixed
215 192 */
216 193 public function getResponseCounts()
217 194 {