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 +108 -9 1.3.21 → 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 *
@@ -71,9 +78,9 @@
71 78
72 79
73 80 $orders = Order::query()
74 81 ->with(['order_items' => function ($query) {
75 - $query->select('id', 'order_id', 'post_title', 'title', 'quantity', 'payment_type', 'line_meta');
82 + $query->select('id', 'order_id', 'object_id', 'post_title', 'title', 'quantity', 'payment_type', 'line_meta', 'other_info');
76 83 }])
77 84 ->where('customer_id', $customer->id)
78 85 ->where(function ($query) {
79 86 $query
@@ -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,
@@ -97,14 +105,15 @@
97 105 'status' => $order->status,
98 106 'renewals_count' => $order->renewals_count,
99 107 'order_items' => $order->order_items->map(function ($item) {
100 108 return [
101 - 'id' => $item->id,
102 - 'post_title' => $item->post_title,
103 - 'title' => $item->title,
104 - 'quantity' => $item->quantity,
105 - 'payment_type' => $item->payment_type,
106 - 'line_meta' => [
109 + 'id' => $item->id,
110 + 'post_title' => $item->post_title,
111 + 'title' => $item->title,
112 + 'variation_display_title' => $item->variation_display_title,
113 + 'quantity' => $item->quantity,
114 + 'payment_type' => $item->payment_type,
115 + 'line_meta' => [
107 116 'bundle_parent_item_id' => Arr::get($item, 'line_meta.bundle_parent_item_id', null),
108 117 ]
109 118 ];
110 119 }),
@@ -177,8 +186,98 @@
177 186 'data' => $userData
178 187 ]);
179 188 }
180 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;
278 + }
279 +
181 280 public function updateCustomerProfileDetails(CustomerProfileAccountDetailsRequest $request): \WP_REST_Response
182 281 {
183 282 $errorResponse = $this->checkUserLoggedIn();
184 283
@@ -272,9 +371,9 @@
272 371 // Get the current logged-in customer
273 372 $customer = CustomerResource::getCurrentCustomer(true);
274 373
275 374 // Sanitize and retrieve the request data
276 - $data = $request->getSafe($request->sanitize());
375 + $data = CustomerAddressResource::normalizeBusinessFields($request->getSafe($request->sanitize()));
277 376
278 377 // Attempt to create a new address for the logged-in customer
279 378 $isCreated = CustomerAddressResource::create(
280 379 $data,
@@ -306,9 +405,9 @@
306 405 // Get the current logged-in customer
307 406 $customer = CustomerResource::getCurrentCustomer();
308 407
309 408 // Sanitize and retrieve the request data
310 - $data = $request->getSafe($request->sanitize());
409 + $data = CustomerAddressResource::normalizeBusinessFields($request->getSafe($request->sanitize()));
311 410
312 411 // Retrieve the address ID from the request
313 412 $id = $request->getSafe('id', 'intval');
314 413