PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.3.21
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.3.21
1.6.6 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 All 49 releases
fluent-cart / app / Hooks / Handlers / ShortCodes / CustomerProfileHandler.php

CustomerProfileHandler.php in FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler 1.3.21, at app/Hooks/Handlers/ShortCodes/CustomerProfileHandler.php

412 lines 20.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCart\App\Hooks\Handlers\ShortCodes;
4
5 use FluentCart\Api\ModuleSettings;
6 use FluentCart\Api\PaymentMethods;
7 use FluentCart\Api\Resource\CustomerResource;
8 use FluentCart\Api\StoreSettings;
9 use FluentCart\App\App;
10 use FluentCart\App\Helpers\Helper;
11 use FluentCart\App\Models\Subscription;
12 use FluentCart\App\Modules\Templating\AssetLoader;
13 use FluentCart\App\Services\TemplateService;
14 use FluentCart\App\Services\Translations\TransStrings;
15 use FluentCart\App\Vite;
16 use FluentCart\Framework\Support\Arr;
17 use FluentCart\Framework\Support\Str;
18
19 class CustomerProfileHandler extends ShortCode
20 {
21 const SHORT_CODE = 'fluent_cart_customer_profile';
22 protected static string $shortCodeName = 'fluent_cart_customer_profile';
23
24 protected static $slug = '';
25 protected string $assetsPath = '';
26
27 public function renderShortcode($block = null)
28 {
29 ob_start(null);
30 $view = $this->render(
31 $this->viewData()
32 );
33 return $view ?? ob_get_clean();
34 }
35
36 public static function register()
37 {
38 parent::register();
39
40 // Add wildcard customer profile pages
41 // add a custom permalink endpoint
42 add_action('init', function () {
43 $pageSlug = (new StoreSettings())->getCustomerDashboardPageSlug();
44 $customerProfilePageId = (new StoreSettings())->getCustomerProfilePageId();
45 static::$slug = $pageSlug;
46
47 if($customerProfilePageId && $pageSlug) {
48 add_rewrite_rule(
49 '^'.$pageSlug.'/(.+)?$',
50 'index.php?page_id='.$customerProfilePageId,
51 'top'
52 );
53 }
54 });
55 }
56
57
58 public function render(?array $viewData = null)
59 {
60 if (!is_user_logged_in()) {
61 ob_start();
62 $redirectUrl = (new StoreSettings())->getCustomerProfilePage();
63 if (defined('FLUENT_AUTH_VERSION') && (new \FluentAuth\App\Hooks\Handlers\CustomAuthHandler())->isEnabled()) {
64 ?>
65 <div style="max-width: 600px; margin: 0 auto; padding: 20px; border: 1px solid #CBD5E0; border-radius: 8px;" class="fct_auth_wrap">
66 <h4><?php echo esc_html__('Please log in to access your customer portal.', 'fluent-cart'); ?></h4>
67 <?php
68 echo do_shortcode('[fluent_auth redirect_to="' . $redirectUrl . '"]');
69 echo '</div>';
70 } else {
71 ?>
72 <div class="fct_auth_wrap">
73 <div class="fct_auth_message">
74 <h2><?php echo esc_html__('Login', 'fluent-cart'); ?></h2>
75 <p><?php echo esc_html__('Please log in to access your customer portal.', 'fluent-cart'); ?></p>
76 <a href="<?php echo esc_url(wp_login_url($redirectUrl ?? '')); ?>" class="button">
77 <?php echo esc_html__('Login', 'fluent-cart'); ?>
78 </a>
79 </div>
80 </div>
81 <?php
82 }
83 return ob_get_clean();
84 }
85
86 $this->renderCustomerAppContainer();
87 }
88
89 public function renderCustomerAppContainer()
90 {
91
92 // Enqueue global styles
93 Vite::enqueueStyle( 'fluent-cart-customer-profile-global',
94 'public/customer-profile/style/customer-profile-global.scss',
95 );
96
97 $customEndpointContent = $this->maybeCustomEndpointContent();
98
99 if(!$customEndpointContent) {
100 (new static())->enqueueStyles();
101 }
102
103 $colors = self::generateCssColorVariables(Arr::get($this->shortCodeAttributes, 'colors', ''));
104 add_action('fluent_cart/customer_menu', array($this, 'renderCustomerMenu'));
105 add_action('fluent_cart/customer_app', function () use ($customEndpointContent) {
106 if($customEndpointContent) {
107 echo $customEndpointContent; // @phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
108 } else {
109 AssetLoader::loadCustomerDashboardAssets();
110 $this->renderCustomerApp();
111 }
112 });
113
114 App::make('view')->render('frontend.customer_app', [
115 'wp_page_title' => get_the_title(),
116 'colors' => $colors,
117 'active_tab' => apply_filters('fluent_cart/customer_portal/active_tab', ''),
118 ]);
119 }
120
121 public function maybeCustomEndpointContent() {
122 global $wp;
123 $requestedPath = $wp->request;
124 // remove static::$slug from the requested path
125 if (static::$slug && Str::startsWith($requestedPath, static::$slug)) {
126 $requestedPath = Str::replaceFirst(static::$slug, '', $requestedPath);
127 $requestedPath = trim($requestedPath, '/');
128 }
129
130 if($requestedPath) {
131 $paths = explode('/', $requestedPath);
132 $requestedPath = array_shift($paths);
133 }
134
135 $reserved = ['dashboard', 'purchase-history', 'subscriptions', 'licenses', 'downloads', 'profile'];
136 if(!$requestedPath || in_array($requestedPath, $reserved)) {
137 return ''; // No specific path requested, return early
138 }
139
140 // Maybe it's a custom endpoint path
141 $customEndpoints = apply_filters('fluent_cart/customer_portal/custom_endpoints', []);
142
143 if(empty($customEndpoints) || !isset($customEndpoints[$requestedPath])) {
144 return ''; // No custom endpoints defined, return early
145 }
146
147 ob_start();
148 $endpoint = $customEndpoints[$requestedPath];
149
150 add_filter('fluent_cart/customer_portal/active_tab', function ($activeTab) use ($requestedPath) {
151 return $requestedPath;
152 });
153
154 if (isset($endpoint['render_callback']) && is_callable($endpoint['render_callback'])) {
155 call_user_func($endpoint['render_callback']);
156 return ob_get_clean();
157 }
158
159 if(isset($endpoint['page_id'])) {
160 $pageId = (int) $endpoint['page_id'];
161 // Create a custom query to fetch the two pages
162 $args = array(
163 'post_type' => 'page',
164 'post__in' => [$pageId],
165 'posts_per_page' => 1,
166 'orderby' => 'post__in', // Preserve the order of the IDs
167 );
168
169 $page_query = new \WP_Query($args);
170
171 // Check if the query has posts
172 if ($page_query->have_posts()) :
173 while ($page_query->have_posts()) : $page_query->the_post();
174 ?>
175 <div class="fluent-cart-custom-page-content">
176 <div><?php the_content(); ?></div>
177 </div>
178 <?php
179 endwhile;
180 // Reset post data to avoid conflicts with other queries
181 wp_reset_postdata();
182 else :
183 echo '<p>' . esc_html__('No content found!', 'fluent-cart') . '</p>';
184 endif;
185 }
186
187 return ob_get_clean();
188 }
189
190 public function renderCustomerApp()
191 {
192 echo '<div data-fluent-cart-customer-profile-app><app/></div>';
193 }
194
195 public function renderCustomerMenu()
196 {
197 $baseUrl = TemplateService::getCustomerProfileUrl('/');
198
199 $menuItems = [
200 'dashboard' => [
201 'label' => __('Dashboard', 'fluent-cart'),
202 'css_class' => 'fct_route',
203 'link' => $baseUrl,
204 'icon_svg' => '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
205 <path d="M5.875 9.625C5.43179 9.625 4.99292 9.5377 4.58344 9.36809C4.17397 9.19848 3.80191 8.94988 3.48851 8.63649C3.17512 8.32309 2.92652 7.95103 2.75691 7.54156C2.5873 7.13208 2.5 6.69321 2.5 6.25C2.5 5.80679 2.5873 5.36792 2.75691 4.95844C2.92652 4.54897 3.17512 4.17691 3.48851 3.86351C3.80191 3.55012 4.17397 3.30152 4.58344 3.13191C4.99292 2.9623 5.43179 2.875 5.875 2.875C6.77011 2.875 7.62855 3.23058 8.26148 3.86351C8.89442 4.49645 9.25 5.35489 9.25 6.25C9.25 7.14511 8.89442 8.00355 8.26148 8.63649C7.62855 9.26942 6.77011 9.625 5.875 9.625V9.625ZM6.25 17.125C5.35489 17.125 4.49645 16.7694 3.86351 16.1365C3.23058 15.5035 2.875 14.6451 2.875 13.75C2.875 12.8549 3.23058 11.9965 3.86351 11.3635C4.49645 10.7306 5.35489 10.375 6.25 10.375C7.14511 10.375 8.00355 10.7306 8.63648 11.3635C9.26942 11.9965 9.625 12.8549 9.625 13.75C9.625 14.6451 9.26942 15.5035 8.63648 16.1365C8.00355 16.7694 7.14511 17.125 6.25 17.125V17.125ZM13.75 9.625C13.3068 9.625 12.8679 9.5377 12.4584 9.36809C12.049 9.19848 11.6769 8.94988 11.3635 8.63649C11.0501 8.32309 10.8015 7.95103 10.6319 7.54156C10.4623 7.13208 10.375 6.69321 10.375 6.25C10.375 5.80679 10.4623 5.36792 10.6319 4.95844C10.8015 4.54897 11.0501 4.17691 11.3635 3.86351C11.6769 3.55012 12.049 3.30152 12.4584 3.13191C12.8679 2.9623 13.3068 2.875 13.75 2.875C14.6451 2.875 15.5035 3.23058 16.1365 3.86351C16.7694 4.49645 17.125 5.35489 17.125 6.25C17.125 7.14511 16.7694 8.00355 16.1365 8.63649C15.5035 9.26942 14.6451 9.625 13.75 9.625V9.625ZM13.75 17.125C12.8549 17.125 11.9964 16.7694 11.3635 16.1365C10.7306 15.5035 10.375 14.6451 10.375 13.75C10.375 12.8549 10.7306 11.9965 11.3635 11.3635C11.9964 10.7306 12.8549 10.375 13.75 10.375C14.6451 10.375 15.5035 10.7306 16.1365 11.3635C16.7694 11.9965 17.125 12.8549 17.125 13.75C17.125 14.6451 16.7694 15.5035 16.1365 16.1365C15.5035 16.7694 14.6451 17.125 13.75 17.125ZM5.875 8.125C6.37228 8.125 6.84919 7.92746 7.20082 7.57583C7.55246 7.22419 7.75 6.74728 7.75 6.25C7.75 5.75272 7.55246 5.27581 7.20082 4.92417C6.84919 4.57254 6.37228 4.375 5.875 4.375C5.37772 4.375 4.90081 4.57254 4.54917 4.92417C4.19754 5.27581 4 5.75272 4 6.25C4 6.74728 4.19754 7.22419 4.54917 7.57583C4.90081 7.92746 5.37772 8.125 5.875 8.125V8.125ZM6.25 15.625C6.74728 15.625 7.22419 15.4275 7.57582 15.0758C7.92746 14.7242 8.125 14.2473 8.125 13.75C8.125 13.2527 7.92746 12.7758 7.57582 12.4242C7.22419 12.0725 6.74728 11.875 6.25 11.875C5.75272 11.875 5.27581 12.0725 4.92417 12.4242C4.57254 12.7758 4.375 13.2527 4.375 13.75C4.375 14.2473 4.57254 14.7242 4.92417 15.0758C5.27581 15.4275 5.75272 15.625 6.25 15.625ZM13.75 8.125C14.2473 8.125 14.7242 7.92746 15.0758 7.57583C15.4275 7.22419 15.625 6.74728 15.625 6.25C15.625 5.75272 15.4275 5.27581 15.0758 4.92417C14.7242 4.57254 14.2473 4.375 13.75 4.375C13.2527 4.375 12.7758 4.57254 12.4242 4.92417C12.0725 5.27581 11.875 5.75272 11.875 6.25C11.875 6.74728 12.0725 7.22419 12.4242 7.57583C12.7758 7.92746 13.2527 8.125 13.75 8.125ZM13.75 15.625C14.2473 15.625 14.7242 15.4275 15.0758 15.0758C15.4275 14.7242 15.625 14.2473 15.625 13.75C15.625 13.2527 15.4275 12.7758 15.0758 12.4242C14.7242 12.0725 14.2473 11.875 13.75 11.875C13.2527 11.875 12.7758 12.0725 12.4242 12.4242C12.0725 12.7758 11.875 13.2527 11.875 13.75C11.875 14.2473 12.0725 14.7242 12.4242 15.0758C12.7758 15.4275 13.2527 15.625 13.75 15.625Z" fill="currentColor"/>
206 </svg>',
207 ],
208 'purchase-history' => [
209 'label' => __('Purchase History', 'fluent-cart'),
210 'css_class' => 'fct_route',
211 'link' => $baseUrl . 'purchase-history',
212 'icon_svg' => '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
213 <path d="M10 2.5C14.1422 2.5 17.5 5.85775 17.5 10C17.5 14.1422 14.1422 17.5 10 17.5C5.85775 17.5 2.5 14.1422 2.5 10H4C4 13.3135 6.6865 16 10 16C13.3135 16 16 13.3135 16 10C16 6.6865 13.3135 4 10 4C7.9375 4 6.118 5.04025 5.03875 6.625H7V8.125H2.5V3.625H4V5.5C5.368 3.6775 7.54675 2.5 10 2.5ZM10.75 6.25V9.68875L13.1823 12.121L12.121 13.1823L9.25 10.3098V6.25H10.75Z" fill="currentColor"/>
214 </svg>'
215 ],
216 'subscriptions' => [
217 'label' => __('Subscription Plans', 'fluent-cart'),
218 'css_class' => 'fct_route',
219 'link' => $baseUrl . 'subscriptions',
220 'icon_svg' => '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
221 <path d="M10 5C11.7179 5 13.2343 5.86641 14.1348 7.1875H12.5V8.4375H16.25V4.6875H15V6.2496C13.8601 4.73229 12.0452 3.75 10 3.75C6.54822 3.75 3.75 6.54822 3.75 10H5C5 7.23857 7.23857 5 10 5ZM15 10C15 12.7614 12.7614 15 10 15C8.28215 15 6.76567 14.1336 5.86527 12.8125H7.5V11.5625H3.75V15.3125H5V13.7504C6.13988 15.2677 7.95477 16.25 10 16.25C13.4517 16.25 16.25 13.4517 16.25 10H15Z" fill="currentColor"/>
222 </svg>'
223 ],
224 'licenses' => [
225 'label' => __('Licenses', 'fluent-cart'),
226 'css_class' => 'fct_route',
227 'link' => $baseUrl . 'licenses',
228 'icon_svg' => '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
229 <path d="M16 17.5H4C3.80109 17.5 3.61032 17.421 3.46967 17.2803C3.32902 17.1397 3.25 16.9489 3.25 16.75V3.25C3.25 3.05109 3.32902 2.86032 3.46967 2.71967C3.61032 2.57902 3.80109 2.5 4 2.5H16C16.1989 2.5 16.3897 2.57902 16.5303 2.71967C16.671 2.86032 16.75 3.05109 16.75 3.25V16.75C16.75 16.9489 16.671 17.1397 16.5303 17.2803C16.3897 17.421 16.1989 17.5 16 17.5ZM15.25 16V4H4.75V16H15.25ZM7 6.25H13V7.75H7V6.25ZM7 9.25H13V10.75H7V9.25ZM7 12.25H10.75V13.75H7V12.25Z" fill="currentColor"/>
230 </svg>'
231 ],
232 'downloads' => [
233 'label' => __('Downloads', 'fluent-cart'),
234 'css_class' => 'fct_route',
235 'link' => $baseUrl . 'downloads',
236 'icon_svg' => '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
237 <path d="M10.75 8.5H14.5L10 13L5.5 8.5H9.25V3.25H10.75V8.5ZM4 15.25H16V10H17.5V16C17.5 16.1989 17.421 16.3897 17.2803 16.5303C17.1397 16.671 16.9489 16.75 16.75 16.75H3.25C3.05109 16.75 2.86032 16.671 2.71967 16.5303C2.57902 16.3897 2.5 16.1989 2.5 16V10H4V15.25Z" fill="currentColor"/>
238 </svg>'
239 ],
240 'profile' => [
241 'label' => __('Profile', 'fluent-cart'),
242 'css_class' => 'fct_route',
243 'link' => $baseUrl . 'profile',
244 'icon_svg' => '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
245 <path d="M4 17.5C4 15.9087 4.63214 14.3826 5.75736 13.2574C6.88258 12.1321 8.4087 11.5 10 11.5C11.5913 11.5 13.1174 12.1321 14.2426 13.2574C15.3679 14.3826 16 15.9087 16 17.5H14.5C14.5 16.3065 14.0259 15.1619 13.182 14.318C12.3381 13.4741 11.1935 13 10 13C8.80653 13 7.66193 13.4741 6.81802 14.318C5.97411 15.1619 5.5 16.3065 5.5 17.5H4ZM10 10.75C7.51375 10.75 5.5 8.73625 5.5 6.25C5.5 3.76375 7.51375 1.75 10 1.75C12.4863 1.75 14.5 3.76375 14.5 6.25C14.5 8.73625 12.4863 10.75 10 10.75ZM10 9.25C11.6575 9.25 13 7.9075 13 6.25C13 4.5925 11.6575 3.25 10 3.25C8.3425 3.25 7 4.5925 7 6.25C7 7.9075 8.3425 9.25 10 9.25Z" fill="currentColor"/>
246 </svg>'
247 ]
248 ];
249
250 $currentCustomer = CustomerResource::getCurrentCustomer();
251
252 if (!ModuleSettings::isActive('license') || !App::isProActive()) {
253 unset($menuItems['licenses']);
254 }
255
256
257 if($currentCustomer) {
258 $hasSubscriptions = Subscription::query()->where('customer_id', $currentCustomer->id)->exists();
259 if(!$hasSubscriptions) {
260 unset($menuItems['subscriptions']);
261 }
262 } else {
263 unset($menuItems['subscriptions']);
264 }
265
266 $menuItems = apply_filters('fluent_cart/global_customer_menu_items', $menuItems, [
267 'base_url' => $baseUrl
268 ]);
269
270 $profileData = null;
271 if($currentCustomer) {
272 $profileData = [
273 'email' => $currentCustomer->email,
274 'full_name' => $currentCustomer->full_name,
275 'photo' => $currentCustomer->photo
276 ];
277 } else if(is_user_logged_in()) {
278 $user = wp_get_current_user();
279 $profileData = [
280 'email' => $user->user_email,
281 'full_name' => $user->display_name,
282 'photo' => get_avatar_url($user->ID)
283 ];
284 }
285
286 add_filter('fct_allowed_svg_tags', function ($tags) {
287 return [
288 'svg' => [
289 'xmlns' => true,
290 'width' => true,
291 'height' => true,
292 'viewBox' => true,
293 'fill' => true,
294 'stroke' => true,
295 'stroke-width' => true,
296 'class' => true
297 ],
298 'path' => [
299 'd' => true,
300 'fill' => true,
301 'stroke' => true,
302 'stroke-width' => true
303 ],
304 'g' => [
305 'fill' => true,
306 'stroke' => true
307 ]
308 ];
309 });
310
311
312 App::make('view')->render('frontend.customer_menu', [
313 'menuItems' => $menuItems,
314 'profileData' => $profileData,
315 ]);
316 }
317
318 public static function getLocalizationData($attributes = []):array
319 {
320
321 $currentCustomer = CustomerResource::getCurrentCustomer();
322 $customerEmail = $currentCustomer ? $currentCustomer->email : '';
323
324
325 $pageUrl = TemplateService::getCustomerProfileUrl();
326
327 $pageSlug = trim(str_replace(home_url('/'), '', $pageUrl), '/');
328 $dashboardSlug = (new StoreSettings())->getCustomerDashboardPageSlug();
329
330 if($pageSlug !== $dashboardSlug) {
331 // we should resave the page slug from settings
332 $prevSettings = (new StoreSettings())->get();
333 (new StoreSettings())->save($prevSettings);
334 }
335
336 $shopLocalizationData = Helper::shopConfig();
337 $shopLocalizationData['shop_url'] = (new StoreSettings())->getShopPage();
338
339
340 // For supporting subdirectory installations
341 $domainParts = parse_url(home_url('/'));
342 // if we have path in domain url, we need to set it as base path
343 $basePath = trim(Arr::get($domainParts, 'path', ''), '/');
344 if($basePath && $basePath !== '/') {
345 $pageSlug = $basePath.'/'.$pageSlug;
346 }
347
348 return [
349 'fluentcart_customer_profile_vars' => [
350 'app_slug' => $pageSlug,
351 'app_url' => TemplateService::getCustomerProfileUrl(),
352 'shop' => $shopLocalizationData,
353 'trans' => TransStrings::getCustomerProfileString(),
354 'download_url_base' => site_url('fluent-cart/download-file/?fluent_cart_download=true'),
355 'placeholder_image' => Vite::getAssetUrl('images/placeholder.svg'),
356 'stripe_pub_key' => apply_filters('fluent_cart/payment_methods/stripe_pub_key', ''),
357 'paypal_client_id' => apply_filters('fluent_cart/payment_methods/paypal_client_id', '', []),
358 'assets_path' => Vite::getAssetUrl(),
359 'rest' => Helper::getRestInfo(),
360 'customer_email' => $customerEmail,
361 'wp_page_title' => get_the_title(),
362 'payment_methods' => PaymentMethods::getActiveMeta(),
363 'site_url' => site_url(),
364 'me' => [
365 'email' => $currentCustomer ? $currentCustomer->email : '',
366 'first_name' => $currentCustomer ? $currentCustomer->first_name : '',
367 'last_name' => $currentCustomer ? $currentCustomer->last_name : '',
368 'photo' => $currentCustomer ? $currentCustomer->photo : ''
369
370 ],
371 'logout_url' => wp_logout_url(home_url()),
372 'datei18' => TransStrings::dateTimeStrings(),
373 'el_strings' => TransStrings::elStrings(),
374 'wp_locale' => get_locale()
375 ],
376 'fluentCartRestVars' => [
377 'rest' => Helper::getRestInfo(),
378 ],
379 ];
380 }
381
382 public function localizeData(): array
383 {
384 return static::getLocalizationData($this->shortCodeAttributes);
385 }
386
387 private static function generateCssColorVariables($colors): string
388 {
389 $cssVariables = '';
390
391 if (!empty($colors)) {
392 // Split the colors string by commas to separate each key-value pair
393 $pairs = explode(',', $colors);
394 $colorVariables = [];
395
396 foreach ($pairs as $pair) {
397 list($key, $value) = explode('=', trim($pair));
398
399 // Only add to the array if the value is not empty or just a semicolon
400 if (!empty($value) && $value !== ';') {
401 $colorVariables[] = "$key: $value;";
402 }
403 }
404
405 $cssVariables = implode("\n", $colorVariables);
406 }
407
408 return $cssVariables;
409 }
410
411 }
412