'fluent-boards', 'label' => __('Boards', 'fluent-boards'), 'permalink' => admin_url('admin.php?page=fluent-boards#/') ]; } return $items; }); add_action('admin_enqueue_scripts', function () { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Checking admin page context for asset enqueuing, no data modification if (!isset($_REQUEST['page']) || $_REQUEST['page'] !== 'fluent-boards') { return; } $this->enqueueAssets(); }); add_filter('fluent_crm/sidebar_core_menu_items', function($menuItems, $permissions) { $settings = fluent_boards_get_pref_settings(); if (Arr::get($settings, 'menu_settings.in_fluent_crm') === 'yes') { $menuItems[] = [ 'key' => 'fluent-boards', 'page_title' => __('Fluent Boards', 'fluent-boards'), 'menu_title' => __('Fluent Boards', 'fluent-boards'), 'capability' => 'manage_options', 'uri' => admin_url('admin.php?page=fluent-boards') ]; } return $menuItems; }, 10, 2); } public function add() { if (!PermissionManager::userHasAnyBoardAccess()) { return; } $user = get_user_by('ID', get_current_user_id()); if (current_user_can('manage_options')) { $capability = 'manage_options'; } else { $roles = array_values((array)$user->roles); $capability = Arr::get($roles, 0); } $settings = fluent_boards_get_pref_settings(); if (defined('FLUENTCRM') && \FluentCrm\App\Services\PermissionManager::currentUserPermissions() && Arr::get($settings, 'menu_settings.in_fluent_crm') === 'yes') { add_submenu_page( 'fluentcrm-admin', //$parent_slug __('Fluent Boards', 'fluent-boards'), //$page_title __('Fluent Boards', 'fluent-boards'), //$menu_title $capability, 'fluent-boards', //$menu_slug [$this, 'render'], ); return; } add_menu_page( __('Fluent Boards', 'fluent-boards'), __('Fluent Boards', 'fluent-boards'), $capability, 'fluent-boards', [$this, 'render'], $this->getMenuIcon(), Arr::get($settings, 'menu_settings.menu_position', 3) ); add_submenu_page( 'fluent-boards', __('Dashboard', 'fluent-boards'), __('Dashboard', 'fluent-boards'), $capability, 'fluent-boards', [$this, 'render'] ); add_submenu_page( 'fluent-boards', __('Boards', 'fluent-boards'), __('Boards', 'fluent-boards'), $capability, 'fluent-boards#/boards', [$this, 'render'] ); do_action('fluent_boards/after_core_menu_items', $permissions = [], $isAdmin = true); add_submenu_page( 'fluent-boards', __('Reports', 'fluent-boards'), __('Reports', 'fluent-boards'), $capability, 'fluent-boards#/reports', [$this, 'render'] ); add_submenu_page( 'fluent-boards', __('Settings', 'fluent-boards'), __('Settings', 'fluent-boards'), 'manage_options', 'fluent-boards#/settings/members-role', [$this, 'render'] ); } public function render() { $this->changeFooter(); $config = App::getInstance('config'); $name = $config->get('app.name'); $app = App::getInstance(); $assets = $app['url.assets']; $slug = $config->get('app.slug'); $baseUrl = fluent_boards_page_url(); do_action('fluent_boards/rendering_app'); // For subtask sync UpdateHandler::maybeSubtaskGroupSync(); App::make('view')->render('admin.menu', [ 'name' => $name, 'slug' => $slug, 'menuItems' => $this->getMenuItems($app), 'baseUrl' => $baseUrl, 'logo' => apply_filters('fluent_boards/app_logo', $assets . 'images/logo.svg'), 'icon' => apply_filters('fluent_boards/app_icon', $assets . 'images/icon.svg'), 'is_new' => Board::count() == 0 ? 'yes' : 'no', 'is_onboarded' => $this->getOnboardingValue() ]); } public function getMenuItems($app) { $config = $app->config; $slug = $config->get('app.slug'); $baseUrl = fluent_boards_page_url(); $isDiffUrl = false; if (is_admin()) { $adminUrl = admin_url('admin.php?page=fluent-boards#/'); if ($adminUrl != $baseUrl) { $isDiffUrl = true; $baseUrl = $adminUrl; } } $menuItems = [ 'dashboard' => [ 'key' => 'dashboard', 'label' => __('Dashboard', 'fluent-boards'), 'permalink' => $baseUrl, ], 'boards' => [ 'key' => 'boards', 'label' => __('Boards', 'fluent-boards'), 'permalink' => $baseUrl . 'boards' ] ]; $menuItems = apply_filters('fluent_boards/core_menu_items', $menuItems); $menuItems['reports'] = [ 'key' => 'reports', 'label' => __('Reports', 'fluent-boards'), 'permalink' => $baseUrl . 'reports' ]; $isAdmin = PermissionManager::isAdmin(); if ($isAdmin) { $menuItems['settings'] = [ 'key' => 'settings', 'label' => __('Settings', 'fluent-boards'), 'permalink' => $baseUrl . 'settings/members-role' ]; } if (!defined('FLUENT_BOARDS_PRO')) { $menuItems['get_pro'] = [ 'key' => 'get_pro', 'label' => __('Get Pro', 'fluent-boards'), 'permalink' => 'https://fluentboards.com?utm_source=plugin&utm_medium=menu&utm_campaign=pro&utm_id=wp', 'class' => 'pro_link' ]; } if ($isAdmin) { $menuItems['help'] = [ 'key' => 'help', 'label' => __('Community', 'fluent-boards'), 'target' => '_blank', 'permalink' => 'https://community.wpmanageninja.com/portal/community/fluent-boards/home' ]; } if ($isDiffUrl) { $menuItems['front'] = [ 'key' => 'front', 'label' => __('Frontend Portal', 'fluent-boards'), 'target' => '_blank', 'permalink' => fluent_boards_page_url() ]; } $menuItems = apply_filters('fluent_boards/menu_items', $menuItems); return array_values($menuItems); } public function enqueueAssets() { if (!PermissionManager::hasAppAccess()) { return; } add_action('wp_print_scripts', function () { $isSkip = apply_filters('fluent_boards/skip_no_conflict', false); if ($isSkip) { return; } global $wp_scripts; if (!$wp_scripts) { return; } $approvedSlugs = apply_filters('fluent_boards/asset_listed_slugs', [ '\/fluent-crm\/' ]); $approvedSlugs[] = '\/fluent-boards\/'; $approvedSlugs = array_unique($approvedSlugs); $approvedSlugs = implode('|', $approvedSlugs); $pluginUrl = plugins_url(); $pluginUrl = str_replace(['http:', 'https:'], '', $pluginUrl); foreach ($wp_scripts->queue as $script) { if (empty($wp_scripts->registered[$script]) || empty($wp_scripts->registered[$script]->src)) { continue; } $src = $wp_scripts->registered[$script]->src; $isMatched = (strpos($src, $pluginUrl) !== false) && !preg_match('/' . $approvedSlugs . '/', $src); if (!$isMatched) { continue; } wp_dequeue_script($wp_scripts->registered[$script]->handle); } }); if (function_exists('wp_enqueue_media')) { // Editor default styles. add_filter('user_can_richedit', '__return_true'); if (is_admin()) { wp_tinymce_inline_scripts(); } wp_enqueue_editor(); wp_enqueue_script('thickbox'); wp_enqueue_script('editor'); } if (function_exists('wp_enqueue_media')) { wp_enqueue_media(); } $app = App::getInstance(); $slug = $app->config->get('app.slug'); // Inject Vite HMR client in dev mode Vite::injectViteClient(); $isRtl = is_rtl(); $adminAppCss = $isRtl ? 'scss/admin.scss' : 'scss/admin.scss'; Vite::enqueueStyle($slug . '_admin_app', $adminAppCss); // In production, load the RTL version if needed if ($isRtl && !Vite::underDevelopment()) { $assets = $app['url.assets']; wp_enqueue_style( $slug . '_admin_app_rtl', $assets . 'admin/admin-rtl.css', [$slug . '_admin_app'], FLUENT_BOARDS_PLUGIN_VERSION ); } // Enqueue merged chunk CSS (Vue SFC styles) in production if (!Vite::underDevelopment()) { $assets = $app['url.assets']; $styleCssPath = FLUENT_BOARDS_PLUGIN_PATH . 'assets/admin/style.css'; if (file_exists($styleCssPath)) { wp_enqueue_style( $slug . '_vite_style', $assets . 'admin/style.css', [$slug . '_admin_app'], FLUENT_BOARDS_PLUGIN_VERSION ); } } do_action('fluent-boards_loading_app'); Vite::enqueueScript( $slug . '_admin_app', 'admin/app.js', ['jquery'], FLUENT_BOARDS_PLUGIN_VERSION, true ); Vite::enqueueStaticScript( $slug . '_global_admin', 'admin/global_admin.js', [], FLUENT_BOARDS_PLUGIN_VERSION, true ); /* * This script only for resolve the conflict of lodash and underscore js * Resolved the issue of media uploader specially for image upload */ wp_add_inline_script($slug . '_global_admin', $this->getInlineScript(), 'after'); wp_localize_script($slug . '_admin_app', 'fluentAddonVars', $this->getAddonVars($app)); do_action('fluent_boards/after_enqueue_assets', $app); } public function getAddonVars($app) { $currentUser = get_user_by('ID', get_current_user_id()); $assets = $app['url.assets']; $roleAndPermissions = $this->getRoleAndPermissions($currentUser->ID); $onboardingValue = $this->getOnboardingValue(); return apply_filters('fluent_boards/app_vars', [ 'slug' => $slug = $app->config->get('app.slug'), 'nonce' => wp_create_nonce($slug), 'rest' => $this->getRestInfo($app), 'fluent_boards_file_upload_nonce' => wp_create_nonce('fluent_boards_file_upload_nonce'), 'ajaxurl' => admin_url('admin-ajax.php'), 'file_upload_limit' => $this->fileUploadLimit(), 'brand_logo' => $this->getMenuIcon(), 'asset_url' => $assets, 'admin_url' => admin_url('admin.php'), 'fluent_crm_exists' => !defined('FLUENTCRM') ? false : true, 'fluent_roadmap_exists' => !defined('FLUENT_ROADMAP') ? false : true, 'has_pro' => !!defined('FLUENT_BOARDS_PRO_VERSION'), 'me' => [ 'id' => $currentUser->ID, 'full_name' => trim($currentUser->first_name . ' ' . $currentUser->last_name), 'display_name' => $currentUser->display_name, 'email' => $currentUser->user_email, 'photo' => fluent_boards_user_avatar($currentUser->user_email, $currentUser->display_name), 'fluent_boards_role' => $roleAndPermissions['role'], 'fluent_boards_capabilities' => $roleAndPermissions['permissions'], 'is_wp_admin' => user_can($currentUser->ID, 'manage_options') ? 'yes' : 'no', 'can_create_board' => PermissionManager::userHasBoardCreationPermission($currentUser->ID) ], 'base_url' => fluent_boards_page_url(), 'site_url' => site_url('/'), // 'server_time' => (new DateTime('now'))->format('Y-m-d H:i:s P'), // Server's default timezone 'server_time' => (new DateTime('now', wp_timezone()))->format('Y-m-d H:i:s P'), // wordpress site time 'server_time_zone' => (new DateTime('now', wp_timezone()))->format( 'P'), 'utc_offset' => current_time('timestamp') - strtotime(gmdate('Y-m-d H:i:s')), 'trans' => TransStrings::getStrings(), 'is_new' => Board::count() == 0 ? 'yes' : 'no', 'is_onboarded' => $onboardingValue, 'render_in' => is_admin() ? 'admin' : 'front', 'dashboard_notices' => apply_filters('fluent_boards/dashboard_notices', []), 'is_beta' => defined('FLUENT_BOARDS_PRO_VERSION') && !defined('FLUENT_BOARDS_PRO_LIVE'), 'advanced_modules' => fluent_boards_get_pref_settings(), 'crm_base_url' => defined('FLUENTCRM') ? fluentcrm_menu_url_base() : '', 'start_of_week' => intval(get_option('start_of_week', 0)), 'time_format' => get_option('time_format', 'g:i'), 'priorities' => apply_filters('fluent_boards/task_priorities', $this->getDefaultPriorities()), 'wpContentCss' => add_query_arg( 'ver', get_bloginfo('version'), site_url('/wp-includes/js/tinymce/skins/wordpress/wp-content.css') ), 'dashiconsCss' => add_query_arg( 'ver', get_bloginfo('version'), site_url('/wp-includes/css/dashicons.css') ), 'is_rtl' => is_rtl(), 'task_tabs' => apply_filters('fluent_boards/task_tabs', $this->getDefaultTaskTabs()), 'board_menu_items' => BoardMenuHandler::getMenuItems(), 'reminder_types' => defined('FLUENT_BOARDS_PRO') ? Helper::taskReminderTypes() : [], ]); } private function getOnboardingValue() { $onboarding = Meta::where('key', Constant::FBS_ONBOARDING)->first(); if ($onboarding) { return $onboarding->value; } // if (Board::first()) { // return 'yes'; // } // // return 'no'; } private function getDefaultTaskTabs() { return [ 'all' => [ 'key' => 'all', 'label' => __('All', 'fluent-boards'), 'component' => 'task-all-comments-and-activities' ], 'comment' => [ 'key' => 'comment', 'label' => __('Comments', 'fluent-boards'), 'component' => 'task-comments' ], 'activity' => [ 'key' => 'activity', 'label' => __('Activities', 'fluent-boards'), 'component' => 'task-activities' ] ]; } public function getDefaultPriorities() { return [ 'low' => __('Low', 'fluent-boards'), 'medium' => __('Medium', 'fluent-boards'), 'high' => __('High', 'fluent-boards') ]; } /* * TODO: This method should be moved to PermissionManager and Helper . Task for Masiur. */ protected function getRoleAndPermissions($userId): array { $role = 'fluent_boards_admin'; $boardsWithPermissions = Relation::query()->where('foreign_id', $userId) ->where('object_type', Constant::OBJECT_TYPE_BOARD_USER) ->get(); $boardUserCollection = Collection::make($boardsWithPermissions); if (PermissionManager::isFluentBoardsAdmin($userId)) { $role = 'fluent_boards_admin'; } elseif (user_can($userId, 'manage_options')) { $role = 'wordpress_admin'; } else { $role = 'member'; } $permissions = []; foreach ($boardUserCollection as $boardWithPermission) { $permissions[] = [ 'board_id' => $boardWithPermission->object_id, 'role' => $this->boardUserRole($boardWithPermission), 'preferences' => $boardWithPermission->preferences, 'permissions' => [], ]; } return [ 'role' => $role, 'permissions' => $permissions, ]; } protected function boardUserRole($boardWithPermission) { return $boardWithPermission['settings']['is_admin'] ? 'board_admin' : (Arr::has($boardWithPermission, 'settings.is_viewer_only') && $boardWithPermission['settings']['is_viewer_only'] ? 'board_viewer' : 'board_member'); } protected function getRestInfo($app) { $ns = $app->config->get('app.rest_namespace'); $ver = $app->config->get('app.rest_version'); return [ 'base_url' => esc_url_raw(rest_url()), 'url' => rest_url($ns . '/' . $ver), 'nonce' => wp_create_nonce('wp_rest'), 'namespace' => $ns, 'version' => $ver, ]; } protected function getMenuIcon() { /* * Left Sidebar Menu Icon */ return 'data:image/svg+xml;base64,' . base64_encode(''); } public function changeFooter() { add_filter('admin_footer_text', function ($content) { $url = '#'; return ''; // translators: %s is the URL for FluentBoards link return sprintf(wp_kses(__('Thank you for using FluentBoards', 'fluent-boards'), ['a' => ['href' => []]]), esc_url($url)) . ''; }); add_filter('update_footer', function ($text) { return FLUENT_BOARDS_PLUGIN_VERSION; }); } /** * Retrieves the maximum upload limit based on PHP and WordPress configurations. * * This method calculates and returns the minimum value among the following: * 1. The PHP 'upload_max_filesize' configuration. * 2. The PHP 'post_max_size' configuration. * 3. The WordPress maximum upload size limit using the 'wp_max_upload_size' function. * * @return int The minimum of the mentioned upload size limits in bytes. */ public function fileUploadLimit() { // Calculate the minimum of 'upload_max_filesize', 'post_max_size', and WordPress maximum upload size. return min( wp_convert_hr_to_bytes(ini_get('upload_max_filesize')), wp_convert_hr_to_bytes(ini_get('post_max_size')), wp_max_upload_size() ); } public function getInlineScript() { return " function isLodash () { let isLodash = false; // If _ is defined and the function _.forEach exists then we know underscore OR lodash are in place if ( 'undefined' != typeof( _ ) && 'function' == typeof( _.forEach ) ) { // A small sample of some of the functions that exist in lodash but not underscore const funcs = [ 'get', 'set', 'at', 'cloneDeep' ]; // Simplest if assume exists to start isLodash = true; funcs.forEach( function ( func ) { // If just one of the functions do not exist, then not lodash isLodash = ( 'function' != typeof( _[ func ] ) ) ? false : isLodash; } ); } if ( isLodash ) { // We know that lodash is loaded in the _ variable return true; } else { // We know that lodash is NOT loaded return false; } }; if ( isLodash() ) { _.noConflict(); } "; } public function singleBoardRender() { $config = App::getInstance('config'); $slug = $config->get('app.slug'); App::make('view')->render('admin.single_board_shortcode', [ 'slug' => $slug, ]); } }