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

536 lines 19.1 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-crm'),
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 $caps = $user->allcaps;
50 // get the first key
51 $capability = key($caps);
52
53 $settings = fluent_boards_get_pref_settings();
54
55 if (defined('FLUENTCRM') && \FluentCrm\App\Services\PermissionManager::currentUserPermissions() && Arr::get($settings, 'menu_settings.in_fluent_crm') === 'yes') {
56 add_submenu_page(
57 'fluentcrm-admin', //$parent_slug
58 __('Fluent Boards', 'fluent-boards'), //$page_title
59 __('Fluent Boards', 'fluent-boards'), //$menu_title
60 $capability,
61 'fluent-boards', //$menu_slug
62 [$this, 'render'],
63 );
64 return;
65 }
66
67 add_menu_page(
68 __('Fluent Boards', 'fluent-boards'),
69 __('Fluent Boards', 'fluent-boards'),
70 $capability,
71 'fluent-boards',
72 [$this, 'render'],
73 $this->getMenuIcon(),
74 Arr::get($settings, 'menu_settings.menu_position', 3)
75 );
76
77 add_submenu_page(
78 'fluent-boards',
79 __('Dashboard', 'fluent-boards'),
80 __('Dashboard', 'fluent-boards'),
81 $capability,
82 'fluent-boards',
83 [$this, 'render']
84 );
85
86 add_submenu_page(
87 'fluent-boards',
88 __('Boards', 'fluent-boards'),
89 __('Boards', 'fluent-boards'),
90 $capability,
91 'fluent-boards#/boards',
92 [$this, 'render']
93 );
94
95 do_action('fluent_boards/after_core_menu_items', $permissions = [], $isAdmin = true);
96
97 add_submenu_page(
98 'fluent-boards',
99 __('Reports', 'fluent-boards'),
100 __('Reports', 'fluent-boards'),
101 $capability,
102 'fluent-boards#/reports',
103 [$this, 'render']
104 );
105
106 add_submenu_page(
107 'fluent-boards',
108 __('Settings', 'fluent-boards'),
109 __('Settings', 'fluent-boards'),
110 'manage_options',
111 'fluent-boards#/settings/members-role',
112 [$this, 'render']
113 );
114 }
115
116 public function render()
117 {
118 $this->changeFooter();
119
120 $config = App::getInstance('config');
121
122 $name = $config->get('app.name');
123 $app = App::getInstance();
124 $assets = $app['url.assets'];
125 $slug = $config->get('app.slug');
126 $baseUrl = fluent_boards_page_url();
127
128 $this->updateDatabase();
129
130
131 do_action('fluent_boards/rendering_app');
132
133
134 App::make('view')->render('admin.menu', [
135 'name' => $name,
136 'slug' => $slug,
137 'menuItems' => $this->getMenuItems($app),
138 'baseUrl' => $baseUrl,
139 'logo' => apply_filters('fluent_boards/app_logo', $assets . 'images/logo.svg'),
140 'icon' => apply_filters('fluent_boards/app_icon', $assets . 'images/icon.svg'),
141 'is_new' => Board::count() == 0 ? 'yes' : 'no',
142 'is_onboarded' => $this->getOnboardingValue()
143 ]);
144 }
145
146 private function updateDatabase($isForced = true)
147 {
148 global $wpdb;
149
150 $table = $wpdb->prefix . 'fbs_board_terms';
151 if ($wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $table)) != $table) {
152 return;
153 } else {
154 // change column type from int to decimal - for already installed sites
155 $column_name = 'position';
156 $preparedQuery = $wpdb->prepare("DESCRIBE $table %s", $column_name);
157 $dataType = $wpdb->get_row($preparedQuery);
158 if (strpos($dataType->Type, 'int') !== false) {
159 $sql = $wpdb->prepare(
160 "ALTER TABLE $table MODIFY $column_name decimal(10,2) NOT NULL DEFAULT '1' COMMENT 'Position: 1 = top/first, 2 = second/second in top, etc.';"
161 );
162 $wpdb->query($sql);
163 }
164 }
165 }
166
167 public function getMenuItems($app)
168 {
169 $config = $app->config;
170 $slug = $config->get('app.slug');
171
172 $baseUrl = fluent_boards_page_url();
173
174 $isDiffUrl = false;
175
176 if (is_admin()) {
177 $adminUrl = admin_url('admin.php?page=fluent-boards#/');
178
179 if ($adminUrl != $baseUrl) {
180 $isDiffUrl = true;
181 $baseUrl = $adminUrl;
182 }
183 }
184
185 $menuItems = [
186 'dashboard' => [
187 'key' => 'dashboard',
188 'label' => __('Dashboard', 'fluent-boards'),
189 'permalink' => $baseUrl,
190 ],
191 'boards' => [
192 'key' => 'boards',
193 'label' => __('Boards', 'fluent-boards'),
194 'permalink' => $baseUrl . 'boards'
195 ]
196 ];
197
198 $menuItems = apply_filters('fluent_boards/core_menu_items', $menuItems);
199
200 $menuItems['reports'] = [
201 'key' => 'reports',
202 'label' => __('Reports', 'fluent-boards'),
203 'permalink' => $baseUrl . 'reports'
204 ];
205
206 $isAdmin = PermissionManager::isAdmin();
207
208 if ($isAdmin) {
209 $menuItems['settings'] = [
210 'key' => 'settings',
211 'label' => __('Settings', 'fluent-boards'),
212 'permalink' => $baseUrl . 'settings/members-role'
213 ];
214 }
215
216 if (!defined('FLUENT_BOARDS_PRO')) {
217 $menuItems['get_pro'] = [
218 'key' => 'get_pro',
219 'label' => __('Get Pro', 'fluent-boards'),
220 'permalink' => 'https://fluentboards.com?utm_source=menu&utm_medium=plugin&utm_campaign=pro&utm_id=wp',
221 'class' => 'pro_link'
222 ];
223 }
224
225 if ($isAdmin) {
226 $menuItems['help'] = [
227 'key' => 'help',
228 'label' => __('Community', 'fluent-boards'),
229 'target' => '_blank',
230 'permalink' => 'https://community.wpmanageninja.com/portal/community/fluent-boards/home'
231 ];
232 }
233
234 if ($isDiffUrl) {
235 $menuItems['front'] = [
236 'key' => 'front',
237 'label' => __('Frontend Portal', 'fluent-boards'),
238 'target' => '_blank',
239 'permalink' => fluent_boards_page_url()
240 ];
241 }
242
243 $menuItems = apply_filters('fluent_boards/menu_items', $menuItems);
244
245 return array_values($menuItems);
246 }
247
248 public function enqueueAssets()
249 {
250 if (!PermissionManager::userHasAnyBoardAccess()) {
251 return;
252 }
253
254 add_action('wp_print_scripts', function () {
255
256 $isSkip = apply_filters('fluent_boards/skip_no_conflict', false);
257
258 if ($isSkip) {
259 return;
260 }
261
262 global $wp_scripts;
263 if (!$wp_scripts) {
264 return;
265 }
266
267 $approvedSlugs = apply_filters('fluent_boards/asset_listed_slugs', [
268 '\/fluent-crm\/'
269 ]);
270
271 $approvedSlugs[] = '\/fluent-boards\/';
272
273 $approvedSlugs = array_unique($approvedSlugs);
274
275 $approvedSlugs = implode('|', $approvedSlugs);
276
277 $pluginUrl = plugins_url();
278
279 $pluginUrl = str_replace(['http:', 'https:'], '', $pluginUrl);
280
281 foreach ($wp_scripts->queue as $script) {
282 if (empty($wp_scripts->registered[$script]) || empty($wp_scripts->registered[$script]->src)) {
283 continue;
284 }
285
286 $src = $wp_scripts->registered[$script]->src;
287 $isMatched = (strpos($src, $pluginUrl) !== false) && !preg_match('/' . $approvedSlugs . '/', $src);
288 if (!$isMatched) {
289 continue;
290 }
291 wp_dequeue_script($wp_scripts->registered[$script]->handle);
292 }
293 });
294
295 if (function_exists('wp_enqueue_media')) {
296 // Editor default styles.
297 add_filter('user_can_richedit', '__return_true');
298 if (is_admin()) {
299 wp_tinymce_inline_scripts();
300 }
301 wp_enqueue_editor();
302 wp_enqueue_script('thickbox');
303 wp_enqueue_script('editor');
304 }
305 if (function_exists('wp_enqueue_media')) {
306 wp_enqueue_media();
307 }
308
309 $app = App::getInstance();
310
311 $assets = $app['url.assets'];
312
313 $slug = $app->config->get('app.slug');
314
315 wp_enqueue_style(
316 $slug . '_admin_app',
317 $assets . 'admin/admin.css',
318 [],
319 FLUENT_BOARDS_PLUGIN_VERSION
320 );
321
322 do_action($slug . '_loading_app');
323
324 wp_enqueue_script(
325 $slug . '_admin_app',
326 $assets . 'admin/app.js',
327 ['jquery'],
328 FLUENT_BOARDS_PLUGIN_VERSION,
329 true
330 );
331
332 wp_enqueue_script(
333 $slug . '_global_admin',
334 $assets . 'admin/global_admin.js',
335 [],
336 FLUENT_BOARDS_PLUGIN_VERSION,
337 true
338 );
339 /*
340 * This script only for resolve the conflict of lodash and underscore js
341 * Resolved the issue of media uploader specially for image upload
342 */
343 wp_add_inline_script($slug . '_global_admin', $this->getInlineScript(), 'after');
344
345 wp_localize_script($slug . '_admin_app', 'fluentAddonVars', $this->getAddonVars($app));
346
347 do_action('fluent_boards/after_enqueue_assets', $app);
348 }
349
350 public function getAddonVars($app)
351 {
352 $currentUser = get_user_by('ID', get_current_user_id());
353 $assets = $app['url.assets'];
354 $roleAndPermissions = $this->getRoleAndPermissions($currentUser->ID);
355 $onboardingValue = $this->getOnboardingValue();
356
357 return apply_filters('fluent_boards/app_vars', [
358 'slug' => $slug = $app->config->get('app.slug'),
359 'nonce' => wp_create_nonce($slug),
360 'rest' => $this->getRestInfo($app),
361 'fluent_boards_file_upload_nonce' => wp_create_nonce('fluent_boards_file_upload_nonce'),
362 'ajaxurl' => admin_url('admin-ajax.php'),
363 'file_upload_limit' => $this->fileUploadLimit(),
364 'brand_logo' => $this->getMenuIcon(),
365 'asset_url' => $assets,
366 'admin_url' => admin_url('admin.php'),
367 'fluent_crm_exists' => !defined('FLUENTCRM') ? false : true,
368 'fluent_roadmap_exists' => !defined('FLUENTROADMAP') ? false : true,
369 'has_pro' => !!defined('FLUENT_BOARDS_PRO_VERSION'),
370 'me' => [
371 'id' => $currentUser->ID,
372 'full_name' => trim($currentUser->first_name . ' ' . $currentUser->last_name),
373 'display_name' => $currentUser->display_name,
374 'email' => $currentUser->user_email,
375 'photo' => fluent_boards_user_avatar($currentUser->user_email, $currentUser->display_name),
376 'fluent_boards_role' => $roleAndPermissions['role'],
377 'fluent_boards_capabilities' => $roleAndPermissions['permissions'],
378 'is_wp_admin' => user_can($currentUser->ID, 'manage_options') ? 'yes' : 'no'
379 ],
380 'base_url' => fluent_boards_page_url(),
381 'site_url' => site_url('/'),
382 'server_time' => current_time('mysql'),
383 'utc_offset' => current_time('timestamp') - strtotime(gmdate('Y-m-d H:i:s')),
384 'trans' => TransStrings::getStrings(),
385 'is_new' => Board::count() == 0 ? 'yes' : 'no',
386 'is_onboarded' => $onboardingValue,
387 'render_in' => is_admin() ? 'admin' : 'front',
388 'dashboard_notices' => apply_filters('fluent_boards/dashboard_notices', []),
389 'is_beta' => defined('FLUENT_BOARDS_PRO_VERSION') && !defined('FLUENT_BOARDS_PRO_LIVE'),
390 'advanced_modules' => fluent_boards_get_pref_settings(),
391 'crm_base_url' => defined('FLUENTCRM') ? fluentcrm_menu_url_base() : '',
392 ]);
393 }
394
395 private function getOnboardingValue()
396 {
397 $onboarding = Meta::where('key', Constant::FBS_ONBOARDING)->first();
398 if ($onboarding) {
399 return $onboarding->value;
400 }
401 // if (Board::first()) {
402 // return 'yes';
403 // }
404 //
405 // return 'no';
406 }
407
408 /*
409 * TODO: This method should be moved to PermissionManager and Helper . Task for Masiur.
410 */
411 protected function getRoleAndPermissions($userId): array
412 {
413 $role = 'fluent_boards_admin';
414 $boardsWithPermissions = Relation::query()->where('foreign_id', $userId)
415 ->where('object_type', Constant::OBJECT_TYPE_BOARD_USER)
416 ->get();
417 $boardUserCollection = Collection::make($boardsWithPermissions);
418
419 if (PermissionManager::isFluentBoardsAdmin($userId)) {
420 $role = 'fluent_boards_admin';
421 } elseif (user_can($userId, 'manage_options')) {
422 $role = 'wordpress_admin';
423 } else {
424 $role = 'member';
425 }
426
427 $permissions = [];
428 foreach ($boardUserCollection as $boardWithPermission) {
429 $permissions[] = [
430 'board_id' => $boardWithPermission->object_id,
431 'role' => $boardWithPermission['settings']['is_admin'] ? 'board_admin' : 'board_member',
432 'preferences' => $boardWithPermission->preferences,
433 'permissions' => [],
434 ];
435 }
436
437 return [
438 'role' => $role,
439 'permissions' => $permissions,
440 ];
441 }
442
443 protected function getRestInfo($app)
444 {
445 $ns = $app->config->get('app.rest_namespace');
446 $ver = $app->config->get('app.rest_version');
447
448 return [
449 'base_url' => esc_url_raw(rest_url()),
450 'url' => rest_url($ns . '/' . $ver),
451 'nonce' => wp_create_nonce('wp_rest'),
452 'namespace' => $ns,
453 'version' => $ver,
454 ];
455 }
456
457 protected function getMenuIcon()
458 {
459 /*
460 * Left Sidebar Menu Icon
461 */
462 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>');
463 }
464
465 public function changeFooter()
466 {
467 add_filter('admin_footer_text', function ($content) {
468 $url = '#';
469 return '';
470
471 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>';
472 });
473
474 add_filter('update_footer', function ($text) {
475 return FLUENT_BOARDS_PLUGIN_VERSION;
476 });
477 }
478
479 /**
480 * Retrieves the maximum upload limit based on PHP and WordPress configurations.
481 *
482 * This method calculates and returns the minimum value among the following:
483 * 1. The PHP 'upload_max_filesize' configuration.
484 * 2. The PHP 'post_max_size' configuration.
485 * 3. The WordPress maximum upload size limit using the 'wp_max_upload_size' function.
486 *
487 * @return int The minimum of the mentioned upload size limits in bytes.
488 */
489 public function fileUploadLimit()
490 {
491 // Calculate the minimum of 'upload_max_filesize', 'post_max_size', and WordPress maximum upload size.
492 return min(
493 wp_convert_hr_to_bytes(ini_get('upload_max_filesize')),
494 wp_convert_hr_to_bytes(ini_get('post_max_size')),
495 wp_max_upload_size()
496 );
497 }
498
499 public function getInlineScript()
500 {
501 return "
502 function isLodash () {
503
504 let isLodash = false;
505
506 // If _ is defined and the function _.forEach exists then we know underscore OR lodash are in place
507 if ( 'undefined' != typeof( _ ) && 'function' == typeof( _.forEach ) ) {
508
509 // A small sample of some of the functions that exist in lodash but not underscore
510 const funcs = [ 'get', 'set', 'at', 'cloneDeep' ];
511
512 // Simplest if assume exists to start
513 isLodash = true;
514
515 funcs.forEach( function ( func ) {
516 // If just one of the functions do not exist, then not lodash
517 isLodash = ( 'function' != typeof( _[ func ] ) ) ? false : isLodash;
518 } );
519 }
520
521 if ( isLodash ) {
522 // We know that lodash is loaded in the _ variable
523 return true;
524 } else {
525 // We know that lodash is NOT loaded
526 return false;
527 }
528 };
529
530 if ( isLodash() ) {
531 _.noConflict();
532 }
533 ";
534 }
535 }
536