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

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