PluginProbe
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration / 2.1.0
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration v2.1.0
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
← All changes | app/Hooks/Handlers/AdminMenuHandler.php +141 -92 1.222.1.0 View file →
@@ -6,12 +6,15 @@
6 6 use FluentBoards\App\Models\Board;
7 7 use FluentBoards\App\Models\Meta;
8 8 use FluentBoards\App\Models\Relation;
9 9 use FluentBoards\App\Services\Constant;
10 +use FluentBoards\App\Services\Helper;
10 11 use FluentBoards\App\Services\TransStrings;
12 +use FluentBoards\App\Vite;
11 13 use FluentBoards\Framework\Support\Arr;
12 14 use FluentBoards\Framework\Support\Collection;
13 15 use FluentBoards\App\Services\PermissionManager;
16 +use FluentBoards\Framework\Support\DateTime;
14 17
15 18 class AdminMenuHandler
16 19 {
17 20
@@ -22,9 +25,9 @@
22 25 add_filter('fluent_crm/core_menu_items', function ($items) {
23 26 if (PermissionManager::userHasAnyBoardAccess()) {
24 27 $items['fluent-boards'] = [
25 28 'key' => 'fluent-boards',
26 - 'label' => __('Fluent Boards', 'fluent-crm'),
29 + 'label' => __('Boards', 'fluent-boards'),
27 30 'permalink' => admin_url('admin.php?page=fluent-boards#/')
28 31 ];
29 32 }
30 33 return $items;
@@ -30,8 +33,9 @@
30 33 return $items;
31 34 });
32 35
33 36 add_action('admin_enqueue_scripts', function () {
37 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Checking admin page context for asset enqueuing, no data modification
34 38 if (!isset($_REQUEST['page']) || $_REQUEST['page'] !== 'fluent-boards') {
35 39 return;
36 40 }
37 41
@@ -36,8 +40,22 @@
36 40 }
37 41
38 42 $this->enqueueAssets();
39 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);
40 58 }
41 59
42 60 public function add()
43 61 {
@@ -108,12 +126,21 @@
108 126 );
109 127
110 128 add_submenu_page(
111 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',
112 139 __('Settings', 'fluent-boards'),
113 140 __('Settings', 'fluent-boards'),
114 141 'manage_options',
115 - 'fluent-boards#/settings/members-role',
142 + 'fluent-boards#/settings/general-settings',
116 143 [$this, 'render']
117 144 );
118 145 }
119 146
@@ -128,13 +155,12 @@
128 155 $assets = $app['url.assets'];
129 156 $slug = $config->get('app.slug');
130 157 $baseUrl = fluent_boards_page_url();
131 158
132 - $this->updateDatabase();
133 -
134 -
135 159 do_action('fluent_boards/rendering_app');
136 160
161 + // For subtask sync
162 + UpdateHandler::maybeSubtaskGroupSync();
137 163
138 164 App::make('view')->render('admin.menu', [
139 165 'name' => $name,
140 166 'slug' => $slug,
@@ -146,48 +172,8 @@
146 172 'is_onboarded' => $this->getOnboardingValue()
147 173 ]);
148 174 }
149 175
150 - private function updateDatabase($isForced = true)
151 - {
152 - global $wpdb;
153 -
154 - $table = $wpdb->prefix . 'fbs_board_terms';
155 -
156 - if ($wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $table)) != $table) {
157 - return;
158 - } else {
159 - // change column type from int to decimal - for already installed sites
160 - $column_name = 'position';
161 - $preparedQuery = $wpdb->prepare("DESCRIBE $table %s", $column_name);
162 - $dataType = $wpdb->get_row($preparedQuery);
163 - if (strpos($dataType->Type, 'int') !== false) {
164 - $sql = $wpdb->prepare(
165 - "ALTER TABLE $table MODIFY $column_name decimal(10,2) NOT NULL DEFAULT '1' COMMENT 'Position: 1 = top/first, 2 = second/second in top, etc.';"
166 - );
167 - $wpdb->query($sql);
168 - }
169 - }
170 -
171 - $table = $wpdb->prefix . 'fbs_comments';
172 -
173 - if ($wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $table)) != $table) {
174 - return;
175 - } else {
176 - $column_name = 'settings';
177 - // Check if the column already exists
178 - $column_exists = $wpdb->get_var($wpdb->prepare(
179 - "SHOW COLUMNS FROM $table LIKE %s", $column_name
180 - ));
181 -
182 - if (!$column_exists) {
183 - $sql = "ALTER TABLE $table ADD $column_name TEXT NULL COMMENT 'serialize array' AFTER `created_by`";
184 - $wpdb->query($sql);
185 - }
186 - }
187 -
188 - }
189 -
190 176 public function getMenuItems($app)
191 177 {
192 178 $config = $app->config;
193 179 $slug = $config->get('app.slug');
@@ -228,25 +214,8 @@
228 214
229 215 $isAdmin = PermissionManager::isAdmin();
230 216
231 217 if ($isAdmin) {
232 - $menuItems['settings'] = [
233 - 'key' => 'settings',
234 - 'label' => __('Settings', 'fluent-boards'),
235 - 'permalink' => $baseUrl . 'settings/members-role'
236 - ];
237 - }
238 -
239 - if (!defined('FLUENT_BOARDS_PRO')) {
240 - $menuItems['get_pro'] = [
241 - 'key' => 'get_pro',
242 - 'label' => __('Get Pro', 'fluent-boards'),
243 - 'permalink' => 'https://fluentboards.com?utm_source=menu&utm_medium=plugin&utm_campaign=pro&utm_id=wp',
244 - 'class' => 'pro_link'
245 - ];
246 - }
247 -
248 - if ($isAdmin) {
249 218 $menuItems['help'] = [
250 219 'key' => 'help',
251 220 'label' => __('Community', 'fluent-boards'),
252 221 'target' => '_blank',
@@ -256,9 +225,9 @@
256 225
257 226 if ($isDiffUrl) {
258 227 $menuItems['front'] = [
259 228 'key' => 'front',
260 - 'label' => __('Frontend Portal', 'fluent-boards'),
229 + 'label' => __('Front Portal', 'fluent-boards'),
261 230 'target' => '_blank',
262 231 'permalink' => fluent_boards_page_url()
263 232 ];
264 233 }
@@ -330,32 +299,58 @@
330 299 }
331 300
332 301 $app = App::getInstance();
333 302
334 - $assets = $app['url.assets'];
303 + $slug = $app->config->get('app.slug');
335 304
336 - $slug = $app->config->get('app.slug');
305 + // Inject Vite HMR client in dev mode
306 + Vite::injectViteClient();
337 307
338 - wp_enqueue_style(
339 - $slug . '_admin_app',
340 - $assets . 'admin/admin.css',
341 - [],
342 - FLUENT_BOARDS_PLUGIN_VERSION
343 - );
308 + $isRtl = fluent_boards_is_rtl();
309 + Vite::enqueueStyle($slug . '_admin_app', 'scss/admin.scss');
344 310
345 - do_action($slug . '_loading_app');
311 + // Enqueue merged chunk CSS (Vue SFC styles) in production
312 + if (!Vite::underDevelopment()) {
313 + $assets = $app['url.assets'];
314 + $styleCssPath = FLUENT_BOARDS_PLUGIN_PATH . 'assets/admin/style.css';
315 + if (file_exists($styleCssPath)) {
316 + wp_enqueue_style(
317 + $slug . '_vite_style',
318 + $assets . 'admin/style.css',
319 + [$slug . '_admin_app'],
320 + FLUENT_BOARDS_PLUGIN_VERSION
321 + );
322 + }
323 + }
346 324
347 - wp_enqueue_script(
325 + // RTL overrides must load after style.css so rules appended to the
326 + // element-plus layer win by source order, not only by specificity.
327 + if ($isRtl) {
328 + $rtlDeps = [$slug . '_admin_app'];
329 + if (wp_style_is($slug . '_vite_style', 'enqueued')) {
330 + $rtlDeps[] = $slug . '_vite_style';
331 + }
332 + Vite::enqueueStyle(
333 + $slug . '_admin_app_rtl',
334 + 'scss/admin_rtl.scss',
335 + $rtlDeps,
336 + FLUENT_BOARDS_PLUGIN_VERSION
337 + );
338 + }
339 +
340 + do_action('fluent-boards_loading_app');
341 +
342 + Vite::enqueueScript(
348 343 $slug . '_admin_app',
349 - $assets . 'admin/app.js',
344 + 'admin/app.js',
350 345 ['jquery'],
351 346 FLUENT_BOARDS_PLUGIN_VERSION,
352 347 true
353 348 );
354 349
355 - wp_enqueue_script(
350 + Vite::enqueueStaticScript(
356 351 $slug . '_global_admin',
357 - $assets . 'admin/global_admin.js',
352 + 'admin/global_admin.js',
358 353 [],
359 354 FLUENT_BOARDS_PLUGIN_VERSION,
360 355 true
361 356 );
@@ -375,8 +370,9 @@
375 370 $currentUser = get_user_by('ID', get_current_user_id());
376 371 $assets = $app['url.assets'];
377 372 $roleAndPermissions = $this->getRoleAndPermissions($currentUser->ID);
378 373 $onboardingValue = $this->getOnboardingValue();
374 + $generalSettings = fluent_boards_get_option('general_settings', []);
379 375
380 376 return apply_filters('fluent_boards/app_vars', [
381 377 'slug' => $slug = $app->config->get('app.slug'),
382 378 'nonce' => wp_create_nonce($slug),
@@ -384,13 +380,23 @@
384 380 'fluent_boards_file_upload_nonce' => wp_create_nonce('fluent_boards_file_upload_nonce'),
385 381 'ajaxurl' => admin_url('admin-ajax.php'),
386 382 'file_upload_limit' => $this->fileUploadLimit(),
387 383 'brand_logo' => $this->getMenuIcon(),
384 + 'business_name' => sanitize_text_field(Arr::get($generalSettings, 'business_name', '')),
388 385 'asset_url' => $assets,
389 386 'admin_url' => admin_url('admin.php'),
390 387 'fluent_crm_exists' => !defined('FLUENTCRM') ? false : true,
388 + 'fluent_support_exists' => !!defined('FLUENT_SUPPORT_VERSION'),
389 + 'can_manage_crm_contacts' => $this->canManageCrmContacts(),
391 390 'fluent_roadmap_exists' => !defined('FLUENT_ROADMAP') ? false : true,
392 391 'has_pro' => !!defined('FLUENT_BOARDS_PRO_VERSION'),
392 + 'upgrade_url' => fluent_boards_get_upgrade_url(),
393 + 'board_solid_colors' => Constant::BOARD_BACKGROUND_DEFAULT_SOLID_COLORS,
394 + 'board_gradient_colors' => Constant::BOARD_BACKGROUND_DEFAULT_GRADIENT_COLORS,
395 + 'label_color_presets' => Constant::LABEL_COLOR_PRESETS,
396 + 'ai_features' => [
397 + 'enabled' => (new \FluentBoards\App\Services\AiService())->isReady(),
398 + ],
393 399 'me' => [
394 400 'id' => $currentUser->ID,
395 401 'full_name' => trim($currentUser->first_name . ' ' . $currentUser->last_name),
396 402 'display_name' => $currentUser->display_name,
@@ -395,15 +401,19 @@
395 401 'full_name' => trim($currentUser->first_name . ' ' . $currentUser->last_name),
396 402 'display_name' => $currentUser->display_name,
397 403 'email' => $currentUser->user_email,
398 404 'photo' => fluent_boards_user_avatar($currentUser->user_email, $currentUser->display_name),
405 + 'logout_url' => wp_logout_url(),
399 406 'fluent_boards_role' => $roleAndPermissions['role'],
400 407 'fluent_boards_capabilities' => $roleAndPermissions['permissions'],
401 - 'is_wp_admin' => user_can($currentUser->ID, 'manage_options') ? 'yes' : 'no'
408 + 'is_wp_admin' => user_can($currentUser->ID, 'manage_options') ? 'yes' : 'no',
409 + 'can_create_board' => PermissionManager::userHasBoardCreationPermission($currentUser->ID)
402 410 ],
403 411 'base_url' => fluent_boards_page_url(),
404 412 'site_url' => site_url('/'),
405 - 'server_time' => current_time('mysql'),
413 + // 'server_time' => (new DateTime('now'))->format('Y-m-d H:i:s P'), // Server's default timezone
414 + 'server_time' => (new DateTime('now', wp_timezone()))->format('Y-m-d H:i:s P'), // wordpress site time
415 + 'server_time_zone' => (new DateTime('now', wp_timezone()))->format( 'P'),
406 416 'utc_offset' => current_time('timestamp') - strtotime(gmdate('Y-m-d H:i:s')),
407 417 'trans' => TransStrings::getStrings(),
408 418 'is_new' => Board::count() == 0 ? 'yes' : 'no',
409 419 'is_onboarded' => $onboardingValue,
@@ -411,11 +421,30 @@
411 421 'dashboard_notices' => apply_filters('fluent_boards/dashboard_notices', []),
412 422 'is_beta' => defined('FLUENT_BOARDS_PRO_VERSION') && !defined('FLUENT_BOARDS_PRO_LIVE'),
413 423 'advanced_modules' => fluent_boards_get_pref_settings(),
414 424 'crm_base_url' => defined('FLUENTCRM') ? fluentcrm_menu_url_base() : '',
425 + 'start_of_week' => intval(get_option('start_of_week', 0)),
426 + 'time_format' => get_option('time_format', 'g:i'),
427 + 'priorities' => apply_filters('fluent_boards/task_priorities', $this->getDefaultPriorities()),
428 + 'wpContentCss' => add_query_arg(
429 + 'ver', get_bloginfo('version'),
430 + site_url('/wp-includes/js/tinymce/skins/wordpress/wp-content.css')
431 + ),
432 + 'dashiconsCss' => add_query_arg(
433 + 'ver', get_bloginfo('version'),
434 + site_url('/wp-includes/css/dashicons.css')
435 + ),
436 + 'is_rtl' => fluent_boards_is_rtl(),
437 + 'board_menu_items' => BoardMenuHandler::getMenuItems(),
438 + 'reminder_types' => defined('FLUENT_BOARDS_PRO') ? Helper::taskReminderTypes() : [],
415 439 ]);
416 440 }
417 441
442 + protected function canManageCrmContacts()
443 + {
444 + return defined('FLUENTCRM') && \FluentCrm\App\Services\PermissionManager::currentUserCan('fcrm_manage_contacts');
445 + }
446 +
418 447 private function getOnboardingValue()
419 448 {
420 449 $onboarding = Meta::where('key', Constant::FBS_ONBOARDING)->first();
421 450 if ($onboarding) {
@@ -427,8 +456,19 @@
427 456 //
428 457 // return 'no';
429 458 }
430 459
460 + public function getDefaultPriorities()
461 + {
462 + return [
463 + '' => __('No priority', 'fluent-boards'),
464 + 'urgent' => __('Urgent', 'fluent-boards'),
465 + 'high' => __('High', 'fluent-boards'),
466 + 'medium' => __('Medium', 'fluent-boards'),
467 + 'low' => __('Low', 'fluent-boards')
468 + ];
469 + }
470 +
431 471 /*
432 472 * TODO: This method should be moved to PermissionManager and Helper . Task for Masiur.
433 473 */
434 474 protected function getRoleAndPermissions($userId): array
@@ -450,9 +490,9 @@
450 490 $permissions = [];
451 491 foreach ($boardUserCollection as $boardWithPermission) {
452 492 $permissions[] = [
453 493 'board_id' => $boardWithPermission->object_id,
454 - 'role' => $boardWithPermission['settings']['is_admin'] ? 'board_admin' : 'board_member',
494 + 'role' => $this->boardUserRole($boardWithPermission),
455 495 'preferences' => $boardWithPermission->preferences,
456 496 'permissions' => [],
457 497 ];
458 498 }
@@ -462,8 +502,13 @@
462 502 'permissions' => $permissions,
463 503 ];
464 504 }
465 505
506 + protected function boardUserRole($boardWithPermission)
507 + {
508 + return $boardWithPermission['settings']['is_admin'] ? 'board_admin' : (Arr::has($boardWithPermission, 'settings.is_viewer_only') && $boardWithPermission['settings']['is_viewer_only'] ? 'board_viewer' : 'board_member');
509 + }
510 +
466 511 protected function getRestInfo($app)
467 512 {
468 513 $ns = $app->config->get('app.rest_namespace');
469 514 $ver = $app->config->get('app.rest_version');
@@ -490,8 +535,9 @@
490 535 add_filter('admin_footer_text', function ($content) {
491 536 $url = '#';
492 537 return '';
493 538
539 + // translators: %s is the URL for FluentBoards link
494 540 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>';
495 541 });
496 542
497 543 add_filter('update_footer', function ($text) {
@@ -499,25 +545,15 @@
499 545 });
500 546 }
501 547
502 548 /**
503 - * Retrieves the maximum upload limit based on PHP and WordPress configurations.
549 + * Retrieves the Fluent Boards upload limit exposed to the admin app.
504 550 *
505 - * This method calculates and returns the minimum value among the following:
506 - * 1. The PHP 'upload_max_filesize' configuration.
507 - * 2. The PHP 'post_max_size' configuration.
508 - * 3. The WordPress maximum upload size limit using the 'wp_max_upload_size' function.
509 - *
510 - * @return int The minimum of the mentioned upload size limits in bytes.
551 + * @return int Upload limit in bytes.
511 552 */
512 553 public function fileUploadLimit()
513 554 {
514 - // Calculate the minimum of 'upload_max_filesize', 'post_max_size', and WordPress maximum upload size.
515 - return min(
516 - wp_convert_hr_to_bytes(ini_get('upload_max_filesize')),
517 - wp_convert_hr_to_bytes(ini_get('post_max_size')),
518 - wp_max_upload_size()
519 - );
555 + return (new \FluentBoards\App\Services\UploadService())->getFileUploadLimit();
520 556 }
521 557
522 558 public function getInlineScript()
523 559 {
@@ -554,5 +590,18 @@
554 590 _.noConflict();
555 591 }
556 592 ";
557 593 }
594 + public function singleBoardRender()
595 + {
596 + $config = App::getInstance('config');
597 +
598 + $slug = $config->get('app.slug');
599 +
600 +
601 +
602 + App::make('view')->render('admin.single_board_shortcode', [
603 + 'slug' => $slug,
604 + ]);
605 + }
606 +
558 607 }