PluginProbe ʕ •ᴥ•ʔ
PublishPress Capabilities – User Role Editor, Access Permissions, User Capabilities, Admin Menus / 2.8.1
PublishPress Capabilities – User Role Editor, Access Permissions, User Capabilities, Admin Menus v2.8.1
2.45.0 2.44.0 trunk 1.10 1.10.1 1.4.1 1.4.10 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.9 1.5 1.5.1 1.5.10 1.5.11 1.5.2 1.5.3 1.5.4 1.5.5 1.5.7 1.5.8 1.5.9 1.6 1.6.1 1.7 1.7.1 1.7.2 1.7.3 1.7.4 1.7.5 1.8.1 1.9 1.9.10 1.9.12 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.9.9 2.0 2.0.2 2.0.3 2.1 2.1.1 2.10.0 2.10.1 2.10.2 2.10.3 2.11.1 2.12.1 2.12.2 2.13.0 2.14.0 2.15.0 2.16.0 2.17.0 2.18.0 2.18.2 2.19.0 2.19.1 2.19.2 2.2 2.2.1 2.20.0 2.21.0 2.22.0 2.23.0 2.3 2.3.1 2.3.2 2.3.3 2.3.4 2.3.5 2.3.6 2.30.0 2.31.0 2.32.0 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.40.0 2.41.0 2.42.0 2.43.0 2.5.0 2.5.1 2.5.2 2.6.0 2.6.1 2.7.0 2.7.1 2.8.0 2.8.1 2.9.0 2.9.1
capability-manager-enhanced / includes / functions.php
capability-manager-enhanced / includes Last commit date
features 3 years ago roles 3 years ago admin-load.php 3 years ago admin.php 3 years ago backup-handler.php 3 years ago backup.php 3 years ago cap-helper.php 4 years ago dashboard.php 3 years ago filters-admin.php 4 years ago filters-woocommerce.php 4 years ago filters-wp_rest_workarounds.php 4 years ago filters.php 4 years ago functions-admin.php 3 years ago functions.php 3 years ago handler.php 4 years ago inflect-cme.php 4 years ago manager.php 3 years ago network.php 4 years ago pp-handler.php 4 years ago pp-ui.php 3 years ago publishpress-roles.php 4 years ago settings-handler.php 3 years ago settings-ui.php 3 years ago settings.php 3 years ago test-user-ui.php 3 years ago test-user.php 3 years ago
functions.php
1286 lines
1 <?php
2 /*
3 * PublishPress Capabilities [Free]
4 *
5 * Functions available for any URL, which are not contained within a class
6 *
7 * For performance and code separation, do not include functions that are only needed for wp-admin requests
8 *
9 */
10
11
12 /**
13 * Sanitizes a string entry
14 *
15 * Keys are used as internal identifiers. Uppercase or lowercase alphanumeric characters,
16 * spaces, periods, commas, plusses, asterisks, colons, pipes, parentheses, dashes and underscores are allowed.
17 *
18 * @param string $entry String entry
19 * @return string Sanitized entry
20 */
21 function pp_capabilities_sanitize_entry( $entry ) {
22 $entry = preg_replace( '/[^a-zA-Z0-9 \.\,\+\*\:\|\(\)_\-\=]/', '', $entry );
23 return $entry;
24 }
25
26 function pp_capabilities_is_editable_role($role_name, $args = []) {
27 static $editable_roles;
28
29 if (!function_exists('wp_roles')) {
30 return false;
31 }
32
33 if (!isset($editable_roles) || !empty($args['force_refresh'])) {
34 $all_roles = wp_roles()->roles;
35 $editable_roles = apply_filters('editable_roles', $all_roles, $args);
36 }
37
38 return apply_filters('pp_capabilities_editable_role', isset($editable_roles[$role_name]), $role_name);
39 }
40
41 function _cme_act_pp_active()
42 {
43 if (defined('PRESSPERMIT_VERSION') || (defined('PPC_VERSION') && function_exists('pp_init_cap_caster'))) {
44 define('PRESSPERMIT_ACTIVE', true);
45 } else {
46 if (defined('SCOPER_VERSION') || (defined('PP_VERSION') && function_exists('pp_init_users_interceptor'))) {
47 define('OLD_PRESSPERMIT_ACTIVE', true);
48 }
49 }
50 }
51
52 function _cme_cap_helper()
53 {
54 global $cme_cap_helper;
55
56 require_once(dirname(__FILE__) . '/cap-helper.php');
57 $cme_cap_helper = new CME_Cap_Helper();
58
59 add_action('registered_post_type', '_cme_post_type_late_reg', 5, 2);
60 add_action('registered_taxonomy', '_cme_taxonomy_late_reg', 5, 2);
61 }
62
63 function _cme_post_type_late_reg($post_type, $type_obj)
64 {
65 global $cme_cap_helper;
66
67 if (!empty($type_obj->public) || !empty($type_obj->show_ui)) {
68 $cme_cap_helper->refresh();
69 }
70 }
71
72 function _cme_taxonomy_late_reg($taxonomy, $tx_obj)
73 {
74 global $cme_cap_helper;
75
76 if (!empty($tx_obj->public)) {
77 $cme_cap_helper->refresh();
78 }
79 }
80
81 function _cme_init()
82 {
83 require_once(dirname(__FILE__) . '/filters.php');
84
85 load_plugin_textdomain('capsman-enhanced', false, dirname(plugin_basename(__FILE__)) . '/languages');
86 }
87
88 function cme_is_plugin_active($check_plugin_file)
89 {
90 if (!$check_plugin_file)
91 return false;
92
93 $plugins = (array)get_option('active_plugins');
94
95 foreach ($plugins as $plugin_file) {
96 if (false !== strpos($plugin_file, $check_plugin_file))
97 return $plugin_file;
98 }
99 }
100
101 // if a role is marked as hidden, also default it for use by Press Permit as a Pattern Role (when PP Collaborative Editing is activated and Advanced Settings enabled)
102 function _cme_pp_default_pattern_role($role)
103 {
104 if (!$pp_role_usage = get_option('pp_role_usage'))
105 $pp_role_usage = array();
106
107 if (empty($pp_role_usage[$role])) {
108 $pp_role_usage[$role] = 'pattern';
109 update_option('pp_role_usage', $pp_role_usage);
110 }
111 }
112
113 // deprecated
114 function capsman_get_pp_option($option_basename)
115 {
116 return pp_capabilities_get_permissions_option($option_basename);
117 }
118
119 function pp_capabilities_autobackup()
120 {
121 global $wpdb;
122
123 $roles = get_option($wpdb->prefix . 'user_roles');
124 update_option('cme_backup_auto_' . current_time('Y-m-d_g-i-s_a'), $roles, false);
125
126 $max_auto_backups = (defined('CME_AUTOBACKUPS')) ? (int) CME_AUTOBACKUPS : 20;
127
128 $current_options = $wpdb->get_col("SELECT option_name FROM $wpdb->options WHERE option_name LIKE 'cme_backup_auto_%' ORDER BY option_id DESC");
129
130 if (count($current_options) >= $max_auto_backups) {
131 $i = 0;
132
133 foreach($current_options as $option_name) {
134 $i++;
135
136 if ($i > $max_auto_backups) {
137 $wpdb->query(
138 $wpdb->prepare(
139 "DELETE FROM $wpdb->options WHERE option_name = %s",
140 $option_name
141 )
142 );
143
144 wp_cache_delete($option_name, 'options');
145 }
146 }
147 }
148 }
149
150 function pp_capabilities_get_permissions_option($option_basename)
151 {
152 return (function_exists('presspermit')) ? presspermit()->getOption($option_basename) : pp_get_option($option_basename);
153 }
154
155 function pp_capabilities_update_permissions_option($option_basename, $option_val)
156 {
157 function_exists('presspermit') ? presspermit()->updateOption($option_basename, $option_val) : pp_update_option($option_basename, $option_val);
158 }
159
160 /**
161 * Get post type.
162 *
163 * @return null|string String of the post type.
164 */
165 function pp_capabilities_get_post_type()
166 {
167 global $post, $typenow, $current_screen;
168
169 // We have a post so we can just get the post type from that.
170 if ($post && $post->post_type) {
171 return $post->post_type;
172 }
173
174 // Check the global $typenow - set in admin.php
175 if ($typenow) {
176 return $typenow;
177 }
178
179 // Check the global $current_screen object - set in screen.php
180 if ($current_screen && $current_screen->post_type) {
181 return $current_screen->post_type;
182 }
183
184 if (isset($_GET['post']) && !is_array($_GET['post'])) {
185 $post_id = (int) $_GET['post'];
186
187 } elseif (isset($_POST['post_ID'])) {
188 $post_id = (int) $_POST['post_ID'];
189 }
190
191 if (!empty($post_id)) {
192 return get_post_type($post_id);
193 }
194
195 // lastly check the post_type querystring
196 if (isset($_REQUEST['post_type'])) {
197 return sanitize_key($_REQUEST['post_type']);
198 }
199
200 return 'post';
201 }
202
203 /**
204 * Check if Classic Editor plugin is available.
205 *
206 * @return bool
207 */
208 function pp_capabilities_is_classic_editor_available()
209 {
210 global $wp_version;
211
212 return class_exists('Classic_Editor')
213 || function_exists( 'the_gutenberg_project' )
214 || class_exists('Gutenberg_Ramp')
215 || version_compare($wp_version, '5.0', '<')
216 || class_exists('WooCommerce')
217 || (defined('PP_CAPABILITIES_CONFIGURE_CLASSIC_EDITOR') && PP_CAPABILITIES_CONFIGURE_CLASSIC_EDITOR)
218 || !empty(get_option('cme_editor_features_classic_editor_tab'))
219 || (function_exists('et_get_option') && 'on' === et_get_option('et_enable_classic_editor', 'off'));
220 }
221
222 /**
223 * Get admin bar node and set as global for our usage.
224 * Due to admin toolbar, this function need to run in frontend as well
225 *
226 * @return array||object $wp_admin_bar nodes.
227 */
228 function ppc_features_get_admin_bar_nodes($wp_admin_bar){
229
230 $adminBarNode = is_object($wp_admin_bar) ? $wp_admin_bar->get_nodes() : '';
231 $ppcAdminBar = [];
232
233 if (is_array($adminBarNode) || is_object($adminBarNode)) {
234 foreach ($adminBarNode as $adminBarnode) {
235 $id = $adminBarnode->id;
236 $title = $adminBarnode->title;
237 $parent = $adminBarnode->parent;
238 $ppcAdminBar[$id] = array('id' => $id, 'title' => $title, 'parent' => $parent);
239 }
240 }
241
242 $GLOBALS['ppcAdminBar'] = $ppcAdminBar;
243 }
244 add_action('admin_bar_menu', 'ppc_features_get_admin_bar_nodes', 999);
245
246 /**
247 * Implement admin features restriction.
248 * Due to admin toolbar, this function need to run in frontend as well
249 *
250 */
251 function ppc_admin_feature_restrictions() {
252 if (pp_capabilities_feature_enabled('admin-features')) {
253 require_once(PUBLISHPRESS_CAPS_ABSPATH . '/includes/features/restrict-admin-features.php');
254 PP_Capabilities_Admin_Features::adminFeaturedRestriction();
255 }
256 }
257 add_action('init', 'ppc_admin_feature_restrictions', 999);
258
259
260 /**
261 * Implement test user feature
262 *
263 * @return void
264 */
265 function ppc_test_user_init () {
266 if (pp_capabilities_feature_enabled('user-testing')) {
267 require_once(PUBLISHPRESS_CAPS_ABSPATH . '/includes/test-user.php');
268 PP_Capabilities_Test_User::init();
269 }
270 }
271 add_action('init', 'ppc_test_user_init');
272
273
274 /**
275 * Redirect user to configured role login redirect
276 *
277 * @param string $redirect_to URL to redirect to.
278 * @param string $request URL the user is coming from.
279 * @param object $user Logged user's data.
280 * @return string
281 */
282 function ppc_roles_login_redirect($redirect_to, $request, $user) {
283
284 if (pp_capabilities_feature_enabled('roles') && isset($user->roles) && is_array($user->roles)) {
285 foreach ($user->roles as $user_role) {
286 //get role option
287 $role_option = get_option("pp_capabilities_{$user_role}_role_option", []);
288
289 if (is_array($role_option) && !empty($role_option)
290 && !empty($role_option['custom_redirect']) && (int)$role_option['custom_redirect'] > 0
291 && !empty($role_option['login_redirect'])
292 ) {
293 //custom url redirect
294 $redirect_to = esc_url_raw($role_option['login_redirect']);
295 break;
296 } else if (is_array($role_option) && !empty($role_option)
297 && !empty($role_option['referer_redirect']) && (int)$role_option['referer_redirect'] > 0
298 && wp_get_referer()
299 ) {
300 //referer url redirect
301 $redirect_to = esc_url_raw(wp_get_referer());
302 break;
303 }
304 }
305 }
306
307 return $redirect_to;
308 }
309 add_filter('login_redirect', 'ppc_roles_login_redirect', 10, 3);
310
311 /**
312 * Redirect user to configured role logout redirect
313 *
314 * @param string $redirect_to URL to redirect to.
315 * @param string $request URL the user is coming from.
316 * @param object $user Logged user's data.
317 * @return string
318 */
319 function ppc_roles_logout_redirect($redirect_to, $request, $user) {
320
321 if (pp_capabilities_feature_enabled('roles') && isset($user->roles) && is_array($user->roles)) {
322 foreach ($user->roles as $user_role) {
323 //get role option
324 $role_option = get_option("pp_capabilities_{$user_role}_role_option", []);
325 if (is_array($role_option) && !empty($role_option) && !empty($role_option['logout_redirect'])) {
326 $redirect_to = esc_url_raw($role_option['logout_redirect']);
327 break;
328 }
329 }
330 }
331
332 return $redirect_to;
333 }
334 add_filter('logout_redirect', 'ppc_roles_logout_redirect', 10, 3);
335
336 /**
337 * Block user role login
338 *
339 * @param $user (null|WP_User|WP_Error) WP_User if the user is authenticated. WP_Error or null otherwise.
340 *
341 * @return WP_User object if credentials authenticate the user. WP_Error or null otherwise
342 */
343 function ppc_roles_wp_authenticate_user($user) {
344
345 if (is_wp_error($user)) {
346 return $user;
347 }
348
349 if (pp_capabilities_feature_enabled('roles') && isset($user->roles) && is_array($user->roles)) {
350 foreach ($user->roles as $user_role) {
351 //get role option
352 $role_option = get_option("pp_capabilities_{$user_role}_role_option", []);
353 if (is_array($role_option) && !empty($role_option)
354 && !empty($role_option['disable_role_user_login'])
355 && (int)$role_option['disable_role_user_login'] > 0
356 ) {
357 return new WP_Error('ppc_roles_user_banned', __('Login permission denied.', 'capsman-enhanced'));
358 }
359 }
360 }
361
362 return $user;
363 }
364 add_filter('wp_authenticate_user', 'ppc_roles_wp_authenticate_user', 1);
365
366 /**
367 * Wocommerce role admin access restriction remove
368 */
369 function ppc_roles_disable_woocommerce_admin_restrictions($restrict_access) {
370
371 if (pp_capabilities_feature_enabled('roles') && $restrict_access && is_user_logged_in()) {
372 $user = get_userdata(get_current_user_id());
373
374 if (isset($user->roles) && is_array($user->roles)) {
375 foreach ($user->roles as $user_role) {
376 //get role option
377 $role_option = get_option("pp_capabilities_{$user_role}_role_option", []);
378 if (is_array($role_option) && !empty($role_option) && !empty($role_option['disable_woocommerce_admin_restrictions'])) {
379 $restrict_access = false;
380 break;
381 }
382 }
383 }
384 }
385 return $restrict_access;
386 }
387 add_filter('woocommerce_prevent_admin_access', 'ppc_roles_disable_woocommerce_admin_restrictions', 20);
388 add_filter('woocommerce_disable_admin_bar', 'ppc_roles_disable_woocommerce_admin_restrictions', 20);
389
390 /**
391 * List of capabilities admin pages
392 *
393 */
394 function pp_capabilities_admin_pages(){
395
396 $pp_capabilities_pages = [
397 'pp-capabilities',
398 'pp-capabilities-dashboard',
399 'pp-capabilities-roles',
400 'pp-capabilities-admin-menus',
401 'pp-capabilities-nav-menus',
402 'pp-capabilities-editor-features',
403 'pp-capabilities-backup',
404 'pp-capabilities-settings',
405 'pp-capabilities-admin-features',
406 'pp-capabilities-profile-features'
407 ];
408
409 return apply_filters('pp_capabilities_admin_pages', $pp_capabilities_pages);
410 }
411
412 /**
413 * Check if user is in capabilities admin page
414 *
415 */
416 function is_pp_capabilities_admin_page(){
417
418 $pp_capabilities_pages = pp_capabilities_admin_pages();
419
420 $is_pp_capabilities_page = false;
421 if ( isset( $_GET['page'] ) && in_array( $_GET['page'], $pp_capabilities_pages )) {
422 $is_pp_capabilities_page = true;
423 }
424
425 return apply_filters('is_pp_capabilities_admin_page', $is_pp_capabilities_page);
426 }
427
428
429 function pp_capabilities_nav_menu_access_denied()
430 {
431 $forbidden = esc_attr__('You do not have permission to access this page.', 'capsman-enhanced');
432 wp_die(esc_html($forbidden));
433 }
434
435
436 /**
437 * Display permission recomendation box
438 *
439 * @return void
440 */
441 function pp_capabilities_sidebox_banner($banner_title, $banner_messages)
442 {
443 //the banner style only got enqueue when banner display
444 //funtion is used which will no longer be true after removing the banner.
445 wp_enqueue_style(
446 'pp-wordpress-banners-style',
447 plugin_dir_url(CME_FILE) . 'vendor/publishpress/wordpress-banners/assets/css/style.css',
448 false,
449 PP_WP_BANNERS_VERSION
450 );
451
452 if (!is_array($banner_messages)) {
453 $banner_messages = [$banner_messages];
454 } ?>
455 <div class="pp-sidebar-box advertisement-box-content postbox">
456 <div class="postbox-header">
457 <h3 class="advertisement-box-header hndle is-non-sortable">
458 <span><?php echo $banner_title; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped?></span>
459 </h3>
460 </div>
461 <div class="inside">
462 <ul>
463 <?php foreach ($banner_messages as $banner_message) : ?>
464 <?php if (!empty($banner_message)) : ?>
465 <li><?php echo $banner_message; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped?></li>
466 <?php endif; ?>
467 <?php endforeach ?>
468 </ul>
469 </div>
470 </div>
471 <?php
472 }
473
474 /**
475 * Load pro sidebar
476 */
477 function pp_capabilities_pro_sidebox()
478 {
479 if (defined('PUBLISHPRESS_CAPS_PRO_VERSION')) {
480 return;
481 }
482
483 //the banner style only got enqueue when banner display
484 //funtion is used which will no longer be true after removing the banner.
485 wp_enqueue_style(
486 'pp-wordpress-banners-style',
487 plugin_dir_url(CME_FILE) . 'vendor/publishpress/wordpress-banners/assets/css/style.css',
488 false,
489 PP_WP_BANNERS_VERSION
490 );
491 ?>
492 <div class="ppc-advertisement-promo">
493 <div class="advertisement-box-content postbox">
494 <div class="postbox-header">
495 <h3 class="advertisement-box-header hndle is-non-sortable">
496 <span><?php echo esc_html__('Upgrade to Capabilities Pro', 'capsman-enhanced'); ?></span>
497 </h3>
498 </div>
499
500 <div class="inside">
501 <p><?php echo esc_html__('Enhance the power of PublishPress Capabilities with the Pro version:', 'capsman-enhanced'); ?>
502 </p>
503 <ul>
504 <li><?php echo esc_html__('Admin Menu restrictions', 'capsman-enhanced'); ?></li>
505 <li><?php echo esc_html__('Remove metaboxes on the editing screen', 'capsman-enhanced'); ?></li>
506 <li><?php echo esc_html__('Remove anything on the editing screen', 'capsman-enhanced'); ?></li>
507 <li><?php echo esc_html__('Remove anything in the WordPress admin', 'capsman-enhanced'); ?></li>
508 <li><?php echo esc_html__('Block admin pages by URL', 'capsman-enhanced'); ?></li>
509 <li><?php echo esc_html__('Fast, professional support', 'capsman-enhanced'); ?></li>
510 <li><?php echo esc_html__('No ads inside the plugin', 'capsman-enhanced'); ?></li>
511 </ul>
512 <div class="upgrade-btn">
513 <a href="https://publishpress.com/links/capabilities-menu" target="__blank"><?php echo esc_html__('Upgrade to Pro', 'capsman-enhanced'); ?></a>
514 </div>
515 </div>
516 </div>
517 <div class="advertisement-box-content postbox">
518 <div class="postbox-header">
519 <h3 class="advertisement-box-header hndle is-non-sortable">
520 <span><?php echo esc_html__('Need PublishPress Capabilities Support?', 'capsman-enhanced'); ?></span>
521 </h3>
522 </div>
523
524 <div class="inside">
525 <p><?php echo esc_html__('If you need help or have a new feature request, let us know.', 'capsman-enhanced'); ?>
526 <a class="advert-link" href="https://wordpress.org/plugins/capability-manager-enhanced/" target="_blank">
527 <?php echo esc_html__('Request Support', 'capsman-enhanced'); ?>
528 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" class="linkIcon">
529 <path
530 d="M18.2 17c0 .7-.6 1.2-1.2 1.2H7c-.7 0-1.2-.6-1.2-1.2V7c0-.7.6-1.2 1.2-1.2h3.2V4.2H7C5.5 4.2 4.2 5.5 4.2 7v10c0 1.5 1.2 2.8 2.8 2.8h10c1.5 0 2.8-1.2 2.8-2.8v-3.6h-1.5V17zM14.9 3v1.5h3.7l-6.4 6.4 1.1 1.1 6.4-6.4v3.7h1.5V3h-6.3z"
531 ></path>
532 </svg>
533 </a>
534 </p>
535 <p>
536 <?php echo esc_html__('Detailed documentation is also available on the plugin website.', 'capsman-enhanced'); ?>
537 <a class="advert-link" href="https://publishpress.com/docs-category/cme/" target="_blank">
538 <?php echo esc_html__('View Knowledge Base', 'capsman-enhanced'); ?>
539 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" class="linkIcon">
540 <path
541 d="M18.2 17c0 .7-.6 1.2-1.2 1.2H7c-.7 0-1.2-.6-1.2-1.2V7c0-.7.6-1.2 1.2-1.2h3.2V4.2H7C5.5 4.2 4.2 5.5 4.2 7v10c0 1.5 1.2 2.8 2.8 2.8h10c1.5 0 2.8-1.2 2.8-2.8v-3.6h-1.5V17zM14.9 3v1.5h3.7l-6.4 6.4 1.1 1.1 6.4-6.4v3.7h1.5V3h-6.3z"
542 ></path>
543 </svg>
544 </a>
545 </p>
546 </div>
547 </div>
548 </div>
549 <?php
550 }
551
552 /**
553 * Check if current active theme is block
554 * theme/support full site editing
555 *
556 * @return bool
557 */
558 function pp_capabilities_is_block_theme()
559 {
560 $is_block_theme = false;
561
562 if (function_exists('wp_is_block_theme')
563 && function_exists('block_template_part')
564 && wp_is_block_theme()
565 ) {
566 $is_block_theme = true;
567 }
568
569 return $is_block_theme;
570 }
571
572 /**
573 * Get FSE theme nav menus
574 *
575 * @return array
576 */
577 function pp_capabilities_get_fse_navs()
578 {
579 $args = array(
580 'post_type' => 'wp_navigation',
581 'no_found_rows' => true,
582 'order' => 'DESC',
583 'orderby' => 'date',
584 'post_status' => 'publish',
585 'posts_per_page' => -1
586 );
587
588 return get_posts($args);
589 }
590
591
592 /**
593 * Get FSE theme nav menus sub items
594 *
595 * @param integer $nav_id
596 * @return array
597 */
598 function pp_capabilities_get_fse_navs_sub_items($nav_id)
599 {
600 $menu_items = [];
601
602 $nav_post = get_post($nav_id);
603
604 if ($nav_post && !is_wp_error($nav_post) && !empty($nav_post->post_content)) {
605 $parsed_blocks = parse_blocks($nav_post->post_content);
606 $parsed_blocks = block_core_navigation_filter_out_empty_blocks($parsed_blocks);
607
608 foreach ($parsed_blocks as $parsed_block) {
609 $menu_items = pp_capabilities_parse_nav_block($parsed_block, $menu_items);
610 }
611 }
612
613 return $menu_items;
614 }
615
616 /**
617 * Parse nav block attributes to required format
618 *
619 * @param object $parsed_block
620 * @param array $menu_items
621 * @param integer $parent
622 * @param integer $depth
623 * @param string $ancestor_class
624 *
625 * @return array $menu_items
626 */
627 function pp_capabilities_parse_nav_block($parsed_block, $menu_items, $parent = 0, $depth = 0, $ancestor_class = '') {
628
629 $block_attrs = $parsed_block['attrs'];
630 $inner_blocks = $parsed_block['innerBlocks'];
631 $block_id = isset($block_attrs['id']) ? $block_attrs['id'] : 0;
632
633 //assign page id to page list block
634 if ($block_id === 0 && isset($parsed_block['blockName']) && in_array($parsed_block['blockName'], ['core/page-list'])) {
635 $block_id = '+'.abs(crc32(uniqid(true)));
636 }
637
638 //add parent id to ancestor class
639 if ($parent !== 0) {
640 $ancestor_class .= ' ancestor-' . str_replace('+', '', $parent);
641 }
642
643 if (isset($block_attrs['kind']) && $block_attrs['kind'] === 'post-type' && wp_get_post_parent_id($block_id) > 0) {
644 $parent = wp_get_post_parent_id($block_attrs['id']);
645 $post_ancestors = get_post_ancestors($block_attrs['id']);
646 $depth = count($post_ancestors);
647 //add post ancestors id to ancestor class
648 if (!empty($post_ancestors)) {
649 $post_ancesstors_class = ' ancestor-' . join(' ancestor-', $post_ancestors);
650 $ancestor_class .= $post_ancesstors_class;
651 }
652 }
653
654 //we don't want current block id in ancestor class
655 $ancestor_class = str_replace('ancestor-' . $block_id . '', '', $ancestor_class);
656
657 if (!empty($block_attrs) && isset($block_attrs['kind']) && isset($block_attrs['label']) && isset($block_attrs['id'])) {
658 //This block has attributes
659 $menu_items[] = (object) [
660 'ID' => $block_id,
661 'title' => ppc_block_menu_icon($parsed_block['blockName']) . ' ' . $block_attrs['label'],
662 'object_id' => $block_attrs['url'],
663 'object' => isset($block_attrs['type']) ? $block_attrs['type'] : $block_attrs['kind'],
664 'menu_item_parent' => $parent,
665 'ancestor_class' => $ancestor_class,
666 'is_parent_page' => !empty($inner_blocks) || (isset($block_attrs['is_parent_page']) && $block_attrs['is_parent_page'] === 1) ? 1 : 0,
667 'depth' => $depth
668 ];
669
670 if (!empty($inner_blocks)) {
671 $depth++;
672 foreach ($inner_blocks as $inner_block) {
673 $menu_items = pp_capabilities_parse_nav_block($inner_block, $menu_items, max($block_id, 1), $depth, $ancestor_class);
674 }
675 }
676 } elseif (!empty($block_attrs) && isset($block_attrs['label'])) {
677 //This block has the needed label attribute (core/search, core/home-link)
678 $menu_items[] = (object) [
679 'ID' => $block_id,
680 'title' => ppc_block_menu_icon($parsed_block['blockName']) . ' ' . $block_attrs['label'],
681 'object_id' => $parsed_block['blockName'],
682 'object' => 'custom_block_' . sanitize_title_with_dashes($block_attrs['label']),
683 'menu_item_parent' => $parent,
684 'ancestor_class' => $ancestor_class,
685 'is_parent_page' => !empty($inner_blocks) ? 1 : 0,
686 'depth' => $depth
687 ];
688
689 if (!empty($inner_blocks)) {
690 $depth++;
691 foreach ($inner_blocks as $inner_block) {
692 $menu_items = pp_capabilities_parse_nav_block($inner_block, $menu_items, max($block_id, 1), $depth, $ancestor_class);
693 }
694 }
695 } elseif (!empty($parsed_block) && isset($parsed_block['blockName']) && in_array($parsed_block['blockName'], ['core/site-logo', 'core/site-title', 'core/social-links', 'core/page-list'])) {
696 //This block doesn't have any block attr
697 $menu_items[] = (object) [
698 'ID' => $block_id,
699 'title' => ppc_block_menu_icon($parsed_block['blockName']) . ' ' . ppc_block_friend_name($parsed_block['blockName']),
700 'object_id' => $parsed_block['blockName'],
701 'object' => 'custom_block',
702 'menu_item_parent' => $parent,
703 'ancestor_class' => $ancestor_class,
704 'is_parent_page' => $block_id !== 0 && (!empty($inner_blocks) || $parsed_block['blockName'] === 'core/page-list') ? 1 : 0,
705 'depth' => $depth
706 ];
707
708 //add page list inner block
709 if ($parsed_block['blockName'] === 'core/page-list') {
710 $pages_args = ['sort_column' => 'menu_order,post_title', 'order' => 'asc'];
711 if (isset($block_attrs['parentPageID']) && !empty($block_attrs['parentPageID'])) {
712 $pages_args['child_of'] = $block_attrs['parentPageID'];
713 }
714 $all_pages = get_pages($pages_args);
715 if (!empty($all_pages)) {
716 foreach ( (array) $all_pages as $page ) {
717 $children = get_pages('child_of='.$page->ID);
718 $inner_blocks[] = [
719 'blockName' => 'page_list_link',
720 'attrs' => [
721 'label' => $page->post_title,
722 'type' => 'page',
723 'kind' => 'post-type',
724 'id' => $page->ID,
725 'is_parent_page' => count($children) > 0 ? 1 : 0,
726 'url' => get_permalink($page->ID)
727 ],
728 'innerBlocks' => []
729 ];
730 }
731 }
732 }
733
734 if (!empty($inner_blocks)) {
735 $depth++;
736 foreach ($inner_blocks as $inner_block) {
737 $menu_items = pp_capabilities_parse_nav_block($inner_block, $menu_items, max($block_id, 1), $depth, $ancestor_class);
738 }
739 }
740 }
741
742 return $menu_items;
743 }
744
745 function ppc_block_friend_name($block_name) {
746
747 $friendly_name = $block_name;
748
749 $supported_blocks = [
750 'core/site-logo' => __('Site Logo', 'capsman-enhanced'),
751 'core/site-title' => __('Site Title', 'capsman-enhanced'),
752 'core/social-links' => __('Social Links', 'capsman-enhanced'),
753 'core/page-list' => __('Page Lists', 'capsman-enhanced'),
754 'core/search' => __('Search', 'capsman-enhanced'),
755 'core/home-link' => __('Home Link', 'capsman-enhanced'),
756 ];
757
758 if (array_key_exists($block_name, $supported_blocks)) {
759 $friendly_name = $supported_blocks[$block_name];
760 }
761
762 return $friendly_name;
763 }
764
765
766 function ppc_block_menu_icon($block_name) {
767
768 $menu_icon = '<span class="ppc-menu-block-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" context="list-view" aria-hidden="true" focusable="false"><path d="M7 13.8h6v-1.5H7v1.5zM18 16V4c0-1.1-.9-2-2-2H6c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2zM5.5 16V4c0-.3.2-.5.5-.5h10c.3 0 .5.2.5.5v12c0 .3-.2.5-.5.5H6c-.3 0-.5-.2-.5-.5zM7 10.5h8V9H7v1.5zm0-3.3h8V5.8H7v1.4zM20.2 6v13c0 .7-.6 1.2-1.2 1.2H8v1.5h11c1.5 0 2.7-1.2 2.7-2.8V6h-1.5z"></path></svg></span>';
769
770 $supported_blocks = [
771 'core/site-logo' => '<span class="ppc-menu-block-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" context="list-view" aria-hidden="true" focusable="false"><path d="M12 3c-5 0-9 4-9 9s4 9 9 9 9-4 9-9-4-9-9-9zm0 1.5c4.1 0 7.5 3.4 7.5 7.5v.1c-1.4-.8-3.3-1.7-3.4-1.8-.2-.1-.5-.1-.8.1l-2.9 2.1L9 11.3c-.2-.1-.4 0-.6.1l-3.7 2.2c-.1-.5-.2-1-.2-1.5 0-4.2 3.4-7.6 7.5-7.6zm0 15c-3.1 0-5.7-1.9-6.9-4.5l3.7-2.2 3.5 1.2c.2.1.5 0 .7-.1l2.9-2.1c.8.4 2.5 1.2 3.5 1.9-.9 3.3-3.9 5.8-7.4 5.8z"></path></svg></span>',
772 'core/site-title' => '<span class="ppc-menu-block-icon"><svg xmlns="https://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" context="list-view" aria-hidden="true" focusable="false"><path d="M12 9c-.8 0-1.5.7-1.5 1.5S11.2 12 12 12s1.5-.7 1.5-1.5S12.8 9 12 9zm0-5c-3.6 0-6.5 2.8-6.5 6.2 0 .8.3 1.8.9 3.1.5 1.1 1.2 2.3 2 3.6.7 1 3 3.8 3.2 3.9l.4.5.4-.5c.2-.2 2.6-2.9 3.2-3.9.8-1.2 1.5-2.5 2-3.6.6-1.3.9-2.3.9-3.1C18.5 6.8 15.6 4 12 4zm4.3 8.7c-.5 1-1.1 2.2-1.9 3.4-.5.7-1.7 2.2-2.4 3-.7-.8-1.9-2.3-2.4-3-.8-1.2-1.4-2.3-1.9-3.3-.6-1.4-.7-2.2-.7-2.5 0-2.6 2.2-4.7 5-4.7s5 2.1 5 4.7c0 .2-.1 1-.7 2.4z"></path></svg></span>',
773 'core/social-links' => '<span class="ppc-menu-block-icon"><svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" width="24" height="24" context="list-view" aria-hidden="true" focusable="false"><path d="M9 11.8l6.1-4.5c.1.4.4.7.9.7h2c.6 0 1-.4 1-1V5c0-.6-.4-1-1-1h-2c-.6 0-1 .4-1 1v.4l-6.4 4.8c-.2-.1-.4-.2-.6-.2H6c-.6 0-1 .4-1 1v2c0 .6.4 1 1 1h2c.2 0 .4-.1.6-.2l6.4 4.8v.4c0 .6.4 1 1 1h2c.6 0 1-.4 1-1v-2c0-.6-.4-1-1-1h-2c-.5 0-.8.3-.9.7L9 12.2v-.4z"></path></svg></span>',
774 'core/page-list' => '<span class="ppc-menu-block-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" context="list-view" aria-hidden="true" focusable="false"><path d="M7 13.8h6v-1.5H7v1.5zM18 16V4c0-1.1-.9-2-2-2H6c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2zM5.5 16V4c0-.3.2-.5.5-.5h10c.3 0 .5.2.5.5v12c0 .3-.2.5-.5.5H6c-.3 0-.5-.2-.5-.5zM7 10.5h8V9H7v1.5zm0-3.3h8V5.8H7v1.4zM20.2 6v13c0 .7-.6 1.2-1.2 1.2H8v1.5h11c1.5 0 2.7-1.2 2.7-2.8V6h-1.5z"></path></svg></span>',
775 'core/search' => '<span class="ppc-menu-block-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" context="list-view" aria-hidden="true" focusable="false"><path d="M13.5 6C10.5 6 8 8.5 8 11.5c0 1.1.3 2.1.9 3l-3.4 3 1 1.1 3.4-2.9c1 .9 2.2 1.4 3.6 1.4 3 0 5.5-2.5 5.5-5.5C19 8.5 16.5 6 13.5 6zm0 9.5c-2.2 0-4-1.8-4-4s1.8-4 4-4 4 1.8 4 4-1.8 4-4 4z"></path></svg></span>',
776 'core/home-link' => '<span class="ppc-menu-block-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" context="list-view" aria-hidden="true" focusable="false"><path d="M12 4L4 7.9V20h16V7.9L12 4zm6.5 14.5H14V13h-4v5.5H5.5V8.8L12 5.7l6.5 3.1v9.7z"></path></svg></span>',
777 'core/navigation-link' => '<span class="ppc-menu-block-icon"><svg xmlns="https://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" context="list-view" aria-hidden="true" focusable="false"><path d="M12.5 14.5h-1V16h1c2.2 0 4-1.8 4-4s-1.8-4-4-4h-1v1.5h1c1.4 0 2.5 1.1 2.5 2.5s-1.1 2.5-2.5 2.5zm-4 1.5v-1.5h-1C6.1 14.5 5 13.4 5 12s1.1-2.5 2.5-2.5h1V8h-1c-2.2 0-4 1.8-4 4s1.8 4 4 4h1zm-1-3.2h5v-1.5h-5v1.5zM18 4H9c-1.1 0-2 .9-2 2v.5h1.5V6c0-.3.2-.5.5-.5h9c.3 0 .5.2.5.5v12c0 .3-.2.5-.5.5H9c-.3 0-.5-.2-.5-.5v-.5H7v.5c0 1.1.9 2 2 2h9c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2z"></path></svg></span>',
778 ];
779
780 if (array_key_exists($block_name, $supported_blocks)) {
781 $menu_icon = $supported_blocks[$block_name];
782 }
783
784 return $menu_icon;
785 }
786
787 /**
788 * Nav menu restriction
789 */
790 if (!is_admin()) {
791
792 /**
793 * Classic menu
794 *
795 * Checks the menu items for their visibility options and
796 * removes menu items that are not visible.
797 *
798 * @return array
799 */
800 function pp_capabilities_nav_menu_permission($items, $menu, $args)
801 {
802 //return if it's admin page
803 if (is_admin() || !pp_capabilities_feature_enabled('nav-menus')) {
804 return $items;
805 }
806
807 $disabled_nav_menu = '';
808
809 $user_roles = (array)wp_get_current_user()->roles;
810 $nav_menu_item_option = !empty(get_option('capsman_nav_item_menus')) ? (array)get_option('capsman_nav_item_menus') : [];
811
812 //add loggedin and guest option to role
813 if (is_user_logged_in()) {
814 $user_roles[] = 'ppc_users';
815 } else {
816 $user_roles[] = 'ppc_guest';
817 }
818
819 /*
820 * PublishPress Permissions: Restrict Nav Menus for a Permission Group
821 * (Integrate PublishPress Capabilities Pro functionality).
822 *
823 * Copy into functions.php, modifying $restriction_role and $permission_group_ids to match your usage.
824 *
825 * note: Restriction_role can be an extra role that you create just for these menu restrictions.
826 * Configure Capabilities > Nav Menus as desired for that role.
827 */
828 /*
829 add_filter('pp_capabilities_nav_menu_apply_role_restrictions',
830 function($roles, $menu_object) {
831 if (function_exists('presspermit')) {
832 $permission_group_ids = [12, 14, 15]; // group IDs to restrict
833 $restriction_role = 'subscriber'; // role that has restrictions defined by Capabilities > Nav Menus
834
835 if (array_intersect(
836 array_keys(presspermit()->getUser()->groups['pp_group']),
837 $permission_group_ids
838 )) {
839 $roles []= $restriction_role;
840 }
841 }
842
843 return $roles;
844 },
845 10, 2
846 );
847 */
848
849 // Support plugin integrations by allowing additional role-based limitations to be applied to user based on external criteria
850 $user_roles = apply_filters('pp_capabilities_nav_menu_apply_role_restrictions', $user_roles, compact('menu'));
851
852 //extract disabled menu for roles user belong
853 foreach ($user_roles as $role) {
854 if (array_key_exists($role, $nav_menu_item_option)) {
855 $disabled_nav_menu .= implode(", ", (array)$nav_menu_item_option[$role]) . ', ';
856 }
857 }
858
859 if ($disabled_nav_menu) {
860
861 //extract only IDS
862 $disabled_item_ids = preg_replace('!(0|[1-9][0-9]*)_([a-zA-Z0-9_.-]*),!s', '$1,', $disabled_nav_menu);
863
864 $disabled_nav_menu_array = array_filter(explode(", ", $disabled_item_ids));
865
866 foreach ($items as $key => $item) {
867
868 $item_parent = get_post_meta($item->ID, '_menu_item_menu_item_parent', true);
869
870 if (in_array($item->ID, $disabled_nav_menu_array) || in_array($item_parent, $disabled_nav_menu_array)) {
871 unset($items[$key]);
872 }
873 }
874
875
876 }
877
878 return $items;
879 }
880 add_filter('wp_get_nav_menu_items', 'pp_capabilities_nav_menu_permission', 99, 3);
881
882 /**
883 * FSE theme menu
884 *
885 * Checks the menu items for their visibility options and
886 * removes menu items that are not visible.
887 *
888 * @return array
889 */
890 function pp_capabilities_fse_nav_menu_permission($inner_blocks) {
891 global $ppc_disabled_nav_menu_data;
892
893 //return if it's admin page
894 if (is_admin() || !pp_capabilities_feature_enabled('nav-menus')) {
895 return $inner_blocks;
896 }
897
898 if (!is_array($ppc_disabled_nav_menu_data)) {
899 //we want to make sure we're not running disabled data check multiple times for each filter
900 $ppc_disabled_nav_menu_data = [];
901 }
902
903 if (isset($ppc_disabled_nav_menu_data['nav_menu_item_option'])) {
904 $nav_menu_item_option = $ppc_disabled_nav_menu_data['nav_menu_item_option'];
905 } else {
906 $nav_menu_item_option = !empty(get_option('capsman_nav_item_menus')) ? (array)get_option('capsman_nav_item_menus') : [];
907 $ppc_disabled_nav_menu_data['nav_menu_item_option'] = $nav_menu_item_option;
908 }
909
910 if (!$nav_menu_item_option || !function_exists('wp_get_current_user')) {
911 return $inner_blocks;
912 }
913
914 if (isset($ppc_disabled_nav_menu_data['disabled_nav_menu'])) {
915 $disabled_nav_menu = $ppc_disabled_nav_menu_data['disabled_nav_menu'];
916 } else {
917 $user_roles = (array)wp_get_current_user()->roles;
918 //add loggedin and guest option to role
919 $user_roles[] = (is_user_logged_in()) ? 'ppc_users' : 'ppc_guest';
920 // Support plugin integrations by allowing additional role-based limitations to be applied to user based on external criteria
921 $user_roles = apply_filters('pp_capabilities_nav_menu_access_role_restrictions', $user_roles);
922 $disabled_nav_menu = '';
923 //extract disabled menu for roles user belong
924 foreach ($user_roles as $role) {
925 if (array_key_exists($role, $nav_menu_item_option)) {
926 $disabled_nav_menu .= implode(", ", (array)$nav_menu_item_option[$role]) . ', ';
927 }
928 }
929 $ppc_disabled_nav_menu_data['disabled_nav_menu'] = $disabled_nav_menu;
930 }
931
932 if ($disabled_nav_menu) {
933 if (isset($ppc_disabled_nav_menu_data['fse_theme'])) {
934 $fse_theme = $ppc_disabled_nav_menu_data['fse_theme'];
935 $disabled_object = $ppc_disabled_nav_menu_data['disabled_object'];
936 $fse_blocked_nav_links = $ppc_disabled_nav_menu_data['fse_blocked_nav_links'];
937 $disabled_nav_menu_array = $ppc_disabled_nav_menu_data['disabled_nav_menu_array'];
938 } else {
939 $fse_theme = pp_capabilities_is_block_theme();
940 //we only need object id and object name e.g, 1_category
941 if ($fse_theme) {
942 $disabled_object = preg_replace("/(\|)(.*?)(\|)/", '_', $disabled_nav_menu);
943 //get all urls for css, js and direct block implementation
944 preg_match_all("/\|\s*(.*?)\s*\|/", $disabled_nav_menu, $matches);
945 $fse_blocked_nav_links = array_filter($matches[1]);
946 } else {
947 //native nav uses _ separator
948 $disabled_object = preg_replace('!(0|[1-9][0-9]*)_([a-zA-Z0-9_.-]*),!s', '$2,', $disabled_nav_menu);
949 $fse_blocked_nav_links = [];
950 }
951 $disabled_nav_menu_array = array_filter(explode(", ", $disabled_object));
952
953 $ppc_disabled_nav_menu_data['fse_theme'] = $fse_theme;
954 $ppc_disabled_nav_menu_data['disabled_object'] = $disabled_object;
955 $ppc_disabled_nav_menu_data['fse_blocked_nav_links'] = $fse_blocked_nav_links;
956 $ppc_disabled_nav_menu_data['disabled_nav_menu_array'] = $disabled_nav_menu_array;
957 }
958
959 //we're having issues removing item due to key change. So, we should do this in array
960 $removeable_keys = [];
961 $new_page_list_items = [];
962 foreach ($inner_blocks as $key_offset => $inner_block) {
963 if (isset($inner_block->parsed_block) && is_array($inner_block->parsed_block) && !empty($inner_block->parsed_block)) {
964 $block_details = $inner_block->parsed_block;
965 $block_name = isset($block_details['blockName']) ? $block_details['blockName'] : false;
966 $block_attrs = isset($block_details['attrs']) ? (array)$block_details['attrs'] : false;
967 $block_label = (isset($block_attrs['label'])) ? $block_attrs['label'] : false;
968 $block_id = isset($block_attrs['id']) ? $block_attrs['id'] : 0;
969 if (in_array($block_name, ['core/site-logo', 'core/site-title', 'core/social-links', 'core/search', 'core/home-link']) && in_array($block_name, $fse_blocked_nav_links)) {
970 //unset core nav block
971 $removeable_keys[] = $key_offset;
972 } elseif ($block_label && in_array($block_id . '_custom_block_' . sanitize_title_with_dashes($block_label), $disabled_nav_menu_array)) {
973 //unset custom block that doesn't have ID identifier
974 $removeable_keys[] = $key_offset;
975 } elseif (is_array($block_attrs) && isset($block_attrs['url']) && in_array($block_attrs['url'], $fse_blocked_nav_links)) {
976 //unset custom links
977 $removeable_keys[] = $key_offset;
978 } elseif (in_array($block_name, ['core/page-list'])) {
979 //unset page list nav block
980 $removeable_keys[] = $key_offset;
981
982 //we need to list all non-editable pagelist pages and add them as block to enable removal
983 $parent_page_id = isset($block_attrs['parentPageID']) ? (int) $block_attrs['parentPageID'] : 0;
984 $all_pages = get_pages(
985 [
986 'sort_column' => 'menu_order,post_title',
987 'order' => 'asc',
988 ]
989 );
990
991 if (!empty($all_pages)) {
992 $top_level_pages = [];
993 $pages_with_children = [];
994 foreach ((array) $all_pages as $page) {
995 $page_link = get_permalink($page->ID);
996 //only add pages not blocked as navigation link
997 if (!in_array($page_link, $fse_blocked_nav_links)) {
998 if ($page->post_parent) {
999 $pages_with_children[ $page->post_parent ][ $page->ID ] = [
1000 'blockName' => 'core/navigation-link',
1001 'attrs' => [
1002 'label' => $page->post_title,
1003 'type' => 'page',
1004 'kind' => 'post-type',
1005 'id' => $page->ID,
1006 'url' => $page_link,
1007 ],
1008 'innerBlocks' => [],
1009 'innerHTML' => '',
1010 'innerContent' => [],
1011 ];
1012 } else {
1013 $top_level_pages[ $page->ID ] = [
1014 'blockName' => 'core/navigation-link',
1015 'attrs' => [
1016 'label' => $page->post_title,
1017 'type' => 'page',
1018 'kind' => 'post-type',
1019 'id' => $page->ID,
1020 'url' => $page_link,
1021 'isTopLevelLink' => 1
1022 ],
1023 'innerBlocks' => [],
1024 'innerHTML' => '',
1025 'innerContent' => [],
1026 ];
1027 }
1028 }
1029 }
1030
1031 $new_page_list_items = ppc_block_core_page_list_nest_pages( $top_level_pages, $pages_with_children );
1032 if ( 0 !== $parent_page_id ) {
1033 if (array_key_exists($parent_page_id, $pages_with_children)) {
1034 $new_page_list_items = ppc_block_core_page_list_nest_pages(
1035 $pages_with_children[$parent_page_id],
1036 $pages_with_children
1037 );
1038 } else {
1039 // If the parent page has no child pages, there is nothing to show.
1040 $new_page_list_items = [];
1041 }
1042 }
1043 }
1044 } else {
1045 //this is probably a block we currently not supporting
1046 }
1047 }
1048 }
1049
1050 //unset using block function
1051 foreach (array_values($removeable_keys) as $removeable_keys) {
1052 $inner_blocks->offsetUnset($removeable_keys);
1053 }
1054 //add new navigation links that wasn't removed from page list to block
1055 if (!empty($new_page_list_items)) {
1056 foreach ($new_page_list_items as $new_page_list_item) {
1057 $inner_blocks->offsetSet(null, $new_page_list_item);
1058 }
1059 }
1060 }
1061
1062 return $inner_blocks;
1063 }
1064 add_filter('block_core_navigation_render_inner_blocks', 'pp_capabilities_fse_nav_menu_permission', 999);
1065
1066 /**
1067 * Outputs nested array of pages inner blocks
1068 *
1069 * @param array $current_level The level being iterated through.
1070 * @param array $children The children grouped by parent post ID.
1071 *
1072 * @return array The nested array of pages.
1073 */
1074 function ppc_block_core_page_list_nest_pages( $current_level, $children ) {
1075 if ( empty( $current_level ) ) {
1076 return;
1077 }
1078 foreach ( (array) $current_level as $key => $current ) {
1079 if ( isset( $children[ $key ] ) ) {
1080 $current_level[ $key ]['innerBlocks'] = ppc_block_core_page_list_nest_pages($children[ $key ], $children);
1081 }
1082 }
1083 return $current_level;
1084 }
1085
1086 /**
1087 * Checks the menu items for their privacy and remove
1088 * if user do not have permission to item
1089 *
1090 */
1091 function pp_capabilities_nav_menu_access($query)
1092 {
1093 global $ppc_nav_menu_restricted, $ppc_disabled_nav_menu_data;
1094
1095 //this function is getting called many times. So, it's needed
1096 if ($ppc_nav_menu_restricted || !pp_capabilities_feature_enabled('nav-menus')) {
1097 return;
1098 }
1099
1100 if (!function_exists('wp_get_current_user')) {
1101 return;
1102 }
1103
1104 if (!is_array($ppc_disabled_nav_menu_data)) {
1105 //we want to make sure we're not running disabled data check multiple times for each filter
1106 $ppc_disabled_nav_menu_data = [];
1107 }
1108
1109 if (isset($ppc_disabled_nav_menu_data['nav_menu_item_option'])) {
1110 $nav_menu_item_option = $ppc_disabled_nav_menu_data['nav_menu_item_option'];
1111 } else {
1112 $nav_menu_item_option = !empty(get_option('capsman_nav_item_menus')) ? (array)get_option('capsman_nav_item_menus') : [];
1113 $ppc_disabled_nav_menu_data['nav_menu_item_option'] = $nav_menu_item_option;
1114 }
1115
1116 if (!$nav_menu_item_option || !function_exists('wp_get_current_user')) {
1117 return;
1118 }
1119
1120 if (isset($ppc_disabled_nav_menu_data['disabled_nav_menu'])) {
1121 $disabled_nav_menu = $ppc_disabled_nav_menu_data['disabled_nav_menu'];
1122 } else {
1123 $user_roles = (array)wp_get_current_user()->roles;
1124 //add loggedin and guest option to role
1125 $user_roles[] = (is_user_logged_in()) ? 'ppc_users' : 'ppc_guest';
1126 // Support plugin integrations by allowing additional role-based limitations to be applied to user based on external criteria
1127 $user_roles = apply_filters('pp_capabilities_nav_menu_access_role_restrictions', $user_roles);
1128 $disabled_nav_menu = '';
1129 //extract disabled menu for roles user belong
1130 foreach ($user_roles as $role) {
1131 if (array_key_exists($role, $nav_menu_item_option)) {
1132 $disabled_nav_menu .= implode(", ", (array)$nav_menu_item_option[$role]) . ', ';
1133 }
1134 }
1135 $ppc_disabled_nav_menu_data['disabled_nav_menu'] = $disabled_nav_menu;
1136 }
1137
1138 if ($disabled_nav_menu) {
1139 if (isset($ppc_disabled_nav_menu_data['fse_theme'])) {
1140 $fse_theme = $ppc_disabled_nav_menu_data['fse_theme'];
1141 $disabled_object = $ppc_disabled_nav_menu_data['disabled_object'];
1142 $fse_blocked_nav_links = $ppc_disabled_nav_menu_data['fse_blocked_nav_links'];
1143 $disabled_nav_menu_array = $ppc_disabled_nav_menu_data['disabled_nav_menu_array'];
1144 } else {
1145 $fse_theme = pp_capabilities_is_block_theme();
1146 //we only need object id and object name e.g, 1_category
1147 if ($fse_theme) {
1148 $disabled_object = preg_replace("/(\|)(.*?)(\|)/", '_', $disabled_nav_menu);
1149 //get all urls for css, js and direct block implementation
1150 preg_match_all("/\|\s*(.*?)\s*\|/", $disabled_nav_menu, $matches);
1151 $fse_blocked_nav_links = array_filter($matches[1]);
1152 } else {
1153 //native nav uses _ separator
1154 $disabled_object = preg_replace('!(0|[1-9][0-9]*)_([a-zA-Z0-9_.-]*),!s', '$2,', $disabled_nav_menu);
1155 $fse_blocked_nav_links = [];
1156 }
1157 $disabled_nav_menu_array = array_filter(explode(", ", $disabled_object));
1158
1159 $ppc_disabled_nav_menu_data['fse_theme'] = $fse_theme;
1160 $ppc_disabled_nav_menu_data['disabled_object'] = $disabled_object;
1161 $ppc_disabled_nav_menu_data['fse_blocked_nav_links'] = $fse_blocked_nav_links;
1162 $ppc_disabled_nav_menu_data['disabled_nav_menu_array'] = $disabled_nav_menu_array;
1163 }
1164
1165 //category tags and taxonomy page check
1166 if (is_category() || is_tag() || is_tax()) {
1167 $taxonomy_id = get_queried_object()->term_id;
1168 $taxonnomy_type = get_queried_object()->taxonomy;
1169 $option_object = $taxonomy_id . '_' . $taxonnomy_type;
1170 if (in_array($option_object, $disabled_nav_menu_array)) {
1171 $ppc_nav_menu_restricted = true;
1172 pp_capabilities_nav_menu_access_denied();
1173 }
1174 }
1175
1176 //post, page, cpt check
1177 if (is_singular()) {
1178 $post_type = get_post_type();
1179 $post_id = get_the_ID();
1180 $option_object = $post_id . '_' . $post_type;
1181 if (in_array($option_object, $disabled_nav_menu_array)) {
1182 $ppc_nav_menu_restricted = true;
1183 pp_capabilities_nav_menu_access_denied();
1184 }
1185 }
1186
1187 $ppc_nav_menu_restricted = true;
1188
1189 if (!empty($fse_blocked_nav_links)) {
1190 //restrict access to url
1191 if (in_array(pp_capabilities_current_url(), $fse_blocked_nav_links)) {
1192 $ppc_nav_menu_restricted = true;
1193 pp_capabilities_nav_menu_access_denied();
1194 }
1195 //hide menu item immediately, remove menu item from li after page load
1196 add_action('wp_head', function() use ($fse_blocked_nav_links) {
1197 $menu_item_selectors = array_map('pp_capabilities_nav_link_selector', $fse_blocked_nav_links);
1198 ?>
1199 <style>
1200 <?php echo join(', ', $menu_item_selectors); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
1201 {
1202 display: none !important;
1203 }
1204 </style>
1205
1206 <script>
1207 jQuery(document).ready( function($) {
1208 $('<?php echo join(', ', $menu_item_selectors); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>').each( function() {
1209 $(this).closest('li').remove();
1210 $(this).remove();
1211 });
1212 });
1213 </script>
1214 <?php
1215 });
1216 }
1217
1218 }
1219 }
1220 add_action('parse_query', 'pp_capabilities_nav_menu_access');
1221 }
1222
1223 /**
1224 * Generate style link selector for a nav link
1225 *
1226 * @param string $url
1227 * @return string
1228 */
1229 function pp_capabilities_nav_link_selector($url) {
1230
1231 $link_selector = 'li.wp-block-navigation-item a[href*="'. $url .'"]';
1232 if ($url === 'core/search') {
1233 $link_selector = '.wp-block-navigation .wp-block-search';
1234 } elseif ($url === 'core/site-logo') {
1235 $link_selector = 'li.wp-block-navigation-item .wp-block-site-logo';
1236 } elseif ($url === 'core/site-title') {
1237 $link_selector = '.wp-block-navigation .wp-block-site-title';
1238 } elseif ($url === 'core/social-links') {
1239 $link_selector = '.wp-block-navigation .wp-block-social-links';
1240 } elseif ($url === 'core/home-link') {
1241 $link_selector = '.wp-block-navigation .wp-block-home-link';
1242 }
1243
1244 return $link_selector;
1245 }
1246
1247 /**
1248 * Get current page URL
1249 *
1250 * @return string
1251 */
1252 function pp_capabilities_current_url()
1253 {
1254 if (!empty($_SERVER['HTTP_HOST']) && !empty($_SERVER['REQUEST_URI'])) {
1255 return esc_url_raw((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http") . "://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
1256 } else {
1257 return home_url('');
1258 }
1259 }
1260
1261 /**
1262 * Check if a feature is enabled
1263 *
1264 * @param integer $feature
1265 *
1266 * @return bool
1267 */
1268 function pp_capabilities_feature_enabled($feature) {
1269 global $capsman_dashboard_features_status;
1270
1271 //let use global settings incase this request is made more than once in a page load
1272 if (!is_array($capsman_dashboard_features_status)) {
1273 $capsman_dashboard_features_status = !empty(get_option('capsman_dashboard_features_status')) ? (array)get_option('capsman_dashboard_features_status') : [];
1274 }
1275
1276 //let enable all feature by default
1277 $feature_enabled = true;
1278 if (isset($capsman_dashboard_features_status[$feature])
1279 && isset($capsman_dashboard_features_status[$feature]['status'])
1280 && $capsman_dashboard_features_status[$feature]['status'] === 'off'
1281 ) {
1282 $feature_enabled = false;
1283 }
1284
1285 return $feature_enabled;
1286 }