PluginProbe
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration / 2.0.4
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration v2.0.4
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.4, at app/Hooks/Handlers/AdminMenuHandler.php

604 lines 22.6 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 __('Addons', 'fluent-boards'),
131 __('Addons', 'fluent-boards'),
132 'manage_options',
133 'fluent-boards#/add-ons',
134 [$this, 'render']
135 );
136
137 add_submenu_page(
138 'fluent-boards',
139 __('Settings', 'fluent-boards'),
140 __('Settings', 'fluent-boards'),
141 'manage_options',
142 'fluent-boards#/settings/general-settings',
143 [$this, 'render']
144 );
145 }
146
147 public function render()
148 {
149 $this->changeFooter();
150
151 $config = App::getInstance('config');
152
153 $name = $config->get('app.name');
154 $app = App::getInstance();
155 $assets = $app['url.assets'];
156 $slug = $config->get('app.slug');
157 $baseUrl = fluent_boards_page_url();
158
159 do_action('fluent_boards/rendering_app');
160
161 // For subtask sync
162 UpdateHandler::maybeSubtaskGroupSync();
163
164 App::make('view')->render('admin.menu', [
165 'name' => $name,
166 'slug' => $slug,
167 'menuItems' => $this->getMenuItems($app),
168 'baseUrl' => $baseUrl,
169 'logo' => apply_filters('fluent_boards/app_logo', $assets . 'images/logo.svg'),
170 'icon' => apply_filters('fluent_boards/app_icon', $assets . 'images/icon.svg'),
171 'is_new' => Board::count() == 0 ? 'yes' : 'no',
172 'is_onboarded' => $this->getOnboardingValue()
173 ]);
174 }
175
176 public function getMenuItems($app)
177 {
178 $config = $app->config;
179 $slug = $config->get('app.slug');
180
181 $baseUrl = fluent_boards_page_url();
182
183 $isDiffUrl = false;
184
185 if (is_admin()) {
186 $adminUrl = admin_url('admin.php?page=fluent-boards#/');
187
188 if ($adminUrl != $baseUrl) {
189 $isDiffUrl = true;
190 $baseUrl = $adminUrl;
191 }
192 }
193
194 $menuItems = [
195 'dashboard' => [
196 'key' => 'dashboard',
197 'label' => __('Dashboard', 'fluent-boards'),
198 'permalink' => $baseUrl,
199 ],
200 'boards' => [
201 'key' => 'boards',
202 'label' => __('Boards', 'fluent-boards'),
203 'permalink' => $baseUrl . 'boards'
204 ]
205 ];
206
207 $menuItems = apply_filters('fluent_boards/core_menu_items', $menuItems);
208
209 $menuItems['reports'] = [
210 'key' => 'reports',
211 'label' => __('Reports', 'fluent-boards'),
212 'permalink' => $baseUrl . 'reports'
213 ];
214
215 $isAdmin = PermissionManager::isAdmin();
216
217 if ($isAdmin) {
218 $menuItems['help'] = [
219 'key' => 'help',
220 'label' => __('Community', 'fluent-boards'),
221 'target' => '_blank',
222 'permalink' => 'https://community.wpmanageninja.com/portal/community/fluent-boards/home'
223 ];
224 }
225
226 if ($isDiffUrl) {
227 $menuItems['front'] = [
228 'key' => 'front',
229 'label' => __('Front Portal', 'fluent-boards'),
230 'target' => '_blank',
231 'permalink' => fluent_boards_page_url()
232 ];
233 }
234
235 $menuItems = apply_filters('fluent_boards/menu_items', $menuItems);
236
237 return array_values($menuItems);
238 }
239
240 public function enqueueAssets()
241 {
242 if (!PermissionManager::hasAppAccess()) {
243 return;
244 }
245
246 add_action('wp_print_scripts', function () {
247
248 $isSkip = apply_filters('fluent_boards/skip_no_conflict', false);
249
250 if ($isSkip) {
251 return;
252 }
253
254 global $wp_scripts;
255 if (!$wp_scripts) {
256 return;
257 }
258
259 $approvedSlugs = apply_filters('fluent_boards/asset_listed_slugs', [
260 '\/fluent-crm\/'
261 ]);
262
263 $approvedSlugs[] = '\/fluent-boards\/';
264
265 $approvedSlugs = array_unique($approvedSlugs);
266
267 $approvedSlugs = implode('|', $approvedSlugs);
268
269 $pluginUrl = plugins_url();
270
271 $pluginUrl = str_replace(['http:', 'https:'], '', $pluginUrl);
272
273 foreach ($wp_scripts->queue as $script) {
274 if (empty($wp_scripts->registered[$script]) || empty($wp_scripts->registered[$script]->src)) {
275 continue;
276 }
277
278 $src = $wp_scripts->registered[$script]->src;
279 $isMatched = (strpos($src, $pluginUrl) !== false) && !preg_match('/' . $approvedSlugs . '/', $src);
280 if (!$isMatched) {
281 continue;
282 }
283 wp_dequeue_script($wp_scripts->registered[$script]->handle);
284 }
285 });
286
287 if (function_exists('wp_enqueue_media')) {
288 // Editor default styles.
289 add_filter('user_can_richedit', '__return_true');
290 if (is_admin()) {
291 wp_tinymce_inline_scripts();
292 }
293 wp_enqueue_editor();
294 wp_enqueue_script('thickbox');
295 wp_enqueue_script('editor');
296 }
297 if (function_exists('wp_enqueue_media')) {
298 wp_enqueue_media();
299 }
300
301 $app = App::getInstance();
302
303 $slug = $app->config->get('app.slug');
304
305 // Inject Vite HMR client in dev mode
306 Vite::injectViteClient();
307
308 $isRtl = is_rtl();
309 $adminAppCss = $isRtl ? 'scss/admin.scss' : 'scss/admin.scss';
310 Vite::enqueueStyle($slug . '_admin_app', $adminAppCss);
311
312 // In production, load the RTL version if needed
313 if ($isRtl && !Vite::underDevelopment()) {
314 $assets = $app['url.assets'];
315 wp_enqueue_style(
316 $slug . '_admin_app_rtl',
317 $assets . 'admin/admin-rtl.css',
318 [$slug . '_admin_app'],
319 FLUENT_BOARDS_PLUGIN_VERSION
320 );
321 }
322
323 // Enqueue merged chunk CSS (Vue SFC styles) in production
324 if (!Vite::underDevelopment()) {
325 $assets = $app['url.assets'];
326 $styleCssPath = FLUENT_BOARDS_PLUGIN_PATH . 'assets/admin/style.css';
327 if (file_exists($styleCssPath)) {
328 wp_enqueue_style(
329 $slug . '_vite_style',
330 $assets . 'admin/style.css',
331 [$slug . '_admin_app'],
332 FLUENT_BOARDS_PLUGIN_VERSION
333 );
334 }
335 }
336
337 do_action('fluent-boards_loading_app');
338
339 Vite::enqueueScript(
340 $slug . '_admin_app',
341 'admin/app.js',
342 ['jquery'],
343 FLUENT_BOARDS_PLUGIN_VERSION,
344 true
345 );
346
347 Vite::enqueueStaticScript(
348 $slug . '_global_admin',
349 'admin/global_admin.js',
350 [],
351 FLUENT_BOARDS_PLUGIN_VERSION,
352 true
353 );
354 /*
355 * This script only for resolve the conflict of lodash and underscore js
356 * Resolved the issue of media uploader specially for image upload
357 */
358 wp_add_inline_script($slug . '_global_admin', $this->getInlineScript(), 'after');
359
360 wp_localize_script($slug . '_admin_app', 'fluentAddonVars', $this->getAddonVars($app));
361
362 do_action('fluent_boards/after_enqueue_assets', $app);
363 }
364
365 public function getAddonVars($app)
366 {
367 $currentUser = get_user_by('ID', get_current_user_id());
368 $assets = $app['url.assets'];
369 $roleAndPermissions = $this->getRoleAndPermissions($currentUser->ID);
370 $onboardingValue = $this->getOnboardingValue();
371 $generalSettings = fluent_boards_get_option('general_settings', []);
372
373 return apply_filters('fluent_boards/app_vars', [
374 'slug' => $slug = $app->config->get('app.slug'),
375 'nonce' => wp_create_nonce($slug),
376 'rest' => $this->getRestInfo($app),
377 'fluent_boards_file_upload_nonce' => wp_create_nonce('fluent_boards_file_upload_nonce'),
378 'ajaxurl' => admin_url('admin-ajax.php'),
379 'file_upload_limit' => $this->fileUploadLimit(),
380 'brand_logo' => $this->getMenuIcon(),
381 'business_name' => sanitize_text_field(Arr::get($generalSettings, 'business_name', '')),
382 'asset_url' => $assets,
383 'admin_url' => admin_url('admin.php'),
384 'fluent_crm_exists' => !defined('FLUENTCRM') ? false : true,
385 'fluent_support_exists' => !!defined('FLUENT_SUPPORT_VERSION'),
386 'can_manage_crm_contacts' => $this->canManageCrmContacts(),
387 'fluent_roadmap_exists' => !defined('FLUENT_ROADMAP') ? false : true,
388 'has_pro' => !!defined('FLUENT_BOARDS_PRO_VERSION'),
389 'upgrade_url' => fluent_boards_get_upgrade_url(),
390 'board_solid_colors' => Constant::BOARD_BACKGROUND_DEFAULT_SOLID_COLORS,
391 'board_gradient_colors' => Constant::BOARD_BACKGROUND_DEFAULT_GRADIENT_COLORS,
392 'ai_features' => [
393 'enabled' => (new \FluentBoards\App\Services\AiService())->isReady(),
394 ],
395 'me' => [
396 'id' => $currentUser->ID,
397 'full_name' => trim($currentUser->first_name . ' ' . $currentUser->last_name),
398 'display_name' => $currentUser->display_name,
399 'email' => $currentUser->user_email,
400 'photo' => fluent_boards_user_avatar($currentUser->user_email, $currentUser->display_name),
401 'logout_url' => wp_logout_url(),
402 'fluent_boards_role' => $roleAndPermissions['role'],
403 'fluent_boards_capabilities' => $roleAndPermissions['permissions'],
404 'is_wp_admin' => user_can($currentUser->ID, 'manage_options') ? 'yes' : 'no',
405 'can_create_board' => PermissionManager::userHasBoardCreationPermission($currentUser->ID)
406 ],
407 'base_url' => fluent_boards_page_url(),
408 'site_url' => site_url('/'),
409 // 'server_time' => (new DateTime('now'))->format('Y-m-d H:i:s P'), // Server's default timezone
410 'server_time' => (new DateTime('now', wp_timezone()))->format('Y-m-d H:i:s P'), // wordpress site time
411 'server_time_zone' => (new DateTime('now', wp_timezone()))->format( 'P'),
412 'utc_offset' => current_time('timestamp') - strtotime(gmdate('Y-m-d H:i:s')),
413 'trans' => TransStrings::getStrings(),
414 'is_new' => Board::count() == 0 ? 'yes' : 'no',
415 'is_onboarded' => $onboardingValue,
416 'render_in' => is_admin() ? 'admin' : 'front',
417 'dashboard_notices' => apply_filters('fluent_boards/dashboard_notices', []),
418 'is_beta' => defined('FLUENT_BOARDS_PRO_VERSION') && !defined('FLUENT_BOARDS_PRO_LIVE'),
419 'advanced_modules' => fluent_boards_get_pref_settings(),
420 'crm_base_url' => defined('FLUENTCRM') ? fluentcrm_menu_url_base() : '',
421 'start_of_week' => intval(get_option('start_of_week', 0)),
422 'time_format' => get_option('time_format', 'g:i'),
423 'priorities' => apply_filters('fluent_boards/task_priorities', $this->getDefaultPriorities()),
424 'wpContentCss' => add_query_arg(
425 'ver', get_bloginfo('version'),
426 site_url('/wp-includes/js/tinymce/skins/wordpress/wp-content.css')
427 ),
428 'dashiconsCss' => add_query_arg(
429 'ver', get_bloginfo('version'),
430 site_url('/wp-includes/css/dashicons.css')
431 ),
432 'is_rtl' => is_rtl(),
433 'board_menu_items' => BoardMenuHandler::getMenuItems(),
434 'reminder_types' => defined('FLUENT_BOARDS_PRO') ? Helper::taskReminderTypes() : [],
435 ]);
436 }
437
438 protected function canManageCrmContacts()
439 {
440 return defined('FLUENTCRM') && \FluentCrm\App\Services\PermissionManager::currentUserCan('fcrm_manage_contacts');
441 }
442
443 private function getOnboardingValue()
444 {
445 $onboarding = Meta::where('key', Constant::FBS_ONBOARDING)->first();
446 if ($onboarding) {
447 return $onboarding->value;
448 }
449 // if (Board::first()) {
450 // return 'yes';
451 // }
452 //
453 // return 'no';
454 }
455
456 public function getDefaultPriorities()
457 {
458 return [
459 '' => __('No priority', 'fluent-boards'),
460 'urgent' => __('Urgent', 'fluent-boards'),
461 'high' => __('High', 'fluent-boards'),
462 'medium' => __('Medium', 'fluent-boards'),
463 'low' => __('Low', 'fluent-boards')
464 ];
465 }
466
467 /*
468 * TODO: This method should be moved to PermissionManager and Helper . Task for Masiur.
469 */
470 protected function getRoleAndPermissions($userId): array
471 {
472 $role = 'fluent_boards_admin';
473 $boardsWithPermissions = Relation::query()->where('foreign_id', $userId)
474 ->where('object_type', Constant::OBJECT_TYPE_BOARD_USER)
475 ->get();
476 $boardUserCollection = Collection::make($boardsWithPermissions);
477
478 if (PermissionManager::isFluentBoardsAdmin($userId)) {
479 $role = 'fluent_boards_admin';
480 } elseif (user_can($userId, 'manage_options')) {
481 $role = 'wordpress_admin';
482 } else {
483 $role = 'member';
484 }
485
486 $permissions = [];
487 foreach ($boardUserCollection as $boardWithPermission) {
488 $permissions[] = [
489 'board_id' => $boardWithPermission->object_id,
490 'role' => $this->boardUserRole($boardWithPermission),
491 'preferences' => $boardWithPermission->preferences,
492 'permissions' => [],
493 ];
494 }
495
496 return [
497 'role' => $role,
498 'permissions' => $permissions,
499 ];
500 }
501
502 protected function boardUserRole($boardWithPermission)
503 {
504 return $boardWithPermission['settings']['is_admin'] ? 'board_admin' : (Arr::has($boardWithPermission, 'settings.is_viewer_only') && $boardWithPermission['settings']['is_viewer_only'] ? 'board_viewer' : 'board_member');
505 }
506
507 protected function getRestInfo($app)
508 {
509 $ns = $app->config->get('app.rest_namespace');
510 $ver = $app->config->get('app.rest_version');
511
512 return [
513 'base_url' => esc_url_raw(rest_url()),
514 'url' => rest_url($ns . '/' . $ver),
515 'nonce' => wp_create_nonce('wp_rest'),
516 'namespace' => $ns,
517 'version' => $ver,
518 ];
519 }
520
521 protected function getMenuIcon()
522 {
523 /*
524 * Left Sidebar Menu Icon
525 */
526 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>');
527 }
528
529 public function changeFooter()
530 {
531 add_filter('admin_footer_text', function ($content) {
532 $url = '#';
533 return '';
534
535 // translators: %s is the URL for FluentBoards link
536 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>';
537 });
538
539 add_filter('update_footer', function ($text) {
540 return FLUENT_BOARDS_PLUGIN_VERSION;
541 });
542 }
543
544 /**
545 * Retrieves the Fluent Boards upload limit exposed to the admin app.
546 *
547 * @return int Upload limit in bytes.
548 */
549 public function fileUploadLimit()
550 {
551 return (new \FluentBoards\App\Services\UploadService())->getFileUploadLimit();
552 }
553
554 public function getInlineScript()
555 {
556 return "
557 function isLodash () {
558
559 let isLodash = false;
560
561 // If _ is defined and the function _.forEach exists then we know underscore OR lodash are in place
562 if ( 'undefined' != typeof( _ ) && 'function' == typeof( _.forEach ) ) {
563
564 // A small sample of some of the functions that exist in lodash but not underscore
565 const funcs = [ 'get', 'set', 'at', 'cloneDeep' ];
566
567 // Simplest if assume exists to start
568 isLodash = true;
569
570 funcs.forEach( function ( func ) {
571 // If just one of the functions do not exist, then not lodash
572 isLodash = ( 'function' != typeof( _[ func ] ) ) ? false : isLodash;
573 } );
574 }
575
576 if ( isLodash ) {
577 // We know that lodash is loaded in the _ variable
578 return true;
579 } else {
580 // We know that lodash is NOT loaded
581 return false;
582 }
583 };
584
585 if ( isLodash() ) {
586 _.noConflict();
587 }
588 ";
589 }
590 public function singleBoardRender()
591 {
592 $config = App::getInstance('config');
593
594 $slug = $config->get('app.slug');
595
596
597
598 App::make('view')->render('admin.single_board_shortcode', [
599 'slug' => $slug,
600 ]);
601 }
602
603 }
604