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 +124 -97 1.412.1.0 View file →
@@ -6,9 +6,11 @@
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;
14 16 use FluentBoards\Framework\Support\DateTime;
@@ -23,9 +25,9 @@
23 25 add_filter('fluent_crm/core_menu_items', function ($items) {
24 26 if (PermissionManager::userHasAnyBoardAccess()) {
25 27 $items['fluent-boards'] = [
26 28 'key' => 'fluent-boards',
27 - 'label' => __('Fluent Boards', 'fluent-boards'),
29 + 'label' => __('Boards', 'fluent-boards'),
28 30 'permalink' => admin_url('admin.php?page=fluent-boards#/')
29 31 ];
30 32 }
31 33 return $items;
@@ -31,8 +33,9 @@
31 33 return $items;
32 34 });
33 35
34 36 add_action('admin_enqueue_scripts', function () {
37 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Checking admin page context for asset enqueuing, no data modification
35 38 if (!isset($_REQUEST['page']) || $_REQUEST['page'] !== 'fluent-boards') {
36 39 return;
37 40 }
38 41
@@ -37,8 +40,22 @@
37 40 }
38 41
39 42 $this->enqueueAssets();
40 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);
41 58 }
42 59
43 60 public function add()
44 61 {
@@ -109,12 +126,21 @@
109 126 );
110 127
111 128 add_submenu_page(
112 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',
113 139 __('Settings', 'fluent-boards'),
114 140 __('Settings', 'fluent-boards'),
115 141 'manage_options',
116 - 'fluent-boards#/settings/members-role',
142 + 'fluent-boards#/settings/general-settings',
117 143 [$this, 'render']
118 144 );
119 145 }
120 146
@@ -129,13 +155,12 @@
129 155 $assets = $app['url.assets'];
130 156 $slug = $config->get('app.slug');
131 157 $baseUrl = fluent_boards_page_url();
132 158
133 - $this->updateDatabase();
134 -
135 -
136 159 do_action('fluent_boards/rendering_app');
137 160
161 + // For subtask sync
162 + UpdateHandler::maybeSubtaskGroupSync();
138 163
139 164 App::make('view')->render('admin.menu', [
140 165 'name' => $name,
141 166 'slug' => $slug,
@@ -147,48 +172,8 @@
147 172 'is_onboarded' => $this->getOnboardingValue()
148 173 ]);
149 174 }
150 175
151 - private function updateDatabase($isForced = true)
152 - {
153 - global $wpdb;
154 -
155 - $table = $wpdb->prefix . 'fbs_board_terms';
156 -
157 - if ($wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $table)) != $table) {
158 - return;
159 - } else {
160 - // change column type from int to decimal - for already installed sites
161 - $column_name = 'position';
162 - $preparedQuery = $wpdb->prepare("DESCRIBE $table %s", $column_name);
163 - $dataType = $wpdb->get_row($preparedQuery);
164 - if (strpos($dataType->Type, 'int') !== false) {
165 - $sql = $wpdb->prepare(
166 - "ALTER TABLE $table MODIFY $column_name decimal(10,2) NOT NULL DEFAULT '1' COMMENT 'Position: 1 = top/first, 2 = second/second in top, etc.';"
167 - );
168 - $wpdb->query($sql);
169 - }
170 - }
171 -
172 - $table = $wpdb->prefix . 'fbs_comments';
173 -
174 - if ($wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $table)) != $table) {
175 - return;
176 - } else {
177 - $column_name = 'settings';
178 - // Check if the column already exists
179 - $column_exists = $wpdb->get_var($wpdb->prepare(
180 - "SHOW COLUMNS FROM $table LIKE %s", $column_name
181 - ));
182 -
183 - if (!$column_exists) {
184 - $sql = "ALTER TABLE $table ADD $column_name TEXT NULL COMMENT 'serialize array' AFTER `created_by`";
185 - $wpdb->query($sql);
186 - }
187 - }
188 -
189 - }
190 -
191 176 public function getMenuItems($app)
192 177 {
193 178 $config = $app->config;
194 179 $slug = $config->get('app.slug');
@@ -229,25 +214,8 @@
229 214
230 215 $isAdmin = PermissionManager::isAdmin();
231 216
232 217 if ($isAdmin) {
233 - $menuItems['settings'] = [
234 - 'key' => 'settings',
235 - 'label' => __('Settings', 'fluent-boards'),
236 - 'permalink' => $baseUrl . 'settings/members-role'
237 - ];
238 - }
239 -
240 - if (!defined('FLUENT_BOARDS_PRO')) {
241 - $menuItems['get_pro'] = [
242 - 'key' => 'get_pro',
243 - 'label' => __('Get Pro', 'fluent-boards'),
244 - 'permalink' => 'https://fluentboards.com?utm_source=plugin&utm_medium=menu&utm_campaign=pro&utm_id=wp',
245 - 'class' => 'pro_link'
246 - ];
247 - }
248 -
249 - if ($isAdmin) {
250 218 $menuItems['help'] = [
251 219 'key' => 'help',
252 220 'label' => __('Community', 'fluent-boards'),
253 221 'target' => '_blank',
@@ -257,9 +225,9 @@
257 225
258 226 if ($isDiffUrl) {
259 227 $menuItems['front'] = [
260 228 'key' => 'front',
261 - 'label' => __('Frontend Portal', 'fluent-boards'),
229 + 'label' => __('Front Portal', 'fluent-boards'),
262 230 'target' => '_blank',
263 231 'permalink' => fluent_boards_page_url()
264 232 ];
265 233 }
@@ -331,37 +299,58 @@
331 299 }
332 300
333 301 $app = App::getInstance();
334 302
335 - $assets = $app['url.assets'];
303 + $slug = $app->config->get('app.slug');
336 304
337 - $slug = $app->config->get('app.slug');
305 + // Inject Vite HMR client in dev mode
306 + Vite::injectViteClient();
338 307
339 -// $isRtl = is_rtl();
340 - $adminAppCss = 'admin/admin.css';
341 -// if($isRtl) {
342 -// $adminAppCss = 'admin/admin-rtl.css';
343 -// }
344 - wp_enqueue_style(
345 - $slug . '_admin_app',
346 - $assets . $adminAppCss,
347 - [],
348 - FLUENT_BOARDS_PLUGIN_VERSION
349 - );
308 + $isRtl = fluent_boards_is_rtl();
309 + Vite::enqueueStyle($slug . '_admin_app', 'scss/admin.scss');
350 310
351 - 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 + }
352 324
353 - 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(
354 343 $slug . '_admin_app',
355 - $assets . 'admin/app.js',
344 + 'admin/app.js',
356 345 ['jquery'],
357 346 FLUENT_BOARDS_PLUGIN_VERSION,
358 347 true
359 348 );
360 349
361 - wp_enqueue_script(
350 + Vite::enqueueStaticScript(
362 351 $slug . '_global_admin',
363 - $assets . 'admin/global_admin.js',
352 + 'admin/global_admin.js',
364 353 [],
365 354 FLUENT_BOARDS_PLUGIN_VERSION,
366 355 true
367 356 );
@@ -381,8 +370,9 @@
381 370 $currentUser = get_user_by('ID', get_current_user_id());
382 371 $assets = $app['url.assets'];
383 372 $roleAndPermissions = $this->getRoleAndPermissions($currentUser->ID);
384 373 $onboardingValue = $this->getOnboardingValue();
374 + $generalSettings = fluent_boards_get_option('general_settings', []);
385 375
386 376 return apply_filters('fluent_boards/app_vars', [
387 377 'slug' => $slug = $app->config->get('app.slug'),
388 378 'nonce' => wp_create_nonce($slug),
@@ -390,13 +380,23 @@
390 380 'fluent_boards_file_upload_nonce' => wp_create_nonce('fluent_boards_file_upload_nonce'),
391 381 'ajaxurl' => admin_url('admin-ajax.php'),
392 382 'file_upload_limit' => $this->fileUploadLimit(),
393 383 'brand_logo' => $this->getMenuIcon(),
384 + 'business_name' => sanitize_text_field(Arr::get($generalSettings, 'business_name', '')),
394 385 'asset_url' => $assets,
395 386 'admin_url' => admin_url('admin.php'),
396 387 'fluent_crm_exists' => !defined('FLUENTCRM') ? false : true,
388 + 'fluent_support_exists' => !!defined('FLUENT_SUPPORT_VERSION'),
389 + 'can_manage_crm_contacts' => $this->canManageCrmContacts(),
397 390 'fluent_roadmap_exists' => !defined('FLUENT_ROADMAP') ? false : true,
398 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 + ],
399 399 'me' => [
400 400 'id' => $currentUser->ID,
401 401 'full_name' => trim($currentUser->first_name . ' ' . $currentUser->last_name),
402 402 'display_name' => $currentUser->display_name,
@@ -401,15 +401,19 @@
401 401 'full_name' => trim($currentUser->first_name . ' ' . $currentUser->last_name),
402 402 'display_name' => $currentUser->display_name,
403 403 'email' => $currentUser->user_email,
404 404 'photo' => fluent_boards_user_avatar($currentUser->user_email, $currentUser->display_name),
405 + 'logout_url' => wp_logout_url(),
405 406 'fluent_boards_role' => $roleAndPermissions['role'],
406 407 'fluent_boards_capabilities' => $roleAndPermissions['permissions'],
407 - '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)
408 410 ],
409 411 'base_url' => fluent_boards_page_url(),
410 412 'site_url' => site_url('/'),
411 - 'server_time' => (new DateTime('now', wp_timezone()))->format('Y-m-d H:i:s P'),
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'),
412 416 'utc_offset' => current_time('timestamp') - strtotime(gmdate('Y-m-d H:i:s')),
413 417 'trans' => TransStrings::getStrings(),
414 418 'is_new' => Board::count() == 0 ? 'yes' : 'no',
415 419 'is_onboarded' => $onboardingValue,
@@ -418,8 +422,10 @@
418 422 'is_beta' => defined('FLUENT_BOARDS_PRO_VERSION') && !defined('FLUENT_BOARDS_PRO_LIVE'),
419 423 'advanced_modules' => fluent_boards_get_pref_settings(),
420 424 'crm_base_url' => defined('FLUENTCRM') ? fluentcrm_menu_url_base() : '',
421 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()),
422 428 'wpContentCss' => add_query_arg(
423 429 'ver', get_bloginfo('version'),
424 430 site_url('/wp-includes/js/tinymce/skins/wordpress/wp-content.css')
425 431 ),
@@ -426,12 +432,19 @@
426 432 'dashiconsCss' => add_query_arg(
427 433 'ver', get_bloginfo('version'),
428 434 site_url('/wp-includes/css/dashicons.css')
429 435 ),
430 -// 'is_rtl' => is_rtl(),
436 + 'is_rtl' => fluent_boards_is_rtl(),
437 + 'board_menu_items' => BoardMenuHandler::getMenuItems(),
438 + 'reminder_types' => defined('FLUENT_BOARDS_PRO') ? Helper::taskReminderTypes() : [],
431 439 ]);
432 440 }
433 441
442 + protected function canManageCrmContacts()
443 + {
444 + return defined('FLUENTCRM') && \FluentCrm\App\Services\PermissionManager::currentUserCan('fcrm_manage_contacts');
445 + }
446 +
434 447 private function getOnboardingValue()
435 448 {
436 449 $onboarding = Meta::where('key', Constant::FBS_ONBOARDING)->first();
437 450 if ($onboarding) {
@@ -443,8 +456,19 @@
443 456 //
444 457 // return 'no';
445 458 }
446 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 +
447 471 /*
448 472 * TODO: This method should be moved to PermissionManager and Helper . Task for Masiur.
449 473 */
450 474 protected function getRoleAndPermissions($userId): array
@@ -511,8 +535,9 @@
511 535 add_filter('admin_footer_text', function ($content) {
512 536 $url = '#';
513 537 return '';
514 538
539 + // translators: %s is the URL for FluentBoards link
515 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>';
516 541 });
517 542
518 543 add_filter('update_footer', function ($text) {
@@ -520,25 +545,15 @@
520 545 });
521 546 }
522 547
523 548 /**
524 - * Retrieves the maximum upload limit based on PHP and WordPress configurations.
549 + * Retrieves the Fluent Boards upload limit exposed to the admin app.
525 550 *
526 - * This method calculates and returns the minimum value among the following:
527 - * 1. The PHP 'upload_max_filesize' configuration.
528 - * 2. The PHP 'post_max_size' configuration.
529 - * 3. The WordPress maximum upload size limit using the 'wp_max_upload_size' function.
530 - *
531 - * @return int The minimum of the mentioned upload size limits in bytes.
551 + * @return int Upload limit in bytes.
532 552 */
533 553 public function fileUploadLimit()
534 554 {
535 - // Calculate the minimum of 'upload_max_filesize', 'post_max_size', and WordPress maximum upload size.
536 - return min(
537 - wp_convert_hr_to_bytes(ini_get('upload_max_filesize')),
538 - wp_convert_hr_to_bytes(ini_get('post_max_size')),
539 - wp_max_upload_size()
540 - );
555 + return (new \FluentBoards\App\Services\UploadService())->getFileUploadLimit();
541 556 }
542 557
543 558 public function getInlineScript()
544 559 {
@@ -574,7 +589,19 @@
574 589 if ( isLodash() ) {
575 590 _.noConflict();
576 591 }
577 592 ";
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 + ]);
578 605 }
579 606
580 607 }