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

575 lines 20.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\TransStrings;
11 use FluentBoards\Framework\Support\Arr;
12 use FluentBoards\Framework\Support\Collection;
13 use FluentBoards\App\Services\PermissionManager;
14 use FluentBoards\Framework\Support\DateTime;
15
16 class AdminMenuHandler
17 {
18
19 public function register()
20 {
21 add_action('admin_menu', [$this, 'add'], 11);
22
23 add_filter('fluent_crm/core_menu_items', function ($items) {
24 if (PermissionManager::userHasAnyBoardAccess()) {
25 $items['fluent-boards'] = [
26 'key' => 'fluent-boards',
27 'label' => __('Fluent Boards', 'fluent-boards'),
28 'permalink' => admin_url('admin.php?page=fluent-boards#/')
29 ];
30 }
31 return $items;
32 });
33
34 add_action('admin_enqueue_scripts', function () {
35 if (!isset($_REQUEST['page']) || $_REQUEST['page'] !== 'fluent-boards') {
36 return;
37 }
38
39 $this->enqueueAssets();
40 });
41 }
42
43 public function add()
44 {
45 if (!PermissionManager::userHasAnyBoardAccess()) {
46 return;
47 }
48
49 $user = get_user_by('ID', get_current_user_id());
50
51 if (current_user_can('manage_options')) {
52 $capability = 'manage_options';
53 } else {
54 $roles = array_values((array)$user->roles);
55 $capability = Arr::get($roles, 0);
56 }
57
58 $settings = fluent_boards_get_pref_settings();
59
60 if (defined('FLUENTCRM') && \FluentCrm\App\Services\PermissionManager::currentUserPermissions() && Arr::get($settings, 'menu_settings.in_fluent_crm') === 'yes') {
61 add_submenu_page(
62 'fluentcrm-admin', //$parent_slug
63 __('Fluent Boards', 'fluent-boards'), //$page_title
64 __('Fluent Boards', 'fluent-boards'), //$menu_title
65 $capability,
66 'fluent-boards', //$menu_slug
67 [$this, 'render'],
68 );
69 return;
70 }
71
72 add_menu_page(
73 __('Fluent Boards', 'fluent-boards'),
74 __('Fluent Boards', 'fluent-boards'),
75 $capability,
76 'fluent-boards',
77 [$this, 'render'],
78 $this->getMenuIcon(),
79 Arr::get($settings, 'menu_settings.menu_position', 3)
80 );
81
82 add_submenu_page(
83 'fluent-boards',
84 __('Dashboard', 'fluent-boards'),
85 __('Dashboard', 'fluent-boards'),
86 $capability,
87 'fluent-boards',
88 [$this, 'render']
89 );
90
91 add_submenu_page(
92 'fluent-boards',
93 __('Boards', 'fluent-boards'),
94 __('Boards', 'fluent-boards'),
95 $capability,
96 'fluent-boards#/boards',
97 [$this, 'render']
98 );
99
100 do_action('fluent_boards/after_core_menu_items', $permissions = [], $isAdmin = true);
101
102 add_submenu_page(
103 'fluent-boards',
104 __('Reports', 'fluent-boards'),
105 __('Reports', 'fluent-boards'),
106 $capability,
107 'fluent-boards#/reports',
108 [$this, 'render']
109 );
110
111 add_submenu_page(
112 'fluent-boards',
113 __('Settings', 'fluent-boards'),
114 __('Settings', 'fluent-boards'),
115 'manage_options',
116 'fluent-boards#/settings/members-role',
117 [$this, 'render']
118 );
119 }
120
121 public function render()
122 {
123 $this->changeFooter();
124
125 $config = App::getInstance('config');
126
127 $name = $config->get('app.name');
128 $app = App::getInstance();
129 $assets = $app['url.assets'];
130 $slug = $config->get('app.slug');
131 $baseUrl = fluent_boards_page_url();
132
133 $this->updateDatabase();
134
135
136 do_action('fluent_boards/rendering_app');
137
138
139 App::make('view')->render('admin.menu', [
140 'name' => $name,
141 'slug' => $slug,
142 'menuItems' => $this->getMenuItems($app),
143 'baseUrl' => $baseUrl,
144 'logo' => apply_filters('fluent_boards/app_logo', $assets . 'images/logo.svg'),
145 'icon' => apply_filters('fluent_boards/app_icon', $assets . 'images/icon.svg'),
146 'is_new' => Board::count() == 0 ? 'yes' : 'no',
147 'is_onboarded' => $this->getOnboardingValue()
148 ]);
149 }
150
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 public function getMenuItems($app)
192 {
193 $config = $app->config;
194 $slug = $config->get('app.slug');
195
196 $baseUrl = fluent_boards_page_url();
197
198 $isDiffUrl = false;
199
200 if (is_admin()) {
201 $adminUrl = admin_url('admin.php?page=fluent-boards#/');
202
203 if ($adminUrl != $baseUrl) {
204 $isDiffUrl = true;
205 $baseUrl = $adminUrl;
206 }
207 }
208
209 $menuItems = [
210 'dashboard' => [
211 'key' => 'dashboard',
212 'label' => __('Dashboard', 'fluent-boards'),
213 'permalink' => $baseUrl,
214 ],
215 'boards' => [
216 'key' => 'boards',
217 'label' => __('Boards', 'fluent-boards'),
218 'permalink' => $baseUrl . 'boards'
219 ]
220 ];
221
222 $menuItems = apply_filters('fluent_boards/core_menu_items', $menuItems);
223
224 $menuItems['reports'] = [
225 'key' => 'reports',
226 'label' => __('Reports', 'fluent-boards'),
227 'permalink' => $baseUrl . 'reports'
228 ];
229
230 $isAdmin = PermissionManager::isAdmin();
231
232 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 $menuItems['help'] = [
251 'key' => 'help',
252 'label' => __('Community', 'fluent-boards'),
253 'target' => '_blank',
254 'permalink' => 'https://community.wpmanageninja.com/portal/community/fluent-boards/home'
255 ];
256 }
257
258 if ($isDiffUrl) {
259 $menuItems['front'] = [
260 'key' => 'front',
261 'label' => __('Frontend Portal', 'fluent-boards'),
262 'target' => '_blank',
263 'permalink' => fluent_boards_page_url()
264 ];
265 }
266
267 $menuItems = apply_filters('fluent_boards/menu_items', $menuItems);
268
269 return array_values($menuItems);
270 }
271
272 public function enqueueAssets()
273 {
274 if (!PermissionManager::hasAppAccess()) {
275 return;
276 }
277
278 add_action('wp_print_scripts', function () {
279
280 $isSkip = apply_filters('fluent_boards/skip_no_conflict', false);
281
282 if ($isSkip) {
283 return;
284 }
285
286 global $wp_scripts;
287 if (!$wp_scripts) {
288 return;
289 }
290
291 $approvedSlugs = apply_filters('fluent_boards/asset_listed_slugs', [
292 '\/fluent-crm\/'
293 ]);
294
295 $approvedSlugs[] = '\/fluent-boards\/';
296
297 $approvedSlugs = array_unique($approvedSlugs);
298
299 $approvedSlugs = implode('|', $approvedSlugs);
300
301 $pluginUrl = plugins_url();
302
303 $pluginUrl = str_replace(['http:', 'https:'], '', $pluginUrl);
304
305 foreach ($wp_scripts->queue as $script) {
306 if (empty($wp_scripts->registered[$script]) || empty($wp_scripts->registered[$script]->src)) {
307 continue;
308 }
309
310 $src = $wp_scripts->registered[$script]->src;
311 $isMatched = (strpos($src, $pluginUrl) !== false) && !preg_match('/' . $approvedSlugs . '/', $src);
312 if (!$isMatched) {
313 continue;
314 }
315 wp_dequeue_script($wp_scripts->registered[$script]->handle);
316 }
317 });
318
319 if (function_exists('wp_enqueue_media')) {
320 // Editor default styles.
321 add_filter('user_can_richedit', '__return_true');
322 if (is_admin()) {
323 wp_tinymce_inline_scripts();
324 }
325 wp_enqueue_editor();
326 wp_enqueue_script('thickbox');
327 wp_enqueue_script('editor');
328 }
329 if (function_exists('wp_enqueue_media')) {
330 wp_enqueue_media();
331 }
332
333 $app = App::getInstance();
334
335 $assets = $app['url.assets'];
336
337 $slug = $app->config->get('app.slug');
338
339 wp_enqueue_style(
340 $slug . '_admin_app',
341 $assets . 'admin/admin.css',
342 [],
343 FLUENT_BOARDS_PLUGIN_VERSION
344 );
345
346 do_action($slug . '_loading_app');
347
348 wp_enqueue_script(
349 $slug . '_admin_app',
350 $assets . 'admin/app.js',
351 ['jquery'],
352 FLUENT_BOARDS_PLUGIN_VERSION,
353 true
354 );
355
356 wp_enqueue_script(
357 $slug . '_global_admin',
358 $assets . 'admin/global_admin.js',
359 [],
360 FLUENT_BOARDS_PLUGIN_VERSION,
361 true
362 );
363 /*
364 * This script only for resolve the conflict of lodash and underscore js
365 * Resolved the issue of media uploader specially for image upload
366 */
367 wp_add_inline_script($slug . '_global_admin', $this->getInlineScript(), 'after');
368
369 wp_localize_script($slug . '_admin_app', 'fluentAddonVars', $this->getAddonVars($app));
370
371 do_action('fluent_boards/after_enqueue_assets', $app);
372 }
373
374 public function getAddonVars($app)
375 {
376 $currentUser = get_user_by('ID', get_current_user_id());
377 $assets = $app['url.assets'];
378 $roleAndPermissions = $this->getRoleAndPermissions($currentUser->ID);
379 $onboardingValue = $this->getOnboardingValue();
380
381 return apply_filters('fluent_boards/app_vars', [
382 'slug' => $slug = $app->config->get('app.slug'),
383 'nonce' => wp_create_nonce($slug),
384 'rest' => $this->getRestInfo($app),
385 'fluent_boards_file_upload_nonce' => wp_create_nonce('fluent_boards_file_upload_nonce'),
386 'ajaxurl' => admin_url('admin-ajax.php'),
387 'file_upload_limit' => $this->fileUploadLimit(),
388 'brand_logo' => $this->getMenuIcon(),
389 'asset_url' => $assets,
390 'admin_url' => admin_url('admin.php'),
391 'fluent_crm_exists' => !defined('FLUENTCRM') ? false : true,
392 'fluent_roadmap_exists' => !defined('FLUENT_ROADMAP') ? false : true,
393 'has_pro' => !!defined('FLUENT_BOARDS_PRO_VERSION'),
394 'me' => [
395 'id' => $currentUser->ID,
396 'full_name' => trim($currentUser->first_name . ' ' . $currentUser->last_name),
397 'display_name' => $currentUser->display_name,
398 'email' => $currentUser->user_email,
399 'photo' => fluent_boards_user_avatar($currentUser->user_email, $currentUser->display_name),
400 'fluent_boards_role' => $roleAndPermissions['role'],
401 'fluent_boards_capabilities' => $roleAndPermissions['permissions'],
402 'is_wp_admin' => user_can($currentUser->ID, 'manage_options') ? 'yes' : 'no'
403 ],
404 'base_url' => fluent_boards_page_url(),
405 'site_url' => site_url('/'),
406 'server_time' => (new DateTime('now', wp_timezone()))->format('Y-m-d H:i:s P'),
407 'utc_offset' => current_time('timestamp') - strtotime(gmdate('Y-m-d H:i:s')),
408 'trans' => TransStrings::getStrings(),
409 'is_new' => Board::count() == 0 ? 'yes' : 'no',
410 'is_onboarded' => $onboardingValue,
411 'render_in' => is_admin() ? 'admin' : 'front',
412 'dashboard_notices' => apply_filters('fluent_boards/dashboard_notices', []),
413 'is_beta' => defined('FLUENT_BOARDS_PRO_VERSION') && !defined('FLUENT_BOARDS_PRO_LIVE'),
414 'advanced_modules' => fluent_boards_get_pref_settings(),
415 'crm_base_url' => defined('FLUENTCRM') ? fluentcrm_menu_url_base() : '',
416 'start_of_week' => intval(get_option('start_of_week', 0)),
417 'wpContentCss' => add_query_arg(
418 'ver', get_bloginfo('version'),
419 site_url('/wp-includes/js/tinymce/skins/wordpress/wp-content.css')
420 ),
421 'dashiconsCss' => add_query_arg(
422 'ver', get_bloginfo('version'),
423 site_url('/wp-includes/css/dashicons.css')
424 ),
425 ]);
426 }
427
428 private function getOnboardingValue()
429 {
430 $onboarding = Meta::where('key', Constant::FBS_ONBOARDING)->first();
431 if ($onboarding) {
432 return $onboarding->value;
433 }
434 // if (Board::first()) {
435 // return 'yes';
436 // }
437 //
438 // return 'no';
439 }
440
441 /*
442 * TODO: This method should be moved to PermissionManager and Helper . Task for Masiur.
443 */
444 protected function getRoleAndPermissions($userId): array
445 {
446 $role = 'fluent_boards_admin';
447 $boardsWithPermissions = Relation::query()->where('foreign_id', $userId)
448 ->where('object_type', Constant::OBJECT_TYPE_BOARD_USER)
449 ->get();
450 $boardUserCollection = Collection::make($boardsWithPermissions);
451
452 if (PermissionManager::isFluentBoardsAdmin($userId)) {
453 $role = 'fluent_boards_admin';
454 } elseif (user_can($userId, 'manage_options')) {
455 $role = 'wordpress_admin';
456 } else {
457 $role = 'member';
458 }
459
460 $permissions = [];
461 foreach ($boardUserCollection as $boardWithPermission) {
462 $permissions[] = [
463 'board_id' => $boardWithPermission->object_id,
464 'role' => $this->boardUserRole($boardWithPermission),
465 'preferences' => $boardWithPermission->preferences,
466 'permissions' => [],
467 ];
468 }
469
470 return [
471 'role' => $role,
472 'permissions' => $permissions,
473 ];
474 }
475
476 protected function boardUserRole($boardWithPermission)
477 {
478 return $boardWithPermission['settings']['is_admin'] ? 'board_admin' : (Arr::has($boardWithPermission, 'settings.is_viewer_only') && $boardWithPermission['settings']['is_viewer_only'] ? 'board_viewer' : 'board_member');
479 }
480
481 protected function getRestInfo($app)
482 {
483 $ns = $app->config->get('app.rest_namespace');
484 $ver = $app->config->get('app.rest_version');
485
486 return [
487 'base_url' => esc_url_raw(rest_url()),
488 'url' => rest_url($ns . '/' . $ver),
489 'nonce' => wp_create_nonce('wp_rest'),
490 'namespace' => $ns,
491 'version' => $ver,
492 ];
493 }
494
495 protected function getMenuIcon()
496 {
497 /*
498 * Left Sidebar Menu Icon
499 */
500 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>');
501 }
502
503 public function changeFooter()
504 {
505 add_filter('admin_footer_text', function ($content) {
506 $url = '#';
507 return '';
508
509 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>';
510 });
511
512 add_filter('update_footer', function ($text) {
513 return FLUENT_BOARDS_PLUGIN_VERSION;
514 });
515 }
516
517 /**
518 * Retrieves the maximum upload limit based on PHP and WordPress configurations.
519 *
520 * This method calculates and returns the minimum value among the following:
521 * 1. The PHP 'upload_max_filesize' configuration.
522 * 2. The PHP 'post_max_size' configuration.
523 * 3. The WordPress maximum upload size limit using the 'wp_max_upload_size' function.
524 *
525 * @return int The minimum of the mentioned upload size limits in bytes.
526 */
527 public function fileUploadLimit()
528 {
529 // Calculate the minimum of 'upload_max_filesize', 'post_max_size', and WordPress maximum upload size.
530 return min(
531 wp_convert_hr_to_bytes(ini_get('upload_max_filesize')),
532 wp_convert_hr_to_bytes(ini_get('post_max_size')),
533 wp_max_upload_size()
534 );
535 }
536
537 public function getInlineScript()
538 {
539 return "
540 function isLodash () {
541
542 let isLodash = false;
543
544 // If _ is defined and the function _.forEach exists then we know underscore OR lodash are in place
545 if ( 'undefined' != typeof( _ ) && 'function' == typeof( _.forEach ) ) {
546
547 // A small sample of some of the functions that exist in lodash but not underscore
548 const funcs = [ 'get', 'set', 'at', 'cloneDeep' ];
549
550 // Simplest if assume exists to start
551 isLodash = true;
552
553 funcs.forEach( function ( func ) {
554 // If just one of the functions do not exist, then not lodash
555 isLodash = ( 'function' != typeof( _[ func ] ) ) ? false : isLodash;
556 } );
557 }
558
559 if ( isLodash ) {
560 // We know that lodash is loaded in the _ variable
561 return true;
562 } else {
563 // We know that lodash is NOT loaded
564 return false;
565 }
566 };
567
568 if ( isLodash() ) {
569 _.noConflict();
570 }
571 ";
572 }
573
574 }
575