PluginProbe
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration / 2.0.1
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration v2.0.1
2.1.0 2.0.15 2.0.12 2.0.10 2.0.4 2.0.1 2.0.0 1.95.3 1.95.2 1.95 1.91.6 trunk 1.11 1.12 1.13 1.20 1.21 1.22 1.23 1.30 1.31 1.32 1.35 1.40 1.41 All 42 releases
fluent-boards / app / Hooks / Handlers / AdminMenuHandler.php

AdminMenuHandler.php in FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration 2.0.1, at app/Hooks/Handlers/AdminMenuHandler.php

595 lines 22.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentBoards\App\Hooks\Handlers;
4
5 use FluentBoards\App\App;
6 use FluentBoards\App\Models\Board;
7 use FluentBoards\App\Models\Meta;
8 use FluentBoards\App\Models\Relation;
9 use FluentBoards\App\Services\Constant;
10 use FluentBoards\App\Services\Helper;
11 use FluentBoards\App\Services\TransStrings;
12 use FluentBoards\App\Vite;
13 use FluentBoards\Framework\Support\Arr;
14 use FluentBoards\Framework\Support\Collection;
15 use FluentBoards\App\Services\PermissionManager;
16 use FluentBoards\Framework\Support\DateTime;
17
18 class AdminMenuHandler
19 {
20
21 public function register()
22 {
23 add_action('admin_menu', [$this, 'add'], 11);
24
25 add_filter('fluent_crm/core_menu_items', function ($items) {
26 if (PermissionManager::userHasAnyBoardAccess()) {
27 $items['fluent-boards'] = [
28 'key' => 'fluent-boards',
29 'label' => __('Boards', 'fluent-boards'),
30 'permalink' => admin_url('admin.php?page=fluent-boards#/')
31 ];
32 }
33 return $items;
34 });
35
36 add_action('admin_enqueue_scripts', function () {
37 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Checking admin page context for asset enqueuing, no data modification
38 if (!isset($_REQUEST['page']) || $_REQUEST['page'] !== 'fluent-boards') {
39 return;
40 }
41
42 $this->enqueueAssets();
43 });
44
45 add_filter('fluent_crm/sidebar_core_menu_items', function($menuItems, $permissions) {
46 $settings = fluent_boards_get_pref_settings();
47 if (Arr::get($settings, 'menu_settings.in_fluent_crm') === 'yes') {
48 $menuItems[] = [
49 'key' => 'fluent-boards',
50 'page_title' => __('Fluent Boards', 'fluent-boards'),
51 'menu_title' => __('Fluent Boards', 'fluent-boards'),
52 'capability' => 'manage_options',
53 'uri' => admin_url('admin.php?page=fluent-boards')
54 ];
55 }
56 return $menuItems;
57 }, 10, 2);
58 }
59
60 public function add()
61 {
62 if (!PermissionManager::userHasAnyBoardAccess()) {
63 return;
64 }
65
66 $user = get_user_by('ID', get_current_user_id());
67
68 if (current_user_can('manage_options')) {
69 $capability = 'manage_options';
70 } else {
71 $roles = array_values((array)$user->roles);
72 $capability = Arr::get($roles, 0);
73 }
74
75 $settings = fluent_boards_get_pref_settings();
76
77 if (defined('FLUENTCRM') && \FluentCrm\App\Services\PermissionManager::currentUserPermissions() && Arr::get($settings, 'menu_settings.in_fluent_crm') === 'yes') {
78 add_submenu_page(
79 'fluentcrm-admin', //$parent_slug
80 __('Fluent Boards', 'fluent-boards'), //$page_title
81 __('Fluent Boards', 'fluent-boards'), //$menu_title
82 $capability,
83 'fluent-boards', //$menu_slug
84 [$this, 'render'],
85 );
86 return;
87 }
88
89 add_menu_page(
90 __('Fluent Boards', 'fluent-boards'),
91 __('Fluent Boards', 'fluent-boards'),
92 $capability,
93 'fluent-boards',
94 [$this, 'render'],
95 $this->getMenuIcon(),
96 Arr::get($settings, 'menu_settings.menu_position', 3)
97 );
98
99 add_submenu_page(
100 'fluent-boards',
101 __('Dashboard', 'fluent-boards'),
102 __('Dashboard', 'fluent-boards'),
103 $capability,
104 'fluent-boards',
105 [$this, 'render']
106 );
107
108 add_submenu_page(
109 'fluent-boards',
110 __('Boards', 'fluent-boards'),
111 __('Boards', 'fluent-boards'),
112 $capability,
113 'fluent-boards#/boards',
114 [$this, 'render']
115 );
116
117 do_action('fluent_boards/after_core_menu_items', $permissions = [], $isAdmin = true);
118
119 add_submenu_page(
120 'fluent-boards',
121 __('Reports', 'fluent-boards'),
122 __('Reports', 'fluent-boards'),
123 $capability,
124 'fluent-boards#/reports',
125 [$this, 'render']
126 );
127
128 add_submenu_page(
129 'fluent-boards',
130 __('Settings', 'fluent-boards'),
131 __('Settings', 'fluent-boards'),
132 'manage_options',
133 'fluent-boards#/settings/general-settings',
134 [$this, 'render']
135 );
136 }
137
138 public function render()
139 {
140 $this->changeFooter();
141
142 $config = App::getInstance('config');
143
144 $name = $config->get('app.name');
145 $app = App::getInstance();
146 $assets = $app['url.assets'];
147 $slug = $config->get('app.slug');
148 $baseUrl = fluent_boards_page_url();
149
150 do_action('fluent_boards/rendering_app');
151
152 // For subtask sync
153 UpdateHandler::maybeSubtaskGroupSync();
154
155 App::make('view')->render('admin.menu', [
156 'name' => $name,
157 'slug' => $slug,
158 'menuItems' => $this->getMenuItems($app),
159 'baseUrl' => $baseUrl,
160 'logo' => apply_filters('fluent_boards/app_logo', $assets . 'images/logo.svg'),
161 'icon' => apply_filters('fluent_boards/app_icon', $assets . 'images/icon.svg'),
162 'is_new' => Board::count() == 0 ? 'yes' : 'no',
163 'is_onboarded' => $this->getOnboardingValue()
164 ]);
165 }
166
167 public function getMenuItems($app)
168 {
169 $config = $app->config;
170 $slug = $config->get('app.slug');
171
172 $baseUrl = fluent_boards_page_url();
173
174 $isDiffUrl = false;
175
176 if (is_admin()) {
177 $adminUrl = admin_url('admin.php?page=fluent-boards#/');
178
179 if ($adminUrl != $baseUrl) {
180 $isDiffUrl = true;
181 $baseUrl = $adminUrl;
182 }
183 }
184
185 $menuItems = [
186 'dashboard' => [
187 'key' => 'dashboard',
188 'label' => __('Dashboard', 'fluent-boards'),
189 'permalink' => $baseUrl,
190 ],
191 'boards' => [
192 'key' => 'boards',
193 'label' => __('Boards', 'fluent-boards'),
194 'permalink' => $baseUrl . 'boards'
195 ]
196 ];
197
198 $menuItems = apply_filters('fluent_boards/core_menu_items', $menuItems);
199
200 $menuItems['reports'] = [
201 'key' => 'reports',
202 'label' => __('Reports', 'fluent-boards'),
203 'permalink' => $baseUrl . 'reports'
204 ];
205
206 $isAdmin = PermissionManager::isAdmin();
207
208 if ($isAdmin) {
209 $menuItems['help'] = [
210 'key' => 'help',
211 'label' => __('Community', 'fluent-boards'),
212 'target' => '_blank',
213 'permalink' => 'https://community.wpmanageninja.com/portal/community/fluent-boards/home'
214 ];
215 }
216
217 if ($isDiffUrl) {
218 $menuItems['front'] = [
219 'key' => 'front',
220 'label' => __('Front Portal', 'fluent-boards'),
221 'target' => '_blank',
222 'permalink' => fluent_boards_page_url()
223 ];
224 }
225
226 $menuItems = apply_filters('fluent_boards/menu_items', $menuItems);
227
228 return array_values($menuItems);
229 }
230
231 public function enqueueAssets()
232 {
233 if (!PermissionManager::hasAppAccess()) {
234 return;
235 }
236
237 add_action('wp_print_scripts', function () {
238
239 $isSkip = apply_filters('fluent_boards/skip_no_conflict', false);
240
241 if ($isSkip) {
242 return;
243 }
244
245 global $wp_scripts;
246 if (!$wp_scripts) {
247 return;
248 }
249
250 $approvedSlugs = apply_filters('fluent_boards/asset_listed_slugs', [
251 '\/fluent-crm\/'
252 ]);
253
254 $approvedSlugs[] = '\/fluent-boards\/';
255
256 $approvedSlugs = array_unique($approvedSlugs);
257
258 $approvedSlugs = implode('|', $approvedSlugs);
259
260 $pluginUrl = plugins_url();
261
262 $pluginUrl = str_replace(['http:', 'https:'], '', $pluginUrl);
263
264 foreach ($wp_scripts->queue as $script) {
265 if (empty($wp_scripts->registered[$script]) || empty($wp_scripts->registered[$script]->src)) {
266 continue;
267 }
268
269 $src = $wp_scripts->registered[$script]->src;
270 $isMatched = (strpos($src, $pluginUrl) !== false) && !preg_match('/' . $approvedSlugs . '/', $src);
271 if (!$isMatched) {
272 continue;
273 }
274 wp_dequeue_script($wp_scripts->registered[$script]->handle);
275 }
276 });
277
278 if (function_exists('wp_enqueue_media')) {
279 // Editor default styles.
280 add_filter('user_can_richedit', '__return_true');
281 if (is_admin()) {
282 wp_tinymce_inline_scripts();
283 }
284 wp_enqueue_editor();
285 wp_enqueue_script('thickbox');
286 wp_enqueue_script('editor');
287 }
288 if (function_exists('wp_enqueue_media')) {
289 wp_enqueue_media();
290 }
291
292 $app = App::getInstance();
293
294 $slug = $app->config->get('app.slug');
295
296 // Inject Vite HMR client in dev mode
297 Vite::injectViteClient();
298
299 $isRtl = is_rtl();
300 $adminAppCss = $isRtl ? 'scss/admin.scss' : 'scss/admin.scss';
301 Vite::enqueueStyle($slug . '_admin_app', $adminAppCss);
302
303 // In production, load the RTL version if needed
304 if ($isRtl && !Vite::underDevelopment()) {
305 $assets = $app['url.assets'];
306 wp_enqueue_style(
307 $slug . '_admin_app_rtl',
308 $assets . 'admin/admin-rtl.css',
309 [$slug . '_admin_app'],
310 FLUENT_BOARDS_PLUGIN_VERSION
311 );
312 }
313
314 // Enqueue merged chunk CSS (Vue SFC styles) in production
315 if (!Vite::underDevelopment()) {
316 $assets = $app['url.assets'];
317 $styleCssPath = FLUENT_BOARDS_PLUGIN_PATH . 'assets/admin/style.css';
318 if (file_exists($styleCssPath)) {
319 wp_enqueue_style(
320 $slug . '_vite_style',
321 $assets . 'admin/style.css',
322 [$slug . '_admin_app'],
323 FLUENT_BOARDS_PLUGIN_VERSION
324 );
325 }
326 }
327
328 do_action('fluent-boards_loading_app');
329
330 Vite::enqueueScript(
331 $slug . '_admin_app',
332 'admin/app.js',
333 ['jquery'],
334 FLUENT_BOARDS_PLUGIN_VERSION,
335 true
336 );
337
338 Vite::enqueueStaticScript(
339 $slug . '_global_admin',
340 'admin/global_admin.js',
341 [],
342 FLUENT_BOARDS_PLUGIN_VERSION,
343 true
344 );
345 /*
346 * This script only for resolve the conflict of lodash and underscore js
347 * Resolved the issue of media uploader specially for image upload
348 */
349 wp_add_inline_script($slug . '_global_admin', $this->getInlineScript(), 'after');
350
351 wp_localize_script($slug . '_admin_app', 'fluentAddonVars', $this->getAddonVars($app));
352
353 do_action('fluent_boards/after_enqueue_assets', $app);
354 }
355
356 public function getAddonVars($app)
357 {
358 $currentUser = get_user_by('ID', get_current_user_id());
359 $assets = $app['url.assets'];
360 $roleAndPermissions = $this->getRoleAndPermissions($currentUser->ID);
361 $onboardingValue = $this->getOnboardingValue();
362 $generalSettings = fluent_boards_get_option('general_settings', []);
363
364 return apply_filters('fluent_boards/app_vars', [
365 'slug' => $slug = $app->config->get('app.slug'),
366 'nonce' => wp_create_nonce($slug),
367 'rest' => $this->getRestInfo($app),
368 'fluent_boards_file_upload_nonce' => wp_create_nonce('fluent_boards_file_upload_nonce'),
369 'ajaxurl' => admin_url('admin-ajax.php'),
370 'file_upload_limit' => $this->fileUploadLimit(),
371 'brand_logo' => $this->getMenuIcon(),
372 'business_name' => sanitize_text_field(Arr::get($generalSettings, 'business_name', '')),
373 'asset_url' => $assets,
374 'admin_url' => admin_url('admin.php'),
375 'fluent_crm_exists' => !defined('FLUENTCRM') ? false : true,
376 'fluent_support_exists' => !!defined('FLUENT_SUPPORT_VERSION'),
377 'can_manage_crm_contacts' => $this->canManageCrmContacts(),
378 'fluent_roadmap_exists' => !defined('FLUENT_ROADMAP') ? false : true,
379 'has_pro' => !!defined('FLUENT_BOARDS_PRO_VERSION'),
380 'upgrade_url' => fluent_boards_get_upgrade_url(),
381 'board_solid_colors' => Constant::BOARD_BACKGROUND_DEFAULT_SOLID_COLORS,
382 'board_gradient_colors' => Constant::BOARD_BACKGROUND_DEFAULT_GRADIENT_COLORS,
383 'ai_features' => [
384 'enabled' => (new \FluentBoards\App\Services\AiService())->isReady(),
385 ],
386 'me' => [
387 'id' => $currentUser->ID,
388 'full_name' => trim($currentUser->first_name . ' ' . $currentUser->last_name),
389 'display_name' => $currentUser->display_name,
390 'email' => $currentUser->user_email,
391 'photo' => fluent_boards_user_avatar($currentUser->user_email, $currentUser->display_name),
392 'logout_url' => wp_logout_url(),
393 'fluent_boards_role' => $roleAndPermissions['role'],
394 'fluent_boards_capabilities' => $roleAndPermissions['permissions'],
395 'is_wp_admin' => user_can($currentUser->ID, 'manage_options') ? 'yes' : 'no',
396 'can_create_board' => PermissionManager::userHasBoardCreationPermission($currentUser->ID)
397 ],
398 'base_url' => fluent_boards_page_url(),
399 'site_url' => site_url('/'),
400 // 'server_time' => (new DateTime('now'))->format('Y-m-d H:i:s P'), // Server's default timezone
401 'server_time' => (new DateTime('now', wp_timezone()))->format('Y-m-d H:i:s P'), // wordpress site time
402 'server_time_zone' => (new DateTime('now', wp_timezone()))->format( 'P'),
403 'utc_offset' => current_time('timestamp') - strtotime(gmdate('Y-m-d H:i:s')),
404 'trans' => TransStrings::getStrings(),
405 'is_new' => Board::count() == 0 ? 'yes' : 'no',
406 'is_onboarded' => $onboardingValue,
407 'render_in' => is_admin() ? 'admin' : 'front',
408 'dashboard_notices' => apply_filters('fluent_boards/dashboard_notices', []),
409 'is_beta' => defined('FLUENT_BOARDS_PRO_VERSION') && !defined('FLUENT_BOARDS_PRO_LIVE'),
410 'advanced_modules' => fluent_boards_get_pref_settings(),
411 'crm_base_url' => defined('FLUENTCRM') ? fluentcrm_menu_url_base() : '',
412 'start_of_week' => intval(get_option('start_of_week', 0)),
413 'time_format' => get_option('time_format', 'g:i'),
414 'priorities' => apply_filters('fluent_boards/task_priorities', $this->getDefaultPriorities()),
415 'wpContentCss' => add_query_arg(
416 'ver', get_bloginfo('version'),
417 site_url('/wp-includes/js/tinymce/skins/wordpress/wp-content.css')
418 ),
419 'dashiconsCss' => add_query_arg(
420 'ver', get_bloginfo('version'),
421 site_url('/wp-includes/css/dashicons.css')
422 ),
423 'is_rtl' => is_rtl(),
424 'board_menu_items' => BoardMenuHandler::getMenuItems(),
425 'reminder_types' => defined('FLUENT_BOARDS_PRO') ? Helper::taskReminderTypes() : [],
426 ]);
427 }
428
429 protected function canManageCrmContacts()
430 {
431 return defined('FLUENTCRM') && \FluentCrm\App\Services\PermissionManager::currentUserCan('fcrm_manage_contacts');
432 }
433
434 private function getOnboardingValue()
435 {
436 $onboarding = Meta::where('key', Constant::FBS_ONBOARDING)->first();
437 if ($onboarding) {
438 return $onboarding->value;
439 }
440 // if (Board::first()) {
441 // return 'yes';
442 // }
443 //
444 // return 'no';
445 }
446
447 public function getDefaultPriorities()
448 {
449 return [
450 '' => __('No priority', 'fluent-boards'),
451 'urgent' => __('Urgent', 'fluent-boards'),
452 'high' => __('High', 'fluent-boards'),
453 'medium' => __('Medium', 'fluent-boards'),
454 'low' => __('Low', 'fluent-boards')
455 ];
456 }
457
458 /*
459 * TODO: This method should be moved to PermissionManager and Helper . Task for Masiur.
460 */
461 protected function getRoleAndPermissions($userId): array
462 {
463 $role = 'fluent_boards_admin';
464 $boardsWithPermissions = Relation::query()->where('foreign_id', $userId)
465 ->where('object_type', Constant::OBJECT_TYPE_BOARD_USER)
466 ->get();
467 $boardUserCollection = Collection::make($boardsWithPermissions);
468
469 if (PermissionManager::isFluentBoardsAdmin($userId)) {
470 $role = 'fluent_boards_admin';
471 } elseif (user_can($userId, 'manage_options')) {
472 $role = 'wordpress_admin';
473 } else {
474 $role = 'member';
475 }
476
477 $permissions = [];
478 foreach ($boardUserCollection as $boardWithPermission) {
479 $permissions[] = [
480 'board_id' => $boardWithPermission->object_id,
481 'role' => $this->boardUserRole($boardWithPermission),
482 'preferences' => $boardWithPermission->preferences,
483 'permissions' => [],
484 ];
485 }
486
487 return [
488 'role' => $role,
489 'permissions' => $permissions,
490 ];
491 }
492
493 protected function boardUserRole($boardWithPermission)
494 {
495 return $boardWithPermission['settings']['is_admin'] ? 'board_admin' : (Arr::has($boardWithPermission, 'settings.is_viewer_only') && $boardWithPermission['settings']['is_viewer_only'] ? 'board_viewer' : 'board_member');
496 }
497
498 protected function getRestInfo($app)
499 {
500 $ns = $app->config->get('app.rest_namespace');
501 $ver = $app->config->get('app.rest_version');
502
503 return [
504 'base_url' => esc_url_raw(rest_url()),
505 'url' => rest_url($ns . '/' . $ver),
506 'nonce' => wp_create_nonce('wp_rest'),
507 'namespace' => $ns,
508 'version' => $ver,
509 ];
510 }
511
512 protected function getMenuIcon()
513 {
514 /*
515 * Left Sidebar Menu Icon
516 */
517 return 'data:image/svg+xml;base64,' . base64_encode('<svg width="256" height="256" viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M0 25.6C0 11.4615 11.4615 0 25.6 0H230.4C244.538 0 256 11.4615 256 25.6V230.4C256 244.538 244.538 256 230.4 256H25.6C11.4615 256 0 244.538 0 230.4V25.6ZM140.8 89.6C140.8 75.4615 152.262 64 166.4 64H186.88C189.708 64 192 66.2923 192 69.12V166.4C192 180.538 180.538 192 166.4 192H145.92C143.092 192 140.8 189.708 140.8 186.88V89.6ZM89.6 64C75.4615 64 64 75.4615 64 89.5999V148.48C64 151.308 66.2923 153.6 69.12 153.6H89.6C103.739 153.6 115.2 142.138 115.2 128V69.12C115.2 66.2923 112.908 64 110.08 64H89.6Z" fill="white"/></svg>');
518 }
519
520 public function changeFooter()
521 {
522 add_filter('admin_footer_text', function ($content) {
523 $url = '#';
524 return '';
525
526 // translators: %s is the URL for FluentBoards link
527 return sprintf(wp_kses(__('Thank you for using <a href="%s">FluentBoards</a>', 'fluent-boards'), ['a' => ['href' => []]]), esc_url($url)) . '<span title="based on your WP timezone settings" style="margin-left: 10px;" data-timestamp="' . current_time('timestamp') . '" id="fc_server_timestamp"></span>';
528 });
529
530 add_filter('update_footer', function ($text) {
531 return FLUENT_BOARDS_PLUGIN_VERSION;
532 });
533 }
534
535 /**
536 * Retrieves the Fluent Boards upload limit exposed to the admin app.
537 *
538 * @return int Upload limit in bytes.
539 */
540 public function fileUploadLimit()
541 {
542 return (new \FluentBoards\App\Services\UploadService())->getFileUploadLimit();
543 }
544
545 public function getInlineScript()
546 {
547 return "
548 function isLodash () {
549
550 let isLodash = false;
551
552 // If _ is defined and the function _.forEach exists then we know underscore OR lodash are in place
553 if ( 'undefined' != typeof( _ ) && 'function' == typeof( _.forEach ) ) {
554
555 // A small sample of some of the functions that exist in lodash but not underscore
556 const funcs = [ 'get', 'set', 'at', 'cloneDeep' ];
557
558 // Simplest if assume exists to start
559 isLodash = true;
560
561 funcs.forEach( function ( func ) {
562 // If just one of the functions do not exist, then not lodash
563 isLodash = ( 'function' != typeof( _[ func ] ) ) ? false : isLodash;
564 } );
565 }
566
567 if ( isLodash ) {
568 // We know that lodash is loaded in the _ variable
569 return true;
570 } else {
571 // We know that lodash is NOT loaded
572 return false;
573 }
574 };
575
576 if ( isLodash() ) {
577 _.noConflict();
578 }
579 ";
580 }
581 public function singleBoardRender()
582 {
583 $config = App::getInstance('config');
584
585 $slug = $config->get('app.slug');
586
587
588
589 App::make('view')->render('admin.single_board_shortcode', [
590 'slug' => $slug,
591 ]);
592 }
593
594 }
595