PluginProbe
Fluent Support – Helpdesk & Customer Support Ticket System / 2.2.0
Fluent Support – Helpdesk & Customer Support Ticket System v2.2.0
2.4.0 2.3.2 2.3.1 2.3.0 2.2.1 2.2.0 trunk 1.10.0 1.10.1 1.10.2 1.10.3 1.10.4 1.10.5 1.4.0 1.4.1 1.4.2 1.4.5 1.4.6 1.4.7 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 All 68 releases
fluent-support / app / Hooks / Handlers / Menu.php

Menu.php in Fluent Support – Helpdesk & Customer Support Ticket System 2.2.0, at app/Hooks/Handlers/Menu.php

523 lines 20.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentSupport\App\Hooks\Handlers;
4
5 use FluentSupport\App\App;
6 use FluentSupport\App\Models\Agent;
7 use FluentSupport\App\Models\AgentGroup;
8 use FluentSupport\App\Models\MailBox;
9 use FluentSupport\App\Models\Product;
10 use FluentSupport\App\Models\TagPivot;
11 use FluentSupport\App\Models\TicketTag;
12 use FluentSupport\App\Modules\PermissionManager;
13 use FluentSupport\App\Services\Helper;
14 use FluentSupport\App\Services\Notifications\NotificationSettings;
15 use FluentSupport\App\Services\TranslationStrings;
16 use FluentSupport\App\Services\Integrations\FluentBooking\FluentBookingService;
17 use FluentSupport\App\Vite;
18
19 class Menu
20 {
21 public function add()
22 {
23 $capability = PermissionManager::getMenuPermission();
24
25 if (!$capability) {
26 return;
27 }
28
29 $menuPosition = 25;
30 if (defined('FLUENTCRM')) {
31 $menuPosition = 4;
32 }
33
34 /*
35 * Filter Fluent Support menu position in WordPress dashboard
36 * @param integer $menuPosition
37 */
38 $menuPosition = apply_filters('fluent_support/admin_menu_position', $menuPosition);
39
40 add_menu_page(
41 __('Fluent Support', 'fluent-support'),
42 __('Fluent Support', 'fluent-support'),
43 $capability,
44 'fluent-support',
45 array($this, 'renderApp'),
46 $this->getMenuIcon(),
47 $menuPosition
48 );
49
50 add_submenu_page(
51 'fluent-support',
52 __('Dashboard', 'fluent-support'),
53 __('Dashboard', 'fluent-support'),
54 $capability,
55 'fluent-support',
56 array($this, 'renderApp')
57 );
58
59 add_submenu_page(
60 'fluent-support',
61 __('Tickets', 'fluent-support'),
62 __('Tickets', 'fluent-support'),
63 $capability,
64 'fluent-support#/tickets',
65 array($this, 'renderApp')
66 );
67
68 if (PermissionManager::currentUserCan('fst_view_all_reports')) {
69 add_submenu_page(
70 'fluent-support',
71 __('Reports', 'fluent-support'),
72 __('Reports', 'fluent-support'),
73 $capability,
74 'fluent-support#/reports',
75 array($this, 'renderApp')
76 );
77 }
78
79 if (PermissionManager::currentUserCan('fst_sensitive_data')) {
80 add_submenu_page(
81 'fluent-support',
82 __('Customers', 'fluent-support'),
83 __('Customers', 'fluent-support'),
84 $capability,
85 'fluent-support#/customers',
86 array($this, 'renderApp')
87 );
88 }
89
90 if (PermissionManager::currentUserCan('fst_view_activity_logs')) {
91 add_submenu_page(
92 'fluent-support',
93 __('Activities', 'fluent-support'),
94 __('Activities', 'fluent-support'),
95 $capability,
96 'fluent-support#/activity',
97 array($this, 'renderApp')
98 );
99 }
100
101 if (PermissionManager::currentUserCan('fst_manage_settings')) {
102 add_submenu_page(
103 'fluent-support',
104 __('Business Inboxes', 'fluent-support'),
105 __('Business Inboxes', 'fluent-support'),
106 $capability,
107 'fluent-support#/mailboxes',
108 array($this, 'renderApp')
109 );
110 }
111
112 if (PermissionManager::currentUserCan('fst_manage_workflows')) {
113 add_submenu_page(
114 'fluent-support',
115 __('Workflows', 'fluent-support'),
116 __('Workflows', 'fluent-support'),
117 $capability,
118 'fluent-support#/workflows',
119 array($this, 'renderApp')
120 );
121 }
122
123 if (PermissionManager::currentUserCan('fst_manage_saved_replies')) {
124 add_submenu_page(
125 'fluent-support',
126 __('Saved Replies', 'fluent-support'),
127 __('Saved Replies', 'fluent-support'),
128 $capability,
129 'fluent-support#/saved-replies',
130 array($this, 'renderApp')
131 );
132 }
133
134 if (PermissionManager::currentUserCan('fst_manage_settings')) {
135 add_submenu_page(
136 'fluent-support',
137 __('Settings', 'fluent-support'),
138 __('Settings', 'fluent-support'),
139 $capability,
140 'fluent-support#/settings',
141 array($this, 'renderApp')
142 );
143 }
144 }
145
146 public function renderApp()
147 {
148 $app = App::getInstance();
149
150 $assets = $app['url.assets'];
151
152 $baseUrl = apply_filters('fluent_support/base_url', admin_url('admin.php?page=fluent-support#/'));
153
154 $menuItems = [
155 [
156 'key' => 'dashboard',
157 'label' => __('Dashboard', 'fluent-support'),
158 'permalink' => $baseUrl
159 ],
160 [
161 'key' => 'tickets',
162 'label' => __('Tickets', 'fluent-support'),
163 'permalink' => $baseUrl . 'tickets',
164 ],
165 [
166 'key' => 'reports',
167 'label' => __('Reports', 'fluent-support'),
168 'permalink' => $baseUrl . 'reports'
169 ],
170 ];
171
172 $canManageSettings = PermissionManager::currentUserCan('fst_manage_settings');
173
174 if ($canManageSettings) {
175 $menuItems[] = [
176 'key' => 'mailboxes',
177 'label' => __('Business Inboxes', 'fluent-support'),
178 'permalink' => $baseUrl . 'mailboxes'
179 ];
180 }
181
182 if (PermissionManager::currentUserCan('fst_view_activity_logs')) {
183 $menuItems[] = [
184 'key' => 'activity',
185 'label' => __('Activities', 'fluent-support'),
186 'permalink' => $baseUrl . 'activity'
187 ];
188 }
189
190 $hasSensitiveAccess = PermissionManager::currentUserCan('fst_sensitive_data');
191 if ($hasSensitiveAccess) {
192 $menuItems[] = [
193 'key' => 'customers',
194 'label' => __('Customers', 'fluent-support'),
195 'permalink' => $baseUrl . 'customers'
196 ];
197 }
198
199 // Build the "More" dropdown children
200 $moreChildren = [];
201
202 if (PermissionManager::currentUserCan('fst_manage_saved_replies')) {
203 $moreChildren['saved_replies'] = [
204 'key' => 'saved_replies',
205 'label' => __('Saved Replies', 'fluent-support'),
206 'permalink' => $baseUrl . 'saved-replies'
207 ];
208 }
209
210 if (PermissionManager::currentUserCan('fst_manage_workflows')) {
211 $moreChildren['workflows'] = [
212 'key' => 'workflows',
213 'label' => __('Workflows', 'fluent-support'),
214 'permalink' => $baseUrl . 'workflows'
215 ];
216 }
217
218 // Add the "More" dropdown if there are children
219 if (!empty($moreChildren)) {
220 $menuItems[] = [
221 'key' => 'more',
222 'label' => __('More', 'fluent-support'),
223 'permalink' => '#',
224 'children' => $moreChildren
225 ];
226 }
227
228 $secondaryItems = [];
229
230 if ($canManageSettings) {
231 $secondaryItems[] = [
232 'key' => 'settings',
233 'label' => __('Global Settings', 'fluent-support'),
234 'permalink' => $baseUrl . 'settings'
235 ];
236 }
237
238 /*
239 * Filter Fluent Support dashboard top-left menu items
240 *
241 * @since v1.0.0
242 *
243 * @param array $menuItems
244 */
245 $menuItems = apply_filters('fluent_support/primary_menu_items', $menuItems);
246
247 /*
248 * Filter Fluent Support dashboard top-right menu items
249 *
250 * @since v1.0.0
251 *
252 * @param array $secondaryItems
253 */
254 $secondaryItems = apply_filters('fluent_support/secondary_menu_items', $secondaryItems);
255
256
257
258 if (!defined('FLUENT_SUPPORT_PRO_DIR_FILE')) {
259 $secondaryItems[] = [
260 'key' => 'upgrade_to_pro',
261 'label' => 'Upgrade to Pro',
262 'permalink' => 'https://fluentsupport.com'
263 ];
264 }
265
266 $app = App::getInstance();
267 $this->enqueueAssets();
268
269 do_action('fluent_support/admin_app_loaded', $app);
270 $app->view->render('admin.menu', [
271 'base_url' => $baseUrl,
272 'logo' => $assets . 'images/logo.svg',
273 'settingsLogo' => $assets . 'images/gear.svg',
274 'upgradeLogo' => $assets . 'images/crown.svg',
275 'assets' => $assets,
276 'menuItems' => $menuItems,
277 'secondaryItems' => isset($secondaryItems) ? $secondaryItems : [],
278 ]);
279 }
280
281 public function maybeEnqueueAssets()
282 {
283 if (isset($_GET['page']) && sanitize_text_field(wp_unslash($_GET['page'])) === 'fluent-support') {
284 add_action('admin_head', [$this, 'printDarkModeInit'], 1);
285 $this->enqueueAssets();
286 }
287 }
288
289 public function printDarkModeInit()
290 {
291 ?>
292 <script>
293 (function () {
294 var savedTheme = localStorage.getItem('fs-theme');
295
296 // Check if dark mode should be active
297 // localStorage stores: 'dark', 'light', or 'system:dark' / 'system:light'
298 var isDark = savedTheme
299 ? savedTheme.split(':').pop() === 'dark'
300 : window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
301
302 if (isDark) {
303 // Apply immediately to <html> to prevent white flash
304 document.documentElement.classList.add('fs-dark-mode');
305
306 // Watch for <body> to appear and apply class as soon as it exists
307 new MutationObserver(function (mutations, observer) {
308 if (document.body) {
309 document.body.classList.add('fs-dark-mode');
310 observer.disconnect();
311 }
312 }).observe(document.documentElement, { childList: true });
313 }
314 })();
315 </script>
316 <?php
317 }
318
319 public function enqueueAssets()
320 {
321 $app = App::getInstance();
322
323 $assets = $app['url.assets'];
324
325 // Inject Vite HMR client for dev mode
326 add_action('admin_head', function () {
327 Vite::injectViteClient();
328 }, 1);
329
330 wp_enqueue_script('dompurify', $assets . 'libs/purify/purify.min.js', [], '2.4.3');
331
332 if (is_rtl()) {
333 wp_enqueue_style('fluent_support_admin_app_rtl', $assets . 'admin/css/alpha-admin-rtl.css', [], FLUENT_SUPPORT_VERSION);
334 } else {
335 wp_enqueue_style('fluent_support_admin_app', Vite::getEnqueuePath('admin/css/alpha-admin.css'), [], FLUENT_SUPPORT_VERSION);
336 }
337
338 $agents = Agent::select(['id', 'first_name', 'last_name'])
339 ->where('person_type', 'agent')
340 ->get()->toArray();
341
342 foreach ($agents as $index => $agent) {
343 $agents[$index]['id'] = strval($agent['id']);
344 }
345
346 $agentGroups = AgentGroup::select(['id', 'title', 'settings'])->get();
347 $groupPivots = TagPivot::where('source_type', 'agent_group')->get();
348 foreach ($agentGroups as $group) {
349 $group->agent_ids = $groupPivots->where('tag_id', $group->id)
350 ->pluck('source_id')
351 ->map(function ($id) { return strval($id); })
352 ->values()
353 ->toArray();
354 }
355
356 $me = Helper::getAgentByUserId(get_current_user_id());
357
358 if (!$me && current_user_can('manage_options')) {
359 // we should create the agent
360 $user = wp_get_current_user();
361 $me = Agent::create([
362 'email' => $user->user_email,
363 'first_name' => $user->first_name,
364 'last_name' => $user->last_name,
365 'user_id' => $user->ID
366 ]);
367 }
368
369 $me->permissions = PermissionManager::currentUserPermissions();
370
371 do_action('fluent_support_loading_app', $app);
372
373 // Editor default styles.
374 add_filter('user_can_richedit', '__return_true');
375 wp_tinymce_inline_scripts();
376 wp_enqueue_editor();
377 wp_enqueue_media();
378
379 wp_enqueue_script(
380 'fluent_support_admin_app_start',
381 Vite::getEnqueuePath('admin/js/start.js'),
382 array('jquery'),
383 FLUENT_SUPPORT_VERSION,
384 true
385 );
386
387 wp_enqueue_script(
388 'fluent_support_global_admin',
389 Vite::getEnqueuePath('admin/js/global_admin.js'),
390 array('jquery'),
391 FLUENT_SUPPORT_VERSION,
392 true
393 );
394
395 $integrationDrivers = [];
396 if (!defined('FLUENTSUPPORTPRO')) {
397 $integrationDrivers = [
398 [
399 'key' => 'telegram_settings',
400 'title' => __('Telegram', 'fluent-support'),
401 'description' => __('Send Telegram notifications to Group, Channel or individual person inbox and reply from Telegram inbox', 'fluent-support'),
402 'promo_heading' => __('Get activity notification to Telegram Messenger and reply directly from Telegram inbox', 'fluent-support'),
403 'require_pro' => true
404 ],
405 [
406 'key' => 'slack_settings',
407 'title' => __('Slack', 'fluent-support'),
408 'description' => __('Send ticket activity notifications to slack', 'fluent-support'),
409 'promo_heading' => __('Get activity notification to Slack Channel and keep your support team super engaged', 'fluent-support'),
410 'require_pro' => true
411 ]
412 ];
413 }
414
415 /*
416 * Filter integration driver
417 * @param array $integrationDrivers
418 */
419 $integrationDrivers = apply_filters('fluent_support/integration_drivers', $integrationDrivers);
420
421 $tags = TicketTag::select(['id', 'title'])->get()->toArray();
422
423 $tags = array_map(function ($tag) {
424 $tag['id'] = strval($tag['id']);
425 return $tag;
426 }, $tags);
427
428 $i18ns = TranslationStrings::getAdminStrings();
429 $i18ns['allowed_files_and_size'] = Helper::getFileUploadMessage();
430
431 /*
432 * Filter agent portal localize javascript data
433 *
434 * @since v1.0.0
435 *
436 * @param array $appVars
437 */
438 $appVars = apply_filters('fluent_support_app_vars', array(
439 'slug' => $slug = $app->config->get('app.slug'),
440 'nonce' => wp_create_nonce($slug),
441 'rest' => $this->getRestInfo($app),
442 'brand_logo' => $this->getMenuIcon(),
443 'firstEntry' => '',
444 'lastEntry' => '',
445 'asset_url' => $assets,
446 'support_agents' => $agents,
447 'agent_groups' => $agentGroups,
448 'support_products' => Product::select(['id', 'title'])->get(),
449 'client_priorities' => Helper::customerTicketPriorities(),
450 'ticket_statuses' => Helper::ticketStatuses(),
451 'ticket_statuses_group' => Helper::ticketStatusGroups(),
452 'changeable_ticket_statuses' => Helper::changeableTicketStatuses(),
453 'admin_priorities' => Helper::adminTicketPriorities(),
454 'mailboxes' => MailBox::select(['id', 'name', 'settings'])->get(),
455 'me' => $me,
456 'pref' => [
457 'go_back_after_reply' => 'yes'
458 ],
459 'notification_integrations' => $integrationDrivers,
460 'internal_notification_settings' => (new NotificationSettings())->get(false),
461 'server_time' => gmdate('Y-m-d\TH:i:sP'),
462 'has_email_parser' => defined('FLUENTSUPPORTPRO_PLUGIN_VERSION'),
463 'ticket_tags' => $tags,
464 'i18n' => $i18ns,
465 'custom_fields' => apply_filters('fluent_support/ticket_custom_fields', []),
466 'has_file_upload' => !!Helper::ticketAcceptedFileMiles(),
467 'repost_export_options' => Helper::getExportOptions(),
468 'enable_draft_mode' => Helper::getBusinessSettings('enable_draft_mode', 'no'),
469 'keyboard_shortcuts' => Helper::getBusinessSettings('keyboard_shortcuts', 'no'),
470 'agent_time_tracking' => Helper::getBusinessSettings('agent_time_tracking', 'no'),
471 'max_file_upload' => Helper::getBusinessSettings('max_file_upload', 3),
472 'ajaxurl' => admin_url('admin-ajax.php'),
473 'auth_provider' => Helper::getAuthProvider(),
474 'fluent_bot_integration' => Helper::fluentBotIntegrationStatus(),
475 ));
476
477 $appVars['fluent_booking'] = (new FluentBookingService())->getStatus();
478
479 if (defined('FLUENTCRM')) {
480 $appVars['fluentcrm_config'] = Helper::getFluentCRMTagConfig();
481 }
482
483 if (defined('FLUENT_BOARDS')) {
484 $appVars['fluent_boards'] = true;
485 }
486
487 $appVars['has_pro'] = defined('FLUENTSUPPORTPRO_PLUGIN_VERSION');
488 if ($appVars['has_pro']) {
489 $appVars['agent_feedback_rating'] = Helper::getBusinessSettings('agent_feedback_rating', 'no');
490 $appVars['open_ai_integration'] = Helper::openAIIntegrationStatus();
491 }
492
493 wp_localize_script('fluent_support_admin_app_start', 'fluentSupportAdmin', $appVars);
494 }
495
496 protected function getRestInfo($app)
497 {
498 $ns = $app->config->get('app.rest_namespace');
499 $v = $app->config->get('app.rest_version');
500
501 return [
502 'base_url' => esc_url_raw(rest_url()),
503 'url' => rest_url($ns . '/' . $v),
504 'nonce' => wp_create_nonce('wp_rest'),
505 'namespace' => $ns,
506 'version' => $v,
507 ];
508 }
509
510 protected function getMenuIcon()
511 {
512 $app = App::getInstance();
513
514 $assets = $app['path.assets'];
515
516 return 'data:image/svg+xml;base64,' . base64_encode(
517 '<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
518 <path d="M90 0C95.5228 1.28852e-06 100 4.47715 100 10V90C100 95.5228 95.5228 100 90 100H10C4.47715 100 0 95.5228 0 90V10C1.28855e-06 4.47715 4.47715 0 10 0H90ZM70.2314 44.8672L70.2266 44.876H69.4941C68.7844 44.876 68.0791 44.9715 67.3965 45.1523L60.3203 47.0508L22.0703 57.3096C21.4826 57.4 20.9177 57.604 20.4023 57.9023H20.3428C18.7515 58.8292 17.7751 60.5292 17.7705 62.3691V81.9424C17.7662 82.0596 17.8564 82.1632 17.9736 82.168C18.0595 82.168 18.1409 82.1224 18.1816 82.041C19.9811 78.7134 22.5589 75.8698 25.7012 73.7539C27.0801 72.7774 28.5854 71.9902 30.1768 71.416C30.6694 71.2352 31.167 71.0821 31.6777 70.9375L34.3682 70.209L70.9092 60.4209L71.6641 60.2129C72.0348 60.1496 72.3973 60.0494 72.75 59.9229C76.7916 58.503 78.9165 54.0818 77.4971 50.04C76.4074 46.9384 73.4817 44.8626 70.1992 44.8535L70.2314 44.8672ZM77.1172 19.3086C76.8415 19.3267 76.5612 19.3583 76.29 19.417C75.9194 19.4532 75.5485 19.5217 75.1914 19.6211L21.79 33.9219C20.1217 34.365 18.7609 35.5766 18.1279 37.1816C18.1189 37.2178 18.1001 37.2588 18.082 37.2949C18.046 37.3851 18.0144 37.4798 17.9873 37.5654C17.9602 37.6513 17.915 37.7422 17.915 37.8281C17.9015 37.8733 17.8928 37.9187 17.8838 37.9639C17.8567 38.0765 17.8118 38.2653 17.8115 38.2979V38.3975C17.7889 38.5105 17.7796 38.6285 17.7705 38.7461V45.6494C17.7705 45.8348 17.7844 46.0164 17.8115 46.1973C17.8343 48.3176 19.5613 50.0217 21.6816 50.0127C21.8895 50.0127 22.0978 49.9951 22.3057 49.959C22.6085 49.9454 22.9113 49.8954 23.2051 49.8096L68.7705 37.5928C69.9958 37.2627 71.2669 37.1404 72.5283 37.2354C76.9635 37.5068 80.8557 40.2878 82.5557 44.3975V25.2637C82.5557 25.0286 82.5425 24.8066 82.5244 24.5986V24.4717C82.5242 21.619 80.2141 19.3087 77.3613 19.3086H77.1172Z" fill="#9CA1A8"/>
519 </svg>'
520 );
521 }
522 }
523