PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.3.28
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.3.28
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 / MenuHandler.php

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

579 lines 23.6 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;
4
5 use FluentCart\Api\ModuleSettings;
6 use FluentCart\App\App;
7 use FluentCart\App\Services\Renderer\CheckoutFieldsSchema;
8 use FluentCart\App\Vite;
9 use FluentCart\App\Services\URL;
10 use FluentCart\Api\StoreSettings;
11 use FluentCart\Api\PaymentMethods;
12 use FluentCart\App\Helpers\Helper;
13 use FluentCart\App\Helpers\Status;
14 use FluentCart\App\Models\Product;
15 use FluentCart\App\Models\Customer;
16 use FluentCart\Framework\Support\Arr;
17 use FluentCart\App\CPT\FluentProducts;
18 use FluentCart\App\Helpers\AdminHelper;
19 use FluentCart\App\Models\AttributeTerm;
20 use FluentCart\App\Models\AttributeGroup;
21 use FluentCart\App\Models\ShippingMethod;
22 use FluentCart\App\Modules\Tax\TaxModule;
23 use FluentCart\App\Helpers\CurrenciesHelper;
24 use FluentCart\App\Services\Filter\TaxFilter;
25 use FluentCart\App\Services\Filter\OrderFilter;
26 use FluentCart\App\Services\Filter\LicenseFilter;
27 use FluentCart\App\Services\Filter\LicenseSiteFilter;
28 use FluentCart\App\Services\Filter\ProductFilter;
29 use FluentCart\App\Services\Filter\CustomerFilter;
30 use FluentCart\App\Modules\Integrations\AddOnModule;
31 use FluentCart\App\Services\Translations\TransStrings;
32 use FluentCart\App\Services\Permission\PermissionManager;
33 use FluentCart\App\Modules\PaymentMethods\Core\GatewayManager;
34
35 class MenuHandler
36 {
37 public function register()
38 {
39 add_action('init', [$this, 'init']);
40
41 add_action('admin_init', function () {
42 $page = $_GET['page'] ?? '';
43
44 if ($page == 'fluent-cart') {
45
46 \FluentCart\App\Events\FirstTimePluginActivation::handle();
47 add_action('admin_enqueue_scripts', function () {
48 Vite::enqueueStyle('fluent_cart_admin_app_css',
49 'styles/tailwind/style.css',
50 );
51 Vite::enqueueScript('fluent_cart_admin_global_js',
52 'admin/global.js',
53 );
54 });
55
56 }
57 });
58
59 // Add a post display state for special WC pages.
60 add_filter('display_post_states', array($this, 'addDisplayPostStates'), 10, 2);
61
62 }
63
64
65 public function addDisplayPostStates($states, $post)
66 {
67 if (!$post || $post->post_type != 'page') {
68 return $states;
69 }
70
71
72 $targetKeys = [
73 'checkout_page_id' => __('Checkout Page', 'fluent-cart'),
74 'cart_page_id' => __('Cart Page', 'fluent-cart'),
75 'receipt_page_id' => __('Receipt Page', 'fluent-cart'),
76 'shop_page_id' => __('Shop Page', 'fluent-cart'),
77 'customer_profile_page_id' => __('Customer Profile Page', 'fluent-cart'),
78 ];
79
80 $pagesConfig = (new StoreSettings())->getPagesSettings();
81 $pagesConfig = Arr::only($pagesConfig, array_keys($targetKeys));
82
83 if (!is_array($pagesConfig)) {
84 return $states;
85 }
86 $pagesConfig = array_filter($pagesConfig);
87
88 if (empty($pagesConfig)) {
89 return $states;
90 }
91
92
93 $pagesConfig = array_flip($pagesConfig);
94 $targetKey = Arr::get($pagesConfig, $post->ID);
95
96 if ($targetKey && isset($targetKeys[$targetKey])) {
97
98 $logo = '<svg style="vertical-align: middle; height: 18px; width: 18px;" width="18" height="18" viewBox="0 0 300 300" fill="none" xmlns="http://www.w3.org/2000/svg">
99 <rect width="300" height="300" rx="30" fill="white"/>
100 <path d="M136.561 205.944H47.1367L61.1704 173.491C65.2906 163.963 74.6784 157.795 85.0589 157.795H191.584L184.338 174.551C176.098 193.607 157.322 205.944 136.561 205.944Z" fill="#00009F"/>
101 <path d="M210.643 142.439H84.8574L92.1035 125.683C100.344 106.627 119.12 94.2905 139.881 94.2905H248.565L234.531 126.743C230.411 136.271 221.023 142.439 210.643 142.439Z" fill="#00009F"/>
102 </svg>';
103
104 $states['fluent_cart'] = '<span>' . $logo . '<span>' . $targetKeys[$targetKey] . '</span></span>';
105 }
106
107 return $states;
108
109 }
110
111 public function init()
112 {
113
114 add_action('admin_menu', array($this, 'addAdminMenu'));
115
116 /*
117 * Disable Gutenberg for Products
118 */
119 add_filter('parent_file', function ($parent_file) {
120 global $submenu_file, $current_screen, $pagenow;
121
122 $postType = $_GET['post_type'] ?? '';
123 if ($postType == FluentProducts::CPT_NAME && $current_screen->post_type == FluentProducts::CPT_NAME) {
124 if ($pagenow == 'post.php') {
125 $submenu_file = 'edit.php?post_type=' . $current_screen->post_type;
126 }
127
128 if ($pagenow == 'edit-tags.php') {
129 $taxonomy = $_GET['taxonomy'] ?? '';
130 $submenu_file = 'edit-tags.php?taxonomy=' . esc_attr($taxonomy) . '&post_type=' . $current_screen->post_type;
131 }
132
133 $parent_file = 'fluent-cart';
134 }
135
136 return $parent_file;
137 });
138
139 add_action('fluent_cart/admin_menu', array($this, 'renderAdminMenu'));
140 // add_action('in_admin_header', array($this, 'maybeRenderAdminMenu'));
141
142 add_action('edit_form_top', array($this, 'pushProductNav'));
143
144 add_action('admin_bar_menu', [$this, 'globalEnqueueAssets'], 10, 1);
145 }
146
147 public function addAdminMenu()
148 {
149 $capability = 'manage_options';
150
151 if (!current_user_can('manage_options') && App::isProActive()) {
152 $capability = PermissionManager::ADMIN_CAP;
153 }
154
155 if (!current_user_can($capability)) {
156 return;
157 }
158
159 global $submenu;
160
161 $adminMenuTitle = apply_filters('fluent_cart/admin_menu_title', 'FluentCart', []);
162
163 add_menu_page(
164 $adminMenuTitle,
165 $adminMenuTitle,
166 $capability,
167 'fluent-cart',
168 [$this, 'renderAdmin'],
169 $this->getMenuIcon(),
170 apply_filters('fluent_cart/admin_menu_position', 3)
171 );
172
173 $submenu['fluent-cart']['dashboard'] = array(
174 __('Dashboard', 'fluent-cart'),
175 $capability,
176 'admin.php?page=fluent-cart#/',
177 '',
178 'fluent_cart_dashboard'
179 );
180
181 if (PermissionManager::hasPermission(['orders/view'])) {
182 $submenu['fluent-cart']['orders'] = array(
183 __('Orders', 'fluent-cart'),
184 $capability,
185 'admin.php?page=fluent-cart#/orders',
186 '',
187 'fluent_cart_orders'
188 );
189 }
190
191 if (PermissionManager::hasPermission(['customers/view', 'customers/manage'])) {
192 $submenu['fluent-cart']['customers'] = array(
193 __('Customers', 'fluent-cart'),
194 $capability,
195 'admin.php?page=fluent-cart#/customers',
196 '',
197 'fluent_cart_customers'
198 );
199 }
200
201 if (PermissionManager::hasPermission(['products/view'])) {
202 $submenu['fluent-cart']['products'] = array(
203 __('Products', 'fluent-cart'),
204 $capability,
205 'admin.php?page=fluent-cart#/products',
206 '',
207 'fluent_cart_products'
208 );
209 }
210
211 if (PermissionManager::hasPermission(['subscriptions/view'])) {
212 $submenu['fluent-cart']['subscriptions'] = array(
213 __('Subscriptions', 'fluent-cart'),
214 $capability,
215 'admin.php?page=fluent-cart#/subscriptions',
216 '',
217 'fluent_cart_subscriptions'
218 );
219 }
220
221 if (PermissionManager::hasPermission(['reports/view'])) {
222 $submenu['fluent-cart']['reports'] = array(
223 __('Reports', 'fluent-cart'),
224 $capability,
225 'admin.php?page=fluent-cart#/reports/overview',
226 '',
227 'fluent_cart_reports'
228 );
229 }
230
231 if (PermissionManager::userCan('is_super_admin')) {
232 $submenu['fluent-cart']['integrations'] = array(
233 __('Integrations', 'fluent-cart'),
234 $capability,
235 'admin.php?page=fluent-cart#/integrations',
236 '',
237 'fluent_cart_integrations'
238 );
239 }
240
241
242 $hasSensitiveAccess = PermissionManager::hasPermission(['store/settings', 'store/sensitive']);
243
244 if ($hasSensitiveAccess) {
245 $submenu['fluent-cart']['settings'] = array(
246 __('Settings', 'fluent-cart'),
247 $capability,
248 'admin.php?page=fluent-cart#/settings/store-settings/',
249 '',
250 'fluent_cart_settings'
251 );
252 }
253
254 if (PermissionManager::hasPermission(['coupons/manage', 'coupons/view'])) {
255 $submenu['fluent-cart']['coupons'] = array(
256 __('Coupons', 'fluent-cart'),
257 $capability,
258 'admin.php?page=fluent-cart#/coupons',
259 '',
260 'fluent_cart_coupons'
261 );
262 }
263
264 if ($hasSensitiveAccess && TaxModule::isTaxEnabled()) {
265 $submenu['fluent-cart']['taxes'] = array(
266 __('Taxes', 'fluent-cart'),
267 $capability,
268 'admin.php?page=fluent-cart#/taxes',
269 '',
270 'fluent_cart_taxes'
271 );
272 }
273
274
275 if (PermissionManager::hasPermission(['products/create', 'products/edit'])) {
276 $taxonomies = get_taxonomies([
277 'object_type' => [FluentProducts::CPT_NAME],
278 'public' => true,
279 ], 'object');
280
281 foreach ($taxonomies as $key => $taxonomy) {
282 $menuUrl = 'edit-tags.php?taxonomy=' . $taxonomy->name . '&post_type=' . FluentProducts::CPT_NAME;
283 $submenu['fluent-cart'][$key] = [
284 $taxonomy->label,
285 $capability,
286 $menuUrl,
287 '',
288 $taxonomy->name
289 ];
290 }
291 }
292
293 if (PermissionManager::userCan('is_super_admin')) {
294 $submenu['fluent-cart']['logs'] = array(
295 __('Logs', 'fluent-cart'),
296 $capability,
297 'admin.php?page=fluent-cart#/logs',
298 '',
299 'fluent_cart_logs'
300 );
301 }
302
303 do_action('fluent_cart/admin_submenu_added', $submenu);
304 }
305
306 public function renderAdmin()
307 {
308 $isCustomDashboardSlugChanged = (new StoreSettings())->getCustomerDashboardPageSlug() != (new StoreSettings())->get('customer_profile_page_slug');
309
310 if ($isCustomDashboardSlugChanged) {
311 $allSettings = (new StoreSettings())->get();
312 (new StoreSettings())->save($allSettings); // we are just resaving to flash the settings cache
313 }
314
315 if (function_exists('wp_enqueue_editor')) {
316 wp_enqueue_editor();
317 wp_tinymce_inline_scripts();
318 wp_enqueue_media();
319 }
320
321 $this->enqueueAssets();
322
323 $config = App::getInstance('config');
324
325 $name = $config->get('app.name');
326
327 $slug = $config->get('app.slug');
328
329 $baseUrl = apply_filters('fluent_connector_base_url', admin_url('admin.php?page=newproject#/'));
330
331 $app = App::getInstance();
332 $assets = $app['url.assets'];
333
334 App::make('view')->render('admin.admin_app', [
335 'name' => $name,
336 'slug' => $slug,
337 'baseUrl' => $baseUrl,
338 'logo' => $assets . 'images/logo.svg',
339 ]);
340 }
341
342 public function maybeRenderAdminMenu()
343 {
344 $postType = $_GET['post_type'] ?? '';
345 if ($postType == FluentProducts::CPT_NAME && is_admin()) {
346 AdminHelper::getAdminMenu(true);
347 AdminHelper::pushGlobalAdminAssets();
348 }
349 }
350
351 public function renderAdminMenu()
352 {
353 if (!apply_filters('fluent_cart/show_admin_top_bar', true)) {
354 return;
355 }
356 AdminHelper::getAdminMenu(true);
357 }
358
359 public function pushProductNav($post)
360 {
361 if (!$post || $post->post_type != FluentProducts::CPT_NAME) {
362 return;
363 }
364
365 AdminHelper::getProductMenu($post->ID, true, 'product_edit');
366 AdminHelper::pushGlobalAdminAssets();
367 }
368
369 public function enqueueAssets()
370 {
371 $app = App::getInstance();
372
373 $slug = $app->config->get('app.slug');
374
375 do_action('fluent_cart/loading_app', $app);
376
377 //This should be done before enqueueing the global script.
378 Vite::enqueueScript($slug . '_admin_app_start', 'admin/bootstrap/app.js', [$slug . '_global_admin_hooks']);
379
380 //Don't register this script using vite.
381 wp_enqueue_script($slug . '_global_admin_hooks', Vite::getEnqueuePath('admin/admin_hooks.js'), [], FLUENTCART_VERSION, true);
382
383 $manager = GatewayManager::getInstance();
384 $payment_routes = $manager->getRoutes();
385 // $payment_routes = apply_filters('fluent_cart/payments/payment_method_settings_routes', []);
386
387 $storage_driver_routes = apply_filters('fluent_cart/storage/storage_driver_settings_routes', [], []);
388
389 $settings = new StoreSettings();
390 $checkoutUrl = add_query_arg(Helper::INSTANT_CHECKOUT_URL_PARAM, '=', $settings->getCheckoutPage());
391 $restVars = Helper::getRestInfo();
392
393 $filterOptions = [
394 'order_filter_options' => OrderFilter::getTableFilterOptions(),
395 'customer_filter_options' => CustomerFilter::getTableFilterOptions(),
396 'product_filter_options' => ProductFilter::getTableFilterOptions(),
397 'license_filter_options' => LicenseFilter::getTableFilterOptions(),
398 'license_site_filter_options' => LicenseSiteFilter::getTableFilterOptions(),
399 'tax_filter_options' => TaxFilter::getTableFilterOptions(),
400 ];
401 $filterOptions = apply_filters('fluent_cart/admin_filter_options', $filterOptions, []);
402
403 // Structured table config keyed by table name (used by Table.js base class)
404 $tableConfig = [
405 'order_table' => ['filters' => Arr::get($filterOptions, 'order_filter_options', [])],
406 'customers' => ['filters' => Arr::get($filterOptions, 'customer_filter_options', [])],
407 'product_table' => ['filters' => Arr::get($filterOptions, 'product_filter_options', [])],
408 'licenses' => ['filters' => Arr::get($filterOptions, 'license_filter_options', [])],
409 'license_sites' => ['filters' => Arr::get($filterOptions, 'license_site_filter_options', [])],
410 'taxes_table' => ['filters' => Arr::get($filterOptions, 'tax_filter_options', [])],
411 'subscriptions' => ['filters' => Arr::get($filterOptions, 'subscription_filter_options', [])],
412 'shipping_zone_table' => ['filters' => Arr::get($filterOptions, 'shipping_zone_filter_options', [])],
413 ];
414
415 $tableConfig = apply_filters('fluent_cart/admin_table_saved_views', $tableConfig, [
416 'filterOptions' => $filterOptions
417 ]);
418
419 $currentUser = get_user_by('ID', get_current_user_id());
420
421 // Get app config and merge additional properties into it
422 $appConfig = $app->config->get('app');
423 $appConfig['version'] = FLUENTCART_VERSION;
424 $appConfig['permissions'] = PermissionManager::getUserPermissions();
425 $appConfig['isProActive'] = App::isProActive();
426 $appConfig['proVersion'] = defined('FLUENTCART_PRO_PLUGIN_VERSION') ? FLUENTCART_PRO_PLUGIN_VERSION : null;
427
428 $appConfig['logos'] = [
429 'dark' => Vite::getAssetUrl('images/logo/logo-full-dark.svg'),
430 'light' => Vite::getAssetUrl('images/logo/logo-full.svg'),
431 ];
432 $appConfig['isModuleTabEnabled'] = $settings->isModuleTabEnabled();
433 $appConfig['upgrade_url'] = 'https://fluentcart.com/pricing/';
434
435 $max_upload_size = wp_max_upload_size(); // Returns size in bytes
436 $adminLocalizeData = apply_filters('fluent_cart/admin_app_data', [
437 'app_config' => $appConfig,
438 'slug' => $app->config->get('app.slug'),
439 'admin_url' => admin_url('admin.php?page=fluent-cart#/'),
440 'frontend_url' => URL::getFrontEndUrl(''),
441 'nonce' => wp_create_nonce($slug),
442 'rest' => $restVars,
443 'brand_logo' => $this->getMenuIcon(),
444 'asset_url' => Vite::getAssetUrl(),
445 'me' => [
446 'id' => $currentUser->ID,
447 'full_name' => trim($currentUser->display_name),
448 'email' => $currentUser->user_email,
449 'avatar' => get_avatar_url($currentUser->ID)
450 ],
451 'shop' => Helper::shopConfig(),
452 'product_statuses' => Status::getProductStatuses(),
453 'total_products_count' => Product::query()->count(),
454 'payment_statuses' => Status::getEditableTransactionStatuses(true),
455 'payment_routes' => $payment_routes,
456 'storage_driver_routes' => $storage_driver_routes,
457 'editable_payment_statuses' => Status::getEditableTransactionStatuses(true),
458 'order_statuses' => Status::getOrderStatuses(),
459 'editable_order_statuses' => Status::getEditableOrderStatuses(),
460 'editable_customer_statuses' => Status::getEditableCustomerStatuses(),
461 // Backward compat: keep the old typo'd keys for existing frontend code
462 'order_statues' => Status::getOrderStatuses(),
463 'editable_order_statues' => Status::getEditableOrderStatuses(),
464 'editable_customer_statues' => Status::getEditableCustomerStatuses(),
465 'shipping_statuses' => Status::getShippingStatuses(),
466 'allow_bulk_payment_status_change' => true,
467 'variation_attributes' => AttributeGroup::with(['terms'])->get(),
468 'variation_terms' => AttributeTerm::query()->get()->keyBy('id'),
469 'product_image_base_uri' => Helper::getProductImageBaseUri(),
470 'add_on_modules' => AddOnModule::showAddOns(),
471 'stock_statuses' => Status::getStockStatuses(),
472 'fulfillment_types' => Helper::getFulfilmentTypes(),
473 'variation_types' => Helper::getVariationTypes(),
474 'trans' => TransStrings::getStrings(),
475 'dashboard_url' => admin_url('admin.php?page=fluent-cart#'),
476 'checkout_url' => $checkoutUrl,
477 'dummy_product_info' => apply_filters('fluent_cart/dummy_product_info', []) ?: [],
478 'currency_signs' => CurrenciesHelper::getCurrencySigns(),
479 'filter_options' => $filterOptions,
480 'table_config' => $tableConfig,
481 'receipt_page_url' => $settings->getReceiptPage(),
482 'payment_logos' => PaymentMethods::getIcons(),
483 'is_admin_bar_showing' => is_admin_bar_showing(),
484 'max_upload_size' => size_format($max_upload_size),
485 'available_shipping_method_count' => ShippingMethod::query()->where('is_enabled', '1')->count(),
486 'is_tax_enabled' => TaxModule::isTaxEnabled(),
487 'eu_vat_county_options' => TaxModule::euVatCountyOptions(),
488 'country_tax_titles' => TaxModule::taxTitleLists(),
489 'site_url' => site_url(),
490 'modules_settings' => ModuleSettings::getAllSettings(),
491 'purchase_fluent_cart_link' => 'https://fluentcart.com/',
492 'admin_notices' => apply_filters('fluent_cart/admin_notices', []),
493 'subscription_intervals' => Helper::getAvailableSubscriptionIntervalOptions(),
494
495 'datei18' => TransStrings::dateTimeStrings(),
496 'el_strings' => TransStrings::elStrings(),
497 'wp_locale' => get_locale(),
498 'is_full_name_required' => CheckoutFieldsSchema::isFullNameRequired(),
499 'fct_editor_frame' => '',
500 ]);
501
502 wp_localize_script($slug . '_admin_app_start', 'fluentCartAdminApp', $adminLocalizeData);
503
504 wp_localize_script($slug . '_admin_app_start', 'fluentCartRestVars', [
505 'rest' => $restVars,
506 ]);
507
508 do_action('fluent_cart/admin_js_loaded', $app);
509 }
510
511 public function globalEnqueueAssets($adminBar)
512 {
513 if (function_exists('get_current_screen')) {
514 $screen = get_current_screen();
515 } else {
516 $screen = null;
517 }
518
519 if ($screen && $screen->base !== 'user-edit') {
520 return;
521 }
522
523 $app = App::getInstance();
524
525 $slug = $app->config->get('app.slug');
526 if (is_admin()) {
527 wp_enqueue_script($slug . '_edit_wp_user_admin_global_js', Vite::getEnqueuePath('admin/utils/edit-wp-user-global.js'), ['jquery'], FLUENTCART_VERSION, true);
528 }
529
530
531 $baseUrl = apply_filters('fluent_cart/admin_base_url', admin_url('admin.php?page=fluent-cart#/'), []);
532
533 $editingUserVars = null;
534
535 if (function_exists('get_current_screen')) {
536 $currentScreen = get_current_screen();
537 if ($currentScreen && $currentScreen->id == 'user-edit') {
538 $userId = (int)($_GET['user_id'] ?? 0);
539 $user = get_user_by('ID', $userId);
540
541 if ($userId && $user) {
542 $fluentcartProfile = Customer::query()->where('email', $user->user_email)
543 ->orWhere('user_id', $user->ID)
544 ->first();
545 if ($fluentcartProfile) {
546 $fluentcartProfileUrl = $baseUrl . 'customers/' . $fluentcartProfile->id . '/view';
547 $editingUserVars = [
548 'user_id' => $userId,
549 'fct_profile_id' => $fluentcartProfile->id,
550 'fct_profile_url' => $fluentcartProfileUrl
551 ];
552 }
553 }
554 }
555 }
556 wp_localize_script('fluent-cart_edit_wp_user_admin_global_js', 'fluentcart_edit_user_global_bar_vars', [
557 'customer_base' => $baseUrl . 'customers/',
558 'edit_user_vars' => $editingUserVars
559 ]);
560 }
561
562 protected function getMenuIcon(): string
563 {
564 $svg = '<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100" fill="none">
565 <g clip-path="url(#clip0_394_8994)">
566 <path d="M90 0C95.5229 0 100 4.47715 100 10V90C100 95.5229 95.5229 100 90 100H10C4.47715 100 0 95.5229 0 90V10C0 4.47715 4.47715 0 10 0H90ZM28.3525 52.5986C24.8927 52.5988 21.7641 54.6544 20.3906 57.8301L15.7119 68.6484H45.5205C52.4408 68.6484 58.6996 64.5355 61.4463 58.1836L63.8613 52.5986H28.3525ZM46.6748 31.333C39.7545 31.333 33.4959 35.4459 30.749 41.7979L28.333 47.3828H70.2617C73.7217 47.3828 76.8513 45.3264 78.2246 42.1504L82.9023 31.333H46.6748Z" fill="#9CA1A8"/>
567 </g>
568 <defs>
569 <clipPath id="clip0_394_8994">
570 <rect width="100" height="100" fill="white"/>
571 </clipPath>
572 </defs>
573 </svg>';
574
575 return 'data:image/svg+xml;base64,' . base64_encode($svg);
576 }
577
578 }
579