PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.6.5
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.6.5
1.6.5 1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.4 1.5.5 1.5.3 1.5.2 1.5.1 1.5.0 1.4.2 1.4.1 1.4.0 1.3.28 1.3.27 1.3.26 1.3.25 1.3.23 1.3.22 1.3.21 1.3.20 1.3.19 trunk All 48 releases
← All changes | app/Http/Controllers/FrontendControllers/CustomerProfileController.php +98 -0 1.5.4 → 1.6.5 View file →
@@ -38,8 +38,15 @@
38 38
39 39
40 40 class CustomerProfileController extends BaseFrontendController
41 41 {
42 + /**
43 + * Portal surfaces an add-on may attach a section to. Each value maps to the
44 + * hook `fluent_cart/customer_portal/{value}`.
45 + */
46 + const PORTAL_SECTION_FILTERS = [
47 + 'profile_sections'
48 + ];
42 49
43 50 /**
44 51 * Handle the request to retrieve the customer's orders.
45 52 *
@@ -91,8 +98,9 @@
91 98 return [
92 99 'created_at' => $order->created_at->format('Y-m-d H:i:s'),
93 100 'invoice_no' => $order->invoice_no,
94 101 'total_amount' => $order->total_amount,
102 + 'currency' => $order->currency,
95 103 'uuid' => $order->uuid,
96 104 'type' => $order->type,
97 105 'status' => $order->status,
98 106 'renewals_count' => $order->renewals_count,
@@ -176,8 +184,98 @@
176 184 return $this->sendSuccess([
177 185 'message' => __('Success', 'fluent-cart'),
178 186 'data' => $userData
179 187 ]);
188 + }
189 +
190 + /**
191 + * Return the add-on sections registered for one customer-portal surface.
192 + *
193 + * Each entry carries an UNCOMPILED Vue component string plus its payload;
194 + * the portal SPA compiles it in the browser (see the shared
195 + * DynamicTemplateParser). This is how an add-on renders inside the SPA at
196 + * all — the router's route table is static and cannot be extended.
197 + *
198 + * @param Request $request
199 + * @return \WP_REST_Response
200 + */
201 + public function getSections(Request $request): \WP_REST_Response
202 + {
203 + // `?filter[]=x` makes this an array, and casting one to string emits a
204 + // warning a caller could raise at will.
205 + $requestedFilter = $request->get('filter', '');
206 + $filter = is_string($requestedFilter) ? sanitize_text_field($requestedFilter) : '';
207 +
208 + // Allowlisted, never interpolated from the raw request value: building
209 + // a hook name out of caller input would let anyone fire arbitrary
210 + // filters through this endpoint.
211 + if (!in_array($filter, self::PORTAL_SECTION_FILTERS, true)) {
212 + return $this->sendError([
213 + 'message' => __('Unknown portal section group.', 'fluent-cart')
214 + ], 422);
215 + }
216 +
217 + $customer = CustomerResource::getCurrentCustomer();
218 +
219 + // A logged-in WP user who has never bought anything is not a customer.
220 + // Short-circuit rather than firing the filter with a null customer,
221 + // so no add-on has to remember to handle that case correctly.
222 + if (!$customer) {
223 + return $this->sendSuccess([
224 + 'message' => __('Success', 'fluent-cart'),
225 + 'sections' => []
226 + ]);
227 + }
228 +
229 + $sections = apply_filters('fluent_cart/customer_portal/' . $filter, [], [
230 + 'customer' => $customer
231 + ]);
232 +
233 + return $this->sendSuccess([
234 + 'message' => __('Success', 'fluent-cart'),
235 + 'sections' => $this->formatPortalSections($sections)
236 + ]);
237 + }
238 +
239 + /**
240 + * Normalise whatever add-ons returned into the shape the SPA renders, and
241 + * drop entries with nothing to compile.
242 + *
243 + * @param mixed $sections
244 + * @return array
245 + */
246 + private function formatPortalSections($sections): array
247 + {
248 + if (!is_array($sections)) {
249 + return [];
250 + }
251 +
252 + $formatted = [];
253 +
254 + foreach ($sections as $sectionKey => $section) {
255 + if (!is_array($section) || empty($section['component'])) {
256 + continue;
257 + }
258 +
259 + $key = Arr::get($section, 'key', $sectionKey);
260 + $type = Arr::get($section, 'type', 'vue-template');
261 +
262 + // sanitize_key()/sanitize_text_field() are scalar-only and throw on
263 + // an array in PHP 8. A malformed add-on entry should drop out here,
264 + // not 500 the whole endpoint.
265 + if (!is_scalar($section['component']) || !is_scalar($key) || !is_scalar($type)) {
266 + continue;
267 + }
268 +
269 + $formatted[] = [
270 + 'key' => sanitize_key($key),
271 + 'type' => sanitize_text_field($type),
272 + 'component' => (string)$section['component'],
273 + 'payload' => Arr::get($section, 'payload', [])
274 + ];
275 + }
276 +
277 + return $formatted;
180 278 }
181 279
182 280 public function updateCustomerProfileDetails(CustomerProfileAccountDetailsRequest $request): \WP_REST_Response
183 281 {