PluginProbe
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration / 2.2.0
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration v2.2.0
2.2.1 2.2.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 All 44 releases
← All changes | app/Hooks/Handlers/AdminMenuHandler.php +134 -92 1.35 → 2.2.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-boards'),
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,14 +155,10 @@
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
137 -
138 161 App::make('view')->render('admin.menu', [
139 162 'name' => $name,
140 163 'slug' => $slug,
141 164 'menuItems' => $this->getMenuItems($app),
@@ -146,48 +169,8 @@
146 169 'is_onboarded' => $this->getOnboardingValue()
147 170 ]);
148 171 }
149 172
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 173 public function getMenuItems($app)
191 174 {
192 175 $config = $app->config;
193 176 $slug = $config->get('app.slug');
@@ -228,25 +211,8 @@
228 211
229 212 $isAdmin = PermissionManager::isAdmin();
230 213
231 214 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 215 $menuItems['help'] = [
250 216 'key' => 'help',
251 217 'label' => __('Community', 'fluent-boards'),
252 218 'target' => '_blank',
@@ -256,9 +222,9 @@
256 222
257 223 if ($isDiffUrl) {
258 224 $menuItems['front'] = [
259 225 'key' => 'front',
260 - 'label' => __('Frontend Portal', 'fluent-boards'),
226 + 'label' => __('Front Portal', 'fluent-boards'),
261 227 'target' => '_blank',
262 228 'permalink' => fluent_boards_page_url()
263 229 ];
264 230 }
@@ -330,32 +296,58 @@
330 296 }
331 297
332 298 $app = App::getInstance();
333 299
334 - $assets = $app['url.assets'];
300 + $slug = $app->config->get('app.slug');
335 301
336 - $slug = $app->config->get('app.slug');
302 + // Inject Vite HMR client in dev mode
303 + Vite::injectViteClient();
337 304
338 - wp_enqueue_style(
339 - $slug . '_admin_app',
340 - $assets . 'admin/admin.css',
341 - [],
342 - FLUENT_BOARDS_PLUGIN_VERSION
343 - );
305 + $isRtl = fluent_boards_is_rtl();
306 + Vite::enqueueStyle($slug . '_admin_app', 'scss/admin.scss');
344 307
345 - do_action($slug . '_loading_app');
308 + // Enqueue merged chunk CSS (Vue SFC styles) in production
309 + if (!Vite::underDevelopment()) {
310 + $assets = $app['url.assets'];
311 + $styleCssPath = FLUENT_BOARDS_PLUGIN_PATH . 'assets/admin/style.css';
312 + if (file_exists($styleCssPath)) {
313 + wp_enqueue_style(
314 + $slug . '_vite_style',
315 + $assets . 'admin/style.css',
316 + [$slug . '_admin_app'],
317 + FLUENT_BOARDS_PLUGIN_VERSION
318 + );
319 + }
320 + }
346 321
347 - wp_enqueue_script(
322 + // RTL overrides must load after style.css so rules appended to the
323 + // element-plus layer win by source order, not only by specificity.
324 + if ($isRtl) {
325 + $rtlDeps = [$slug . '_admin_app'];
326 + if (wp_style_is($slug . '_vite_style', 'enqueued')) {
327 + $rtlDeps[] = $slug . '_vite_style';
328 + }
329 + Vite::enqueueStyle(
330 + $slug . '_admin_app_rtl',
331 + 'scss/admin_rtl.scss',
332 + $rtlDeps,
333 + FLUENT_BOARDS_PLUGIN_VERSION
334 + );
335 + }
336 +
337 + do_action('fluent-boards_loading_app');
338 +
339 + Vite::enqueueScript(
348 340 $slug . '_admin_app',
349 - $assets . 'admin/app.js',
341 + 'admin/app.js',
350 342 ['jquery'],
351 343 FLUENT_BOARDS_PLUGIN_VERSION,
352 344 true
353 345 );
354 346
355 - wp_enqueue_script(
347 + Vite::enqueueStaticScript(
356 348 $slug . '_global_admin',
357 - $assets . 'admin/global_admin.js',
349 + 'admin/global_admin.js',
358 350 [],
359 351 FLUENT_BOARDS_PLUGIN_VERSION,
360 352 true
361 353 );
@@ -375,8 +367,9 @@
375 367 $currentUser = get_user_by('ID', get_current_user_id());
376 368 $assets = $app['url.assets'];
377 369 $roleAndPermissions = $this->getRoleAndPermissions($currentUser->ID);
378 370 $onboardingValue = $this->getOnboardingValue();
371 + $generalSettings = fluent_boards_get_option('general_settings', []);
379 372
380 373 return apply_filters('fluent_boards/app_vars', [
381 374 'slug' => $slug = $app->config->get('app.slug'),
382 375 'nonce' => wp_create_nonce($slug),
@@ -384,13 +377,23 @@
384 377 'fluent_boards_file_upload_nonce' => wp_create_nonce('fluent_boards_file_upload_nonce'),
385 378 'ajaxurl' => admin_url('admin-ajax.php'),
386 379 'file_upload_limit' => $this->fileUploadLimit(),
387 380 'brand_logo' => $this->getMenuIcon(),
381 + 'business_name' => sanitize_text_field(Arr::get($generalSettings, 'business_name', '')),
388 382 'asset_url' => $assets,
389 383 'admin_url' => admin_url('admin.php'),
390 384 'fluent_crm_exists' => !defined('FLUENTCRM') ? false : true,
385 + 'fluent_support_exists' => !!defined('FLUENT_SUPPORT_VERSION'),
386 + 'can_manage_crm_contacts' => $this->canManageCrmContacts(),
391 387 'fluent_roadmap_exists' => !defined('FLUENT_ROADMAP') ? false : true,
392 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 + 'label_color_presets' => Constant::LABEL_COLOR_PRESETS,
393 + 'ai_features' => [
394 + 'enabled' => (new \FluentBoards\App\Services\AiService())->isReady(),
395 + ],
393 396 'me' => [
394 397 'id' => $currentUser->ID,
395 398 'full_name' => trim($currentUser->first_name . ' ' . $currentUser->last_name),
396 399 'display_name' => $currentUser->display_name,
@@ -395,28 +398,52 @@
395 398 'full_name' => trim($currentUser->first_name . ' ' . $currentUser->last_name),
396 399 'display_name' => $currentUser->display_name,
397 400 'email' => $currentUser->user_email,
398 401 'photo' => fluent_boards_user_avatar($currentUser->user_email, $currentUser->display_name),
402 + 'logout_url' => wp_logout_url(),
399 403 'fluent_boards_role' => $roleAndPermissions['role'],
400 404 'fluent_boards_capabilities' => $roleAndPermissions['permissions'],
401 - 'is_wp_admin' => user_can($currentUser->ID, 'manage_options') ? 'yes' : 'no'
405 + 'is_wp_admin' => user_can($currentUser->ID, 'manage_options') ? 'yes' : 'no',
406 + 'can_create_board' => PermissionManager::userHasBoardCreationPermission($currentUser->ID)
402 407 ],
403 408 'base_url' => fluent_boards_page_url(),
404 409 'site_url' => site_url('/'),
405 - 'server_time' => current_time('mysql'),
410 + // 'server_time' => (new DateTime('now'))->format('Y-m-d H:i:s P'), // Server's default timezone
411 + 'server_time' => (new DateTime('now', wp_timezone()))->format('Y-m-d H:i:s P'), // wordpress site time
412 + 'server_time_zone' => (new DateTime('now', wp_timezone()))->format( 'P'),
406 413 'utc_offset' => current_time('timestamp') - strtotime(gmdate('Y-m-d H:i:s')),
407 414 'trans' => TransStrings::getStrings(),
408 415 'is_new' => Board::count() == 0 ? 'yes' : 'no',
409 416 'is_onboarded' => $onboardingValue,
410 417 'render_in' => is_admin() ? 'admin' : 'front',
418 + // Gates wp-admin links (WP profile, CRM, application passwords) so the frontend portal shows no admin traces.
419 + 'can_access_wp_admin' => (bool) apply_filters('fluent_boards/can_access_wp_admin', is_admin() || current_user_can('manage_options'), $currentUser),
411 420 'dashboard_notices' => apply_filters('fluent_boards/dashboard_notices', []),
412 421 'is_beta' => defined('FLUENT_BOARDS_PRO_VERSION') && !defined('FLUENT_BOARDS_PRO_LIVE'),
413 422 'advanced_modules' => fluent_boards_get_pref_settings(),
414 423 'crm_base_url' => defined('FLUENTCRM') ? fluentcrm_menu_url_base() : '',
415 424 'start_of_week' => intval(get_option('start_of_week', 0)),
425 + 'time_format' => get_option('time_format', 'g:i'),
426 + 'priorities' => apply_filters('fluent_boards/task_priorities', $this->getDefaultPriorities()),
427 + 'wpContentCss' => add_query_arg(
428 + 'ver', get_bloginfo('version'),
429 + site_url('/wp-includes/js/tinymce/skins/wordpress/wp-content.css')
430 + ),
431 + 'dashiconsCss' => add_query_arg(
432 + 'ver', get_bloginfo('version'),
433 + site_url('/wp-includes/css/dashicons.css')
434 + ),
435 + 'is_rtl' => fluent_boards_is_rtl(),
436 + 'board_menu_items' => BoardMenuHandler::getMenuItems(),
437 + 'reminder_types' => defined('FLUENT_BOARDS_PRO') ? Helper::taskReminderTypes() : [],
416 438 ]);
417 439 }
418 440
441 + protected function canManageCrmContacts()
442 + {
443 + return defined('FLUENTCRM') && \FluentCrm\App\Services\PermissionManager::currentUserCan('fcrm_manage_contacts');
444 + }
445 +
419 446 private function getOnboardingValue()
420 447 {
421 448 $onboarding = Meta::where('key', Constant::FBS_ONBOARDING)->first();
422 449 if ($onboarding) {
@@ -428,8 +455,19 @@
428 455 //
429 456 // return 'no';
430 457 }
431 458
459 + public function getDefaultPriorities()
460 + {
461 + return [
462 + '' => __('No priority', 'fluent-boards'),
463 + 'urgent' => __('Urgent', 'fluent-boards'),
464 + 'high' => __('High', 'fluent-boards'),
465 + 'medium' => __('Medium', 'fluent-boards'),
466 + 'low' => __('Low', 'fluent-boards')
467 + ];
468 + }
469 +
432 470 /*
433 471 * TODO: This method should be moved to PermissionManager and Helper . Task for Masiur.
434 472 */
435 473 protected function getRoleAndPermissions($userId): array
@@ -496,8 +534,9 @@
496 534 add_filter('admin_footer_text', function ($content) {
497 535 $url = '#';
498 536 return '';
499 537
538 + // translators: %s is the URL for FluentBoards link
500 539 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>';
501 540 });
502 541
503 542 add_filter('update_footer', function ($text) {
@@ -505,25 +544,15 @@
505 544 });
506 545 }
507 546
508 547 /**
509 - * Retrieves the maximum upload limit based on PHP and WordPress configurations.
548 + * Retrieves the Fluent Boards upload limit exposed to the admin app.
510 549 *
511 - * This method calculates and returns the minimum value among the following:
512 - * 1. The PHP 'upload_max_filesize' configuration.
513 - * 2. The PHP 'post_max_size' configuration.
514 - * 3. The WordPress maximum upload size limit using the 'wp_max_upload_size' function.
515 - *
516 - * @return int The minimum of the mentioned upload size limits in bytes.
550 + * @return int Upload limit in bytes.
517 551 */
518 552 public function fileUploadLimit()
519 553 {
520 - // Calculate the minimum of 'upload_max_filesize', 'post_max_size', and WordPress maximum upload size.
521 - return min(
522 - wp_convert_hr_to_bytes(ini_get('upload_max_filesize')),
523 - wp_convert_hr_to_bytes(ini_get('post_max_size')),
524 - wp_max_upload_size()
525 - );
554 + return (new \FluentBoards\App\Services\UploadService())->getFileUploadLimit();
526 555 }
527 556
528 557 public function getInlineScript()
529 558 {
@@ -560,5 +589,18 @@
560 589 _.noConflict();
561 590 }
562 591 ";
563 592 }
593 + public function singleBoardRender()
594 + {
595 + $config = App::getInstance('config');
596 +
597 + $slug = $config->get('app.slug');
598 +
599 +
600 +
601 + App::make('view')->render('admin.single_board_shortcode', [
602 + 'slug' => $slug,
603 + ]);
604 + }
605 +
564 606 }