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

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

505 lines 25.9 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\Services\CustomerIdentity\EmailClaimPortal;
11 use FluentCart\App\Services\CustomerIdentity\CustomerRecoveryService;
12 use FluentCart\App\Services\CustomerIdentity\EmailClaimService;
13 use FluentCart\App\Services\CustomerIdentity\EmailVerificationService;
14 use FluentCart\App\Helpers\CurrenciesHelper;
15 use FluentCart\App\Helpers\Helper;
16 use FluentCart\App\Models\Subscription;
17 use FluentCart\App\Modules\Templating\AssetLoader;
18 use FluentCart\App\Services\ProductReviewService;
19 use FluentCart\App\Services\Renderer\CheckoutFieldsSchema;
20 use FluentCart\App\Services\TemplateService;
21 use FluentCart\App\Services\DateTime\DayjsFormatter;
22 use FluentCart\App\Services\Translations\TransStrings;
23 use FluentCart\App\Vite;
24 use FluentCart\Framework\Support\Arr;
25 use FluentCart\Framework\Support\Str;
26
27 class CustomerProfileHandler extends ShortCode
28 {
29 const SHORT_CODE = 'fluent_cart_customer_profile';
30 protected static string $shortCodeName = 'fluent_cart_customer_profile';
31
32 protected static $slug = '';
33 protected string $assetsPath = '';
34
35 public function renderShortcode($block = null)
36 {
37 ob_start(null);
38 $view = $this->render(
39 $this->viewData()
40 );
41 return $view ?? ob_get_clean();
42 }
43
44 public static function register()
45 {
46 parent::register();
47
48 add_action(CustomerRecoveryService::HOOK, [CustomerRecoveryService::class, 'run']);
49
50 add_action('template_redirect', function () {
51 $redirect = EmailClaimPortal::handleSubmission();
52 if ($redirect) {
53 wp_safe_redirect($redirect);
54 exit;
55 }
56 });
57
58 // Add wildcard customer profile pages
59 // add a custom permalink endpoint
60 add_action('init', function () {
61 $pageSlug = (new StoreSettings())->getCustomerDashboardPageSlug();
62 $customerProfilePageId = (new StoreSettings())->getCustomerProfilePageId();
63 static::$slug = $pageSlug;
64
65 if($customerProfilePageId && $pageSlug) {
66 add_rewrite_rule(
67 '^'.$pageSlug.'/(.+)?$',
68 'index.php?page_id='.$customerProfilePageId,
69 'top'
70 );
71 }
72 });
73 }
74
75
76 public function render(?array $viewData = null)
77 {
78 if (!is_user_logged_in()) {
79 ob_start();
80 $redirectUrl = $this->resolveLoginRedirectUrl(
81 (new StoreSettings())->getCustomerProfilePage()
82 );
83
84 $claimToken = Arr::get($_GET, EmailClaimService::QUERY_TOKEN, '');
85 if (is_string($claimToken) && $claimToken !== '') {
86 $redirectUrl = add_query_arg(EmailClaimService::QUERY_TOKEN, sanitize_text_field(wp_unslash($claimToken)), (new StoreSettings())->getCustomerProfilePage());
87 }
88
89 if (defined('FLUENT_AUTH_VERSION') && (new \FluentAuth\App\Hooks\Handlers\CustomAuthHandler())->isEnabled()) {
90 ?>
91 <div style="max-width: 600px; margin: 0 auto; padding: 20px; border: 1px solid #CBD5E0; border-radius: 8px;" class="fct_auth_wrap">
92 <h4><?php echo esc_html__('Please log in to access your customer portal.', 'fluent-cart'); ?></h4>
93 <?php
94 // The URL travels inside a double-quoted shortcode attribute, where a
95 // bracket or quote would truncate the shortcode. Carry them encoded.
96 $attributeUrl = str_replace(['[', ']', '"'], ['%5B', '%5D', '%22'], $redirectUrl);
97 echo do_shortcode('[fluent_auth redirect_to="' . $attributeUrl . '"]');
98 echo '</div>';
99 } else {
100 // The Login link wears the portal's button pair (see
101 // customer-profile-global.scss), not the theme's `.button`.
102 Vite::enqueueStyle(
103 'fluent-cart-customer-profile-global',
104 'public/customer-profile/style/customer-profile-global.scss'
105 );
106 ?>
107 <div class="fct_auth_wrap">
108 <div class="fct_auth_message">
109 <h2><?php echo esc_html__('Login', 'fluent-cart'); ?></h2>
110 <p><?php echo esc_html__('Please log in to access your customer portal.', 'fluent-cart'); ?></p>
111 <a href="<?php echo esc_url(wp_login_url($redirectUrl ?? '')); ?>" class="button fct-customer-login-btn">
112 <?php echo esc_html__('Login', 'fluent-cart'); ?>
113 </a>
114 </div>
115 </div>
116 <?php
117 }
118 return ob_get_clean();
119 }
120
121 $this->renderCustomerAppContainer();
122 }
123
124 /**
125 * Resolve where a logged-out visitor should land once they have logged in.
126 *
127 * `redirect_to` is attacker-supplied, so it is only honoured when
128 * wp_validate_redirect() accepts it. That compares the parsed host against the
129 * site host. A string-prefix comparison must not be used here: a hostile host
130 * can be built by suffixing the site host, or by placing the site host in the
131 * userinfo position ahead of an `@`, and both keep the site URL as a prefix
132 * while resolving somewhere else entirely.
133 *
134 * @param string $fallbackUrl Where to send the visitor when no usable target was supplied.
135 * @return string
136 */
137 public function resolveLoginRedirectUrl($fallbackUrl)
138 {
139 if (empty($_GET['redirect_to']) || !is_string($_GET['redirect_to'])) {
140 return $fallbackUrl;
141 }
142
143 $intendedRedirectUrl = sanitize_url(wp_unslash($_GET['redirect_to']));
144
145 $validatedUrl = wp_validate_redirect($intendedRedirectUrl, '');
146
147 return $validatedUrl ? $validatedUrl : $fallbackUrl;
148 }
149
150 public function renderCustomerAppContainer()
151 {
152
153 // Enqueue global styles
154 Vite::enqueueStyle( 'fluent-cart-customer-profile-global',
155 'public/customer-profile/style/customer-profile-global.scss',
156 );
157
158 // Gate before custom endpoint callbacks or the dashboard load customer data.
159 $verificationNotice = EmailClaimPortal::render();
160 if (EmailVerificationService::isRequired(get_current_user_id())) {
161 echo $verificationNotice; // @phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- escaped in the view
162 return;
163 }
164
165 if (!EmailVerificationService::isEnabled()) {
166 CustomerResource::getCurrentCustomer(true);
167 }
168
169 $customEndpointContent = $this->maybeCustomEndpointContent();
170
171 if(!$customEndpointContent) {
172 (new static())->enqueueStyles();
173 }
174
175 $colors = self::generateCssColorVariables(Arr::get($this->shortCodeAttributes, 'colors', ''));
176 add_action('fluent_cart/customer_menu', array($this, 'renderCustomerMenu'));
177 add_action('fluent_cart/customer_app', function () use ($customEndpointContent, $verificationNotice) {
178 echo $verificationNotice; // @phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- escaped in the view
179 if($customEndpointContent) {
180 echo $customEndpointContent; // @phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
181 } else {
182 AssetLoader::loadCustomerDashboardAssets();
183 $this->renderCustomerApp();
184 }
185 });
186
187 App::make('view')->render('frontend.customer_app', [
188 'wp_page_title' => get_the_title(),
189 'colors' => $colors,
190 'active_tab' => apply_filters('fluent_cart/customer_portal/active_tab', ''),
191 ]);
192 }
193
194 public function maybeCustomEndpointContent() {
195 global $wp;
196 $requestedPath = $wp->request;
197 // remove static::$slug from the requested path
198 if (static::$slug && Str::startsWith($requestedPath, static::$slug)) {
199 $requestedPath = Str::replaceFirst(static::$slug, '', $requestedPath);
200 $requestedPath = trim($requestedPath, '/');
201 }
202
203 if($requestedPath) {
204 $paths = explode('/', $requestedPath);
205 $requestedPath = array_shift($paths);
206 }
207
208 $reserved = ['dashboard', 'purchase-history', 'subscriptions', 'licenses', 'downloads', 'profile'];
209 if(!$requestedPath || in_array($requestedPath, $reserved)) {
210 return ''; // No specific path requested, return early
211 }
212
213 // Maybe it's a custom endpoint path
214 $customEndpoints = apply_filters('fluent_cart/customer_portal/custom_endpoints', []);
215
216 if(empty($customEndpoints) || !isset($customEndpoints[$requestedPath])) {
217 return ''; // No custom endpoints defined, return early
218 }
219
220 ob_start();
221 $endpoint = $customEndpoints[$requestedPath];
222
223 add_filter('fluent_cart/customer_portal/active_tab', function ($activeTab) use ($requestedPath) {
224 return $requestedPath;
225 });
226
227 if (isset($endpoint['render_callback']) && is_callable($endpoint['render_callback'])) {
228 call_user_func($endpoint['render_callback']);
229 return ob_get_clean();
230 }
231
232 if(isset($endpoint['page_id'])) {
233 $pageId = (int) $endpoint['page_id'];
234 // Create a custom query to fetch the two pages
235 $args = array(
236 'post_type' => 'page',
237 'post__in' => [$pageId],
238 'posts_per_page' => 1,
239 'orderby' => 'post__in', // Preserve the order of the IDs
240 );
241
242 $page_query = new \WP_Query($args);
243
244 // Check if the query has posts
245 if ($page_query->have_posts()) :
246 while ($page_query->have_posts()) : $page_query->the_post();
247 ?>
248 <div class="fluent-cart-custom-page-content">
249 <div><?php the_content(); ?></div>
250 </div>
251 <?php
252 endwhile;
253 // Reset post data to avoid conflicts with other queries
254 wp_reset_postdata();
255 else :
256 echo '<p>' . esc_html__('No content found!', 'fluent-cart') . '</p>';
257 endif;
258 }
259
260 return ob_get_clean();
261 }
262
263 public function renderCustomerApp()
264 {
265 echo '<div data-fluent-cart-customer-profile-app><app/></div>';
266 }
267
268 public function renderCustomerMenu()
269 {
270 $baseUrl = TemplateService::getCustomerProfileUrl('/');
271
272 $menuItems = [
273 'dashboard' => [
274 'label' => __('Dashboard', 'fluent-cart'),
275 'css_class' => 'fct_route',
276 'link' => $baseUrl,
277 'icon_svg' => '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
278 <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"/>
279 </svg>',
280 ],
281 'purchase-history' => [
282 'label' => __('Purchase History', 'fluent-cart'),
283 'css_class' => 'fct_route',
284 'link' => $baseUrl . 'purchase-history',
285 'icon_svg' => '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
286 <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"/>
287 </svg>'
288 ],
289 'subscriptions' => [
290 'label' => __('Subscription Plans', 'fluent-cart'),
291 'css_class' => 'fct_route',
292 'link' => $baseUrl . 'subscriptions',
293 'icon_svg' => '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
294 <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"/>
295 </svg>'
296 ],
297 'licenses' => [
298 'label' => __('Licenses', 'fluent-cart'),
299 'css_class' => 'fct_route',
300 'link' => $baseUrl . 'licenses',
301 'icon_svg' => '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
302 <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"/>
303 </svg>'
304 ],
305 'downloads' => [
306 'label' => __('Downloads', 'fluent-cart'),
307 'css_class' => 'fct_route',
308 'link' => $baseUrl . 'downloads',
309 'icon_svg' => '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
310 <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"/>
311 </svg>'
312 ],
313 'reviews' => [
314 'label' => __('My Reviews', 'fluent-cart'),
315 'css_class' => 'fct_route',
316 'link' => $baseUrl . 'reviews',
317 'icon_svg' => '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
318 <path d="M10 1.875L12.4635 6.86713L17.9727 7.66771L13.9863 11.5533L14.9271 17.0398L10 14.4488L5.07295 17.0398L6.01368 11.5533L2.02734 7.66771L7.53647 6.86713L10 1.875ZM10 5.26443L8.53252 8.23787L5.25123 8.71472L7.62536 11.0289L7.06498 14.2963L10 12.7534L12.935 14.2963L12.3746 11.0289L14.7488 8.71472L11.4675 8.23787L10 5.26443Z" fill="currentColor"/>
319 </svg>'
320 ],
321 'profile' => [
322 'label' => __('Profile', 'fluent-cart'),
323 'css_class' => 'fct_route',
324 'link' => $baseUrl . 'profile',
325 'icon_svg' => '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
326 <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"/>
327 </svg>'
328 ]
329 ];
330
331 $currentCustomer = CustomerResource::getCurrentCustomer();
332
333 if (!ModuleSettings::isActive('license') || !App::isProActive()) {
334 unset($menuItems['licenses']);
335 }
336
337 if (ProductReviewService::getReviewSettings()['reviews_enabled'] !== 'yes') {
338 unset($menuItems['reviews']);
339 }
340
341
342 if($currentCustomer) {
343 $hasSubscriptions = Subscription::query()->where('customer_id', $currentCustomer->id)->exists();
344 if(!$hasSubscriptions) {
345 unset($menuItems['subscriptions']);
346 }
347 } else {
348 unset($menuItems['subscriptions']);
349 }
350
351 $menuItems = apply_filters('fluent_cart/global_customer_menu_items', $menuItems, [
352 'base_url' => $baseUrl
353 ]);
354
355 $profileData = null;
356 if($currentCustomer) {
357 $profileData = [
358 'email' => $currentCustomer->email,
359 'full_name' => $currentCustomer->full_name,
360 'photo' => $currentCustomer->photo
361 ];
362 } else if(is_user_logged_in()) {
363 $user = wp_get_current_user();
364 $profileData = [
365 'email' => $user->user_email,
366 'full_name' => $user->display_name,
367 'photo' => Helper::getUserAvatarUrl($user->ID, $user->user_email)
368 ];
369 }
370
371 add_filter('fct_allowed_svg_tags', function ($tags) {
372 return [
373 'svg' => [
374 'xmlns' => true,
375 'width' => true,
376 'height' => true,
377 'viewBox' => true,
378 'fill' => true,
379 'stroke' => true,
380 'stroke-width' => true,
381 'class' => true
382 ],
383 'path' => [
384 'd' => true,
385 'fill' => true,
386 'stroke' => true,
387 'stroke-width' => true
388 ],
389 'g' => [
390 'fill' => true,
391 'stroke' => true
392 ]
393 ];
394 });
395
396
397 App::make('view')->render('frontend.customer_menu', [
398 'menuItems' => $menuItems,
399 'profileData' => $profileData,
400 ]);
401 }
402
403 public static function getLocalizationData($attributes = []):array
404 {
405
406 $currentCustomer = CustomerResource::getCurrentCustomer();
407 $customerEmail = $currentCustomer ? $currentCustomer->email : '';
408
409
410 $pageUrl = TemplateService::getCustomerProfileUrl();
411
412 $pageSlug = trim(str_replace(home_url('/'), '', $pageUrl), '/');
413 $dashboardSlug = (new StoreSettings())->getCustomerDashboardPageSlug();
414
415 if($pageSlug !== $dashboardSlug) {
416 // we should resave the page slug from settings
417 $prevSettings = (new StoreSettings())->get();
418 (new StoreSettings())->save($prevSettings);
419 }
420
421 $shopLocalizationData = Helper::shopConfig();
422 $shopLocalizationData['shop_url'] = (new StoreSettings())->getShopPage();
423
424
425 // For supporting subdirectory installations
426 $domainParts = parse_url(home_url('/'));
427 // if we have path in domain url, we need to set it as base path
428 $basePath = trim(Arr::get($domainParts, 'path', ''), '/');
429 if($basePath && $basePath !== '/') {
430 $pageSlug = $basePath.'/'.$pageSlug;
431 }
432
433 return [
434 'fluentcart_customer_profile_vars' => [
435 'app_slug' => $pageSlug,
436 'app_url' => TemplateService::getCustomerProfileUrl(),
437 'shop' => $shopLocalizationData,
438 'currency_signs' => array_map(function ($sign) {
439 return html_entity_decode($sign, ENT_QUOTES, 'UTF-8');
440 }, CurrenciesHelper::getCurrencySigns()),
441 'trans' => TransStrings::getCustomerProfileString(),
442 'download_url_base' => site_url('fluent-cart/download-file/?fluent_cart_download=true'),
443 'placeholder_image' => Helper::getProductPlaceholderUrl(),
444 'reviews_enabled' => ProductReviewService::getReviewSettings()['reviews_enabled'] === 'yes',
445 'stripe_pub_key' => apply_filters('fluent_cart/payment_methods/stripe_pub_key', ''),
446 'paypal_client_id' => apply_filters('fluent_cart/payment_methods/paypal_client_id', '', []),
447 'assets_path' => Vite::getAssetUrl(),
448 'rest' => Helper::getRestInfo(),
449 'customer_email' => $customerEmail,
450 'wp_page_title' => get_the_title(),
451 'payment_methods' => PaymentMethods::getActiveMeta(),
452 'site_url' => site_url(),
453 'me' => [
454 'email' => $currentCustomer ? $currentCustomer->email : '',
455 'first_name' => $currentCustomer ? $currentCustomer->first_name : '',
456 'last_name' => $currentCustomer ? $currentCustomer->last_name : '',
457 ],
458 'logout_url' => wp_logout_url(home_url()),
459 'datei18' => DayjsFormatter::localizedStrings(),
460 'el_strings' => TransStrings::elStrings(),
461 'wp_locale' => get_locale(),
462 'is_company_name_enabled' => CheckoutFieldsSchema::isCompanyNameEnabled(),
463 'is_company_name_required' => CheckoutFieldsSchema::isCompanyNameRequired(),
464 'is_vat_number_enabled' => CheckoutFieldsSchema::isVatNumberEnabled(),
465 'is_vat_number_required' => CheckoutFieldsSchema::isVatNumberRequired(),
466 'is_legal_registration_id_enabled' => CheckoutFieldsSchema::isLegalRegistrationIdEnabled(),
467 'is_legal_registration_id_required' => CheckoutFieldsSchema::isLegalRegistrationIdRequired()
468 ],
469 'fluentCartRestVars' => [
470 'rest' => Helper::getRestInfo(),
471 ],
472 ];
473 }
474
475 public function localizeData(): array
476 {
477 return static::getLocalizationData($this->shortCodeAttributes);
478 }
479
480 private static function generateCssColorVariables($colors): string
481 {
482 $cssVariables = '';
483
484 if (!empty($colors)) {
485 // Split the colors string by commas to separate each key-value pair
486 $pairs = explode(',', $colors);
487 $colorVariables = [];
488
489 foreach ($pairs as $pair) {
490 list($key, $value) = explode('=', trim($pair));
491
492 // Only add to the array if the value is not empty or just a semicolon
493 if (!empty($value) && $value !== ';') {
494 $colorVariables[] = "$key: $value;";
495 }
496 }
497
498 $cssVariables = implode("\n", $colorVariables);
499 }
500
501 return $cssVariables;
502 }
503
504 }
505