PluginProbe ʕ •ᴥ•ʔ
PublishPress Capabilities – User Role Editor, Access Permissions, User Capabilities, Admin Menus / 2.8.0
PublishPress Capabilities – User Role Editor, Access Permissions, User Capabilities, Admin Menus v2.8.0
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
1281 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',
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 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)) {
968 //unset core nav block
969 $removeable_keys[] = $key_offset;
970 } elseif (is_array($block_attrs) && isset($block_attrs['url']) && in_array($block_attrs['url'], $fse_blocked_nav_links)) {
971 //unset custom links
972 $removeable_keys[] = $key_offset;
973 } elseif (in_array($block_name, ['core/page-list'])) {
974 //unset page list nav block
975 $removeable_keys[] = $key_offset;
976
977 //we need to list all non-editable pagelist pages and add them as block to enable removal
978 $parent_page_id = isset($block_attrs['parentPageID']) ? (int) $block_attrs['parentPageID'] : 0;
979 $all_pages = get_pages(
980 [
981 'sort_column' => 'menu_order,post_title',
982 'order' => 'asc',
983 ]
984 );
985
986 if (!empty($all_pages)) {
987 $top_level_pages = [];
988 $pages_with_children = [];
989 foreach ((array) $all_pages as $page) {
990 $page_link = get_permalink($page->ID);
991 //only add pages not blocked as navigation link
992 if (!in_array($page_link, $fse_blocked_nav_links)) {
993 if ($page->post_parent) {
994 $pages_with_children[ $page->post_parent ][ $page->ID ] = [
995 'blockName' => 'core/navigation-link',
996 'attrs' => [
997 'label' => $page->post_title,
998 'type' => 'page',
999 'kind' => 'post-type',
1000 'id' => $page->ID,
1001 'url' => $page_link,
1002 ],
1003 'innerBlocks' => [],
1004 'innerHTML' => '',
1005 'innerContent' => [],
1006 ];
1007 } else {
1008 $top_level_pages[ $page->ID ] = [
1009 'blockName' => 'core/navigation-link',
1010 'attrs' => [
1011 'label' => $page->post_title,
1012 'type' => 'page',
1013 'kind' => 'post-type',
1014 'id' => $page->ID,
1015 'url' => $page_link,
1016 'isTopLevelLink' => 1
1017 ],
1018 'innerBlocks' => [],
1019 'innerHTML' => '',
1020 'innerContent' => [],
1021 ];
1022 }
1023 }
1024 }
1025
1026 $new_page_list_items = ppc_block_core_page_list_nest_pages( $top_level_pages, $pages_with_children );
1027 if ( 0 !== $parent_page_id ) {
1028 if (array_key_exists($parent_page_id, $pages_with_children)) {
1029 $new_page_list_items = ppc_block_core_page_list_nest_pages(
1030 $pages_with_children[$parent_page_id],
1031 $pages_with_children
1032 );
1033 } else {
1034 // If the parent page has no child pages, there is nothing to show.
1035 $new_page_list_items = [];
1036 }
1037 }
1038 }
1039 } else {
1040 //this is probably a block we currently not supporting
1041 }
1042 }
1043 }
1044
1045 //unset using block function
1046 foreach (array_values($removeable_keys) as $removeable_keys) {
1047 $inner_blocks->offsetUnset($removeable_keys);
1048 }
1049 //add new navigation links that wasn't removed from page list to block
1050 if (!empty($new_page_list_items)) {
1051 foreach ($new_page_list_items as $new_page_list_item) {
1052 $inner_blocks->offsetSet(null, $new_page_list_item);
1053 }
1054 }
1055 }
1056
1057 return $inner_blocks;
1058 }
1059 add_filter('block_core_navigation_render_inner_blocks', 'pp_capabilities_fse_nav_menu_permission', 999);
1060
1061 /**
1062 * Outputs nested array of pages inner blocks
1063 *
1064 * @param array $current_level The level being iterated through.
1065 * @param array $children The children grouped by parent post ID.
1066 *
1067 * @return array The nested array of pages.
1068 */
1069 function ppc_block_core_page_list_nest_pages( $current_level, $children ) {
1070 if ( empty( $current_level ) ) {
1071 return;
1072 }
1073 foreach ( (array) $current_level as $key => $current ) {
1074 if ( isset( $children[ $key ] ) ) {
1075 $current_level[ $key ]['innerBlocks'] = ppc_block_core_page_list_nest_pages($children[ $key ], $children);
1076 }
1077 }
1078 return $current_level;
1079 }
1080
1081 /**
1082 * Checks the menu items for their privacy and remove
1083 * if user do not have permission to item
1084 *
1085 */
1086 function pp_capabilities_nav_menu_access($query)
1087 {
1088 global $ppc_nav_menu_restricted, $ppc_disabled_nav_menu_data;
1089
1090 //this function is getting called many times. So, it's needed
1091 if ($ppc_nav_menu_restricted || !pp_capabilities_feature_enabled('nav-menus')) {
1092 return;
1093 }
1094
1095 if (!function_exists('wp_get_current_user')) {
1096 return;
1097 }
1098
1099 if (!is_array($ppc_disabled_nav_menu_data)) {
1100 //we want to make sure we're not running disabled data check multiple times for each filter
1101 $ppc_disabled_nav_menu_data = [];
1102 }
1103
1104 if (isset($ppc_disabled_nav_menu_data['nav_menu_item_option'])) {
1105 $nav_menu_item_option = $ppc_disabled_nav_menu_data['nav_menu_item_option'];
1106 } else {
1107 $nav_menu_item_option = !empty(get_option('capsman_nav_item_menus')) ? (array)get_option('capsman_nav_item_menus') : [];
1108 $ppc_disabled_nav_menu_data['nav_menu_item_option'] = $nav_menu_item_option;
1109 }
1110
1111 if (!$nav_menu_item_option || !function_exists('wp_get_current_user')) {
1112 return;
1113 }
1114
1115 if (isset($ppc_disabled_nav_menu_data['disabled_nav_menu'])) {
1116 $disabled_nav_menu = $ppc_disabled_nav_menu_data['disabled_nav_menu'];
1117 } else {
1118 $user_roles = (array)wp_get_current_user()->roles;
1119 //add loggedin and guest option to role
1120 $user_roles[] = (is_user_logged_in()) ? 'ppc_users' : 'ppc_guest';
1121 // Support plugin integrations by allowing additional role-based limitations to be applied to user based on external criteria
1122 $user_roles = apply_filters('pp_capabilities_nav_menu_access_role_restrictions', $user_roles);
1123 $disabled_nav_menu = '';
1124 //extract disabled menu for roles user belong
1125 foreach ($user_roles as $role) {
1126 if (array_key_exists($role, $nav_menu_item_option)) {
1127 $disabled_nav_menu .= implode(", ", (array)$nav_menu_item_option[$role]) . ', ';
1128 }
1129 }
1130 $ppc_disabled_nav_menu_data['disabled_nav_menu'] = $disabled_nav_menu;
1131 }
1132
1133 if ($disabled_nav_menu) {
1134 if (isset($ppc_disabled_nav_menu_data['fse_theme'])) {
1135 $fse_theme = $ppc_disabled_nav_menu_data['fse_theme'];
1136 $disabled_object = $ppc_disabled_nav_menu_data['disabled_object'];
1137 $fse_blocked_nav_links = $ppc_disabled_nav_menu_data['fse_blocked_nav_links'];
1138 $disabled_nav_menu_array = $ppc_disabled_nav_menu_data['disabled_nav_menu_array'];
1139 } else {
1140 $fse_theme = pp_capabilities_is_block_theme();
1141 //we only need object id and object name e.g, 1_category
1142 if ($fse_theme) {
1143 $disabled_object = preg_replace("/(\|)(.*?)(\|)/", '_', $disabled_nav_menu);
1144 //get all urls for css, js and direct block implementation
1145 preg_match_all("/\|\s*(.*?)\s*\|/", $disabled_nav_menu, $matches);
1146 $fse_blocked_nav_links = array_filter($matches[1]);
1147 } else {
1148 //native nav uses _ separator
1149 $disabled_object = preg_replace('!(0|[1-9][0-9]*)_([a-zA-Z0-9_.-]*),!s', '$2,', $disabled_nav_menu);
1150 $fse_blocked_nav_links = [];
1151 }
1152 $disabled_nav_menu_array = array_filter(explode(", ", $disabled_object));
1153
1154 $ppc_disabled_nav_menu_data['fse_theme'] = $fse_theme;
1155 $ppc_disabled_nav_menu_data['disabled_object'] = $disabled_object;
1156 $ppc_disabled_nav_menu_data['fse_blocked_nav_links'] = $fse_blocked_nav_links;
1157 $ppc_disabled_nav_menu_data['disabled_nav_menu_array'] = $disabled_nav_menu_array;
1158 }
1159
1160 //category tags and taxonomy page check
1161 if (is_category() || is_tag() || is_tax()) {
1162 $taxonomy_id = get_queried_object()->term_id;
1163 $taxonnomy_type = get_queried_object()->taxonomy;
1164 $option_object = $taxonomy_id . '_' . $taxonnomy_type;
1165 if (in_array($option_object, $disabled_nav_menu_array)) {
1166 $ppc_nav_menu_restricted = true;
1167 pp_capabilities_nav_menu_access_denied();
1168 }
1169 }
1170
1171 //post, page, cpt check
1172 if (is_singular()) {
1173 $post_type = get_post_type();
1174 $post_id = get_the_ID();
1175 $option_object = $post_id . '_' . $post_type;
1176 if (in_array($option_object, $disabled_nav_menu_array)) {
1177 $ppc_nav_menu_restricted = true;
1178 pp_capabilities_nav_menu_access_denied();
1179 }
1180 }
1181
1182 $ppc_nav_menu_restricted = true;
1183
1184 if (!empty($fse_blocked_nav_links)) {
1185 //restrict access to url
1186 if (in_array(pp_capabilities_current_url(), $fse_blocked_nav_links)) {
1187 $ppc_nav_menu_restricted = true;
1188 pp_capabilities_nav_menu_access_denied();
1189 }
1190 //hide menu item immediately, remove menu item from li after page load
1191 add_action('wp_head', function() use ($fse_blocked_nav_links) {
1192 $menu_item_selectors = array_map('pp_capabilities_nav_link_selector', $fse_blocked_nav_links);
1193 ?>
1194 <style>
1195 <?php echo join(', ', $menu_item_selectors); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
1196 {
1197 display: none !important;
1198 }
1199 </style>
1200
1201 <script>
1202 jQuery(document).ready( function($) {
1203 $('<?php echo join(', ', $menu_item_selectors); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>').each( function() {
1204 $(this).closest('li').remove();
1205 $(this).remove();
1206 });
1207 });
1208 </script>
1209 <?php
1210 });
1211 }
1212
1213 }
1214 }
1215 add_action('parse_query', 'pp_capabilities_nav_menu_access');
1216 }
1217
1218 /**
1219 * Generate style link selector for a nav link
1220 *
1221 * @param string $url
1222 * @return string
1223 */
1224 function pp_capabilities_nav_link_selector($url) {
1225
1226 $link_selector = 'li.wp-block-navigation-item a[href*="'. $url .'"]';
1227 if ($url === 'core/search') {
1228 $link_selector = '.wp-block-navigation .wp-block-search';
1229 } elseif ($url === 'core/site-logo') {
1230 $link_selector = 'li.wp-block-navigation-item .wp-block-site-logo';
1231 } elseif ($url === 'core/site-title') {
1232 $link_selector = '.wp-block-navigation .wp-block-site-title';
1233 } elseif ($url === 'core/social-links') {
1234 $link_selector = '.wp-block-navigation .wp-block-social-links';
1235 } elseif ($url === 'core/home-link') {
1236 $link_selector = '.wp-block-navigation .wp-block-home-link';
1237 }
1238
1239 return $link_selector;
1240 }
1241
1242 /**
1243 * Get current page URL
1244 *
1245 * @return string
1246 */
1247 function pp_capabilities_current_url()
1248 {
1249 if (!empty($_SERVER['HTTP_HOST']) && !empty($_SERVER['REQUEST_URI'])) {
1250 return esc_url_raw((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http") . "://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
1251 } else {
1252 return home_url('');
1253 }
1254 }
1255
1256 /**
1257 * Check if a feature is enabled
1258 *
1259 * @param integer $feature
1260 *
1261 * @return bool
1262 */
1263 function pp_capabilities_feature_enabled($feature) {
1264 global $capsman_dashboard_features_status;
1265
1266 //let use global settings incase this request is made more than once in a page load
1267 if (!is_array($capsman_dashboard_features_status)) {
1268 $capsman_dashboard_features_status = !empty(get_option('capsman_dashboard_features_status')) ? (array)get_option('capsman_dashboard_features_status') : [];
1269 }
1270
1271 //let enable all feature by default
1272 $feature_enabled = true;
1273 if (isset($capsman_dashboard_features_status[$feature])
1274 && isset($capsman_dashboard_features_status[$feature]['status'])
1275 && $capsman_dashboard_features_status[$feature]['status'] === 'off'
1276 ) {
1277 $feature_enabled = false;
1278 }
1279
1280 return $feature_enabled;
1281 }