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

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