PluginProbe
PatternsWP – Gutenberg Block Patterns & Page Templates Library / 1.0.7
PatternsWP – Gutenberg Block Patterns & Page Templates Library v1.0.7
trunk 1.0.0 1.0.1 1.0.10 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9
← All changes | includes/class-patternswp-admin.php +14 -856 trunk1.0.7 View file →
@@ -2,13 +2,8 @@
2 2 /**
3 3 * Admin Page
4 4 */
5 5
6 -// Exit if accessed directly
7 -if (!defined('ABSPATH')) {
8 - exit;
9 -}
10 -
11 6 class PatternsWP_Admin {
12 7
13 8 /**
14 9 * Constructor
@@ -16,64 +11,15 @@
16 11 public function __construct() {
17 12 // Add menu and pages
18 13 add_action('admin_menu', array($this, 'add_admin_menu'));
19 14 add_action('admin_init', array($this, 'patternswp_clear_cache'));
20 - add_action('admin_init', array($this, 'patternswp_ensure_hourly_cron'));
21 - add_action('wp_ajax_patternswp_background_transient_load_ajax', array($this, 'patternswp_background_transient_load_ajaxcc'));
22 - add_action('wp_ajax_nopriv_patternswp_background_transient_load_ajax', array($this, 'patternswp_background_transient_load_ajaxcc'));
23 - add_action('patternswp_load_patterns_by_ra', array($this, 'patternswp_load_patterns_by_remote_ajax'));
15 + add_action( 'admin_init', array( $this, 'patternswp_ensure_hourly_cron' ) );
16 + add_action( 'wp_ajax_patternswp_background_transient_load_ajax', array( $this, 'patternswp_background_transient_load_ajaxcc' ) );
17 + add_action( 'wp_ajax_nopriv_patternswp_background_transient_load_ajax', array( $this, 'patternswp_background_transient_load_ajaxcc' ) );
18 + add_action( 'patternswp_load_patterns_by_ra', array( $this, 'patternswp_load_patterns_by_remote_ajax' ) );
24 19 register_activation_hook(plugin_dir_path(__DIR__) . 'patternswp.php', array($this, 'patternswp_on_activation'));
25 20 add_action('admin_init', array($this, 'patternswp_redirect_on_activation'));
26 -
27 - // Initialize settings
28 - add_action('admin_init', array($this, 'register_pattern_visibility_settings'));
29 -
30 - // Add pattern deregistration hooks
31 - add_action('init', array($this, 'maybe_deregister_core_patterns'), 999);
32 - add_action('init', array($this, 'maybe_deregister_theme_patterns'), 1000);
33 - add_action('init', array($this, 'maybe_deregister_uncategorized_patterns'), 1001);
34 -
35 - // Add a second check later to ensure patterns are deregistered
36 - add_action('init', array($this, 'maybe_deregister_theme_patterns'), 9999);
37 - add_action('init', array($this, 'maybe_deregister_uncategorized_patterns'), 10000);
38 - add_action('init', array($this, 'maybe_deregister_core_patterns'), 10001);
39 -
40 - // error_log('[PatternsWP] Constructor - All pattern deregistration hooks registered');
41 -
42 - // Apply filters for different pattern sources
43 - add_filter('patternswp_patterns', array($this, 'filter_patterns_by_visibility'), 20);
44 - add_filter('block_editor_settings_all', array($this, 'filter_block_editor_settings'), 10, 2);
45 -
46 - // Filter REST API patterns
47 - add_filter('rest_wp_block_query', array($this, 'filter_rest_patterns'), 10, 2);
48 -
49 - // Handle core and theme patterns
50 - add_filter('should_load_remote_block_patterns', array($this, 'maybe_disable_remote_patterns'), 10, 1);
51 - add_filter('should_load_remote_patterns_default_override', array($this, 'maybe_disable_remote_patterns'), 10, 1);
52 -
53 - // Add action to clear pattern cache when theme changes
54 - add_action('switch_theme', array($this, 'clear_pattern_cache_on_theme_switch'));
55 -
56 - // Also filter the REST API response directly
57 - add_filter('rest_prepare_wp_block', array($this, 'filter_rest_block_response'), 10, 3);
58 -
59 - // Filter the block patterns list in the editor
60 - add_filter('wp_block_patterns', array($this, 'filter_block_patterns_list'), 10, 1);
61 -
62 - // Add a test hook to verify settings are loaded
63 - add_action('wp_loaded', array($this, 'test_settings'));
64 21 }
65 -
66 - /**
67 - * Test method to verify settings are loaded
68 - */
69 - public function test_settings() {
70 - $hide_theme = get_option('patternswp_hide_theme_patterns', false);
71 - $hide_uncategorized = get_option('patternswp_hide_uncategorized_patterns', false);
72 - $hide_core = get_option('patternswp_hide_core_patterns', false);
73 -
74 - // error_log('[PatternsWP] Settings Check - Theme: ' . ($hide_theme ? 'true' : 'false') . ', Uncategorized: ' . ($hide_uncategorized ? 'true' : 'false') . ', Core: ' . ($hide_core ? 'true' : 'false'));
75 - }
76 22
77 23 /**
78 24 * Add admin menu
79 25 */
@@ -101,10 +47,10 @@
101 47 );
102 48
103 49 // Add subpages
104 50 $this->add_subpage($menu_slug, 'Dashboard', 'Dashboard', 'patternswp-plugin-menu', array($this, 'render_main_page'));
105 - $this->add_subpage($menu_slug, 'Settings', 'Settings', 'patternswp-settings', array($this, 'render_pattern_visibility_page'));
106 51 $this->add_subpage($menu_slug, 'Support', 'Support', 'patternswp-plugin-page-1', array($this, 'patternswp_support'));
52 + $this->add_subpage($menu_slug, 'Clear Cache', 'Clear Cache', 'patternswp-clear-cache', array($this, 'patternswp_clear_cache_form'));
107 53 }
108 54
109 55 /**
110 56 * Add subpage
@@ -119,29 +65,15 @@
119 65 */
120 66 public function patterswp_tab( $tab ) {
121 67 $tabs = array(
122 68 'Dashboard' => 'patternswp-plugin-menu',
123 - 'Settings' => 'patternswp-settings',
124 69 'Support' => 'patternswp-plugin-page-1',
70 + 'Clear Cache' => 'patternswp-clear-cache',
125 71 'License' => 'patternswp-license_section'
126 72 );
127 73
128 - // Get the current page from URL parameter
129 - $current_page = isset($_GET['page']) ? sanitize_text_field($_GET['page']) : 'patternswp-plugin-menu';
130 -
131 - // Map page slugs to tab names
132 - $page_to_tab = array(
133 - 'patternswp-plugin-menu' => 'Dashboard',
134 - 'patternswp-settings' => 'Settings',
135 - 'patternswp-plugin-page-1' => 'Support',
136 - 'patternswp-license_section' => 'License'
137 - );
138 -
139 - // Determine current tab
140 - $current_tab = isset($page_to_tab[$current_page]) ? $page_to_tab[$current_page] : 'Dashboard';
141 -
142 74 foreach ($tabs as $name => $slug) {
143 - $class = ( $current_tab === $name ) ? 'nav-tab-active' : '';
75 + $class = ( $tab === $name ) ? 'nav-tab-active' : '';
144 76 echo '<a href="?page=' . esc_attr($slug) . '" class="nav-tab ' . esc_attr($class) . '">' . esc_html($name) . '</a>';
145 77 }
146 78 }
147 79
@@ -287,9 +219,9 @@
287 219 </h2>
288 220 <div class="patterns-wp-tabs-content">
289 221 <div id="tab-1" class="patterns-wp-tab-content patterns-wp-tab-active">
290 222 <div class="wrap">
291 - <div class="pw-feature-box big-box" style="max-width: 1280px; padding: 40px; background-color: white; border-radius: 10px; overflow: hidden; margin: 0 auto;">
223 + <div class="container" style="padding: 20px; background-color: white; border-radius: 5px; overflow: hidden; width: 70%;">
292 224 <div class="">
293 225 <form id="patternswp_clearcache_form" method="post">
294 226 <?php wp_nonce_field('patternswp_clear_cache_action', 'patternswp_clear_cache_nonce'); ?>
295 227 <input name="patternswp_clear_cache" type="submit" class="button button-primary" value="Clear Cache">
@@ -304,37 +236,8 @@
304 236 </div>
305 237 <?php }
306 238
307 239 /**
308 - * Clear pattern cache when theme is switched
309 - */
310 - public function clear_pattern_cache_on_theme_switch() {
311 - $this->clear_pattern_cache(false, false);
312 - }
313 -
314 - /**
315 - * Handle remote pattern loading based on settings
316 - */
317 - public function maybe_disable_remote_patterns($should_load) {
318 - if (get_option('patternswp_hide_core_patterns', false)) {
319 - return false;
320 - }
321 - return $should_load;
322 - }
323 -
324 - /**
325 - * Filter block editor settings to handle pattern visibility
326 - */
327 - public function filter_block_editor_settings($settings, $context) {
328 - if (get_option('patternswp_hide_core_patterns', false)) {
329 - $settings['__experimentalBlockPatterns'] = array();
330 - $settings['__experimentalBlockPatternCategories'] = array();
331 - $settings['__experimentalBlockPatterns'] = array();
332 - }
333 - return $settings;
334 - }
335 -
336 - /**
337 240 * Clear Cache
338 241 */
339 242 public function patternswp_clear_cache() {
340 243 global $wpdb;
@@ -376,16 +279,16 @@
376 279 delete_option($transient);
377 280 delete_option(str_replace('_transient_', '_transient_timeout_', $transient));
378 281 }
379 282
380 - // Load patterns in background
283 + //load patterns in background
381 284 $this->patternswp_load_patterns_by_remote_ajax();
382 285
383 - // Set a transient for the success message
384 - set_transient('patternswp_cache_cleared', 'Cache cleared successfully', 5);
385 -
386 - // Redirect to Settings page
387 - wp_safe_redirect(admin_url('admin.php?page=patternswp-settings'));
286 + wp_redirect(add_query_arg(array(
287 + 'page' => 'patternswp-clear-cache',
288 + 'pt_msg' => urlencode('Clear Cache Successfully'),
289 + 'status' => 'success'
290 + ), admin_url('admin.php')));
388 291 exit;
389 292 }
390 293 }
391 294
@@ -423,753 +326,8 @@
423 326 * Set transient on plugin activation
424 327 */
425 328 public function patternswp_on_activation() {
426 329 set_transient('patternswp_activation_redirect', true, 30);
427 - }
428 -
429 - /**
430 - * Clear pattern cache when visibility settings change
431 - */
432 - public function clear_pattern_cache($old_value, $new_value) {
433 - // Clear object cache first
434 - if (function_exists('wp_cache_flush')) {
435 - wp_cache_flush();
436 - }
437 -
438 - // Clear known transients
439 - $transients = [
440 - 'patternswp_all_patterns',
441 - 'patternswp_categorized_patterns',
442 - 'patternswp_patterns_data',
443 - 'patternswp_categories'
444 - ];
445 -
446 - foreach ($transients as $transient) {
447 - delete_transient($transient);
448 - }
449 -
450 - // Clear any remaining transients with our prefix
451 - if (!function_exists('delete_expired_transients')) {
452 - require_once ABSPATH . 'wp-admin/includes/plugin.php';
453 - }
454 -
455 - // This is a core function that will clean up expired transients
456 - delete_expired_transients(true);
457 -
458 - // Clear specific pattern caches
459 - if (function_exists('wp_cache_delete')) {
460 - wp_cache_delete('all_patterns', 'patternswp');
461 - wp_cache_delete('categorized_patterns', 'patternswp');
462 - }
463 - }
464 -
465 - /**
466 - * Attempt to deregister any patterns from the active or child theme.
467 - */
468 - public function maybe_deregister_theme_patterns() {
469 - $hide_theme_patterns = get_option('patternswp_hide_theme_patterns', false);
470 - // error_log('[PatternsWP] maybe_deregister_theme_patterns called - hide_theme_patterns: ' . ($hide_theme_patterns ? 'true' : 'false'));
471 -
472 - if (!$hide_theme_patterns) {
473 - // error_log('[PatternsWP] Theme pattern hiding is disabled');
474 - return;
475 - }
476 -
477 - // Get the active theme.
478 - $theme = wp_get_theme();
479 -
480 - // Let's get some theme data.
481 - $paths_to_search = array(
482 - $theme->stylesheet,
483 - );
484 -
485 - // Check if this is a child theme.
486 - if ($theme->parent()) {
487 - $parent_theme = wp_get_theme($theme->parent()->template);
488 - $paths_to_search[] = $parent_theme->template;
489 - }
490 - $paths_to_search = array_unique($paths_to_search);
491 -
492 - // error_log('[PatternsWP] Theme paths to search: ' . implode(', ', $paths_to_search));
493 -
494 - // Get all registered patterns.
495 - $patterns = \WP_Block_Patterns_Registry::get_instance();
496 - $all_patterns = $patterns->get_all_registered();
497 -
498 - // error_log('[PatternsWP] Total registered patterns: ' . count($all_patterns));
499 -
500 - // Loop through all patterns and deregister any that are from the active or child theme.
501 - foreach ($all_patterns as $index => $pattern) {
502 - $file_path = $pattern['filePath'] ?? '';
503 - if (empty($file_path)) {
504 - continue;
505 - }
506 -
507 - // Check if the pattern is from the active or child theme.
508 - foreach ($paths_to_search as $path) {
509 - if (false !== strpos($file_path, 'themes/' . $path)) {
510 - unregister_block_pattern($pattern['name']);
511 - // error_log('[PatternsWP] Deregistered theme pattern: ' . $pattern['name'] . ' from path: ' . $file_path);
512 - }
513 - }
514 - }
515 - }
516 -
517 - /**
518 - * Deregister any uncategorized patterns.
519 - */
520 - public function maybe_deregister_uncategorized_patterns() {
521 - $hide_uncategorized_enabled = get_option('patternswp_hide_uncategorized_patterns', false);
522 - // error_log('[PatternsWP] maybe_deregister_uncategorized_patterns called - hide_uncategorized: ' . ($hide_uncategorized_enabled ? 'true' : 'false'));
523 -
524 - // Exit early if not enabled.
525 - if (!$hide_uncategorized_enabled) {
526 - // error_log('[PatternsWP] Uncategorized pattern hiding is disabled');
527 - return;
528 - }
529 -
530 - // Retrieve all patterns.
531 - $patterns = \WP_Block_Patterns_Registry::get_instance();
532 - $all_patterns = $patterns->get_all_registered();
533 -
534 - // error_log('[PatternsWP] Checking ' . count($all_patterns) . ' patterns for uncategorized ones');
535 -
536 - // Loop through all patterns and deregister any that are uncategorized.
537 - foreach ($all_patterns as $index => $pattern) {
538 - if (!isset($pattern['categories']) || empty($pattern['categories'])) {
539 - unregister_block_pattern($pattern['name']);
540 - // error_log('[PatternsWP] Deregistered uncategorized pattern: ' . $pattern['name']);
541 - } else {
542 - $found = false;
543 - $block_categories = $pattern['categories'] ?? array();
544 - foreach ($block_categories as $block_category) {
545 - $categories = \WP_Block_Pattern_Categories_Registry::get_instance();
546 - if ($categories->is_registered($block_category)) {
547 - $found = true;
548 - }
549 - }
550 - if (!$found) {
551 - unregister_block_pattern($pattern['name']);
552 - // error_log('[PatternsWP] Deregistered uncategorized pattern (invalid category): ' . $pattern['name']);
553 - }
554 - }
555 - }
556 - }
557 -
558 - /**
559 - * Deregister core patterns.
560 - */
561 - public function maybe_deregister_core_patterns() {
562 - $hide_core_patterns = get_option('patternswp_hide_core_patterns', false);
563 - // error_log('[PatternsWP] maybe_deregister_core_patterns called - hide_core_patterns: ' . ($hide_core_patterns ? 'true' : 'false'));
564 -
565 - // Exit early if not enabled.
566 - if (!$hide_core_patterns) {
567 - // error_log('[PatternsWP] Core pattern hiding is disabled');
568 - return;
569 - }
570 -
571 - // Retrieve all patterns.
572 - $patterns = \WP_Block_Patterns_Registry::get_instance();
573 - $all_patterns = $patterns->get_all_registered();
574 -
575 - // error_log('[PatternsWP] Checking ' . count($all_patterns) . ' patterns for core ones');
576 -
577 - // Loop through all patterns and deregister any that are core patterns.
578 - foreach ($all_patterns as $index => $pattern) {
579 - // Core patterns typically have names starting with 'core/' or no file path
580 - $pattern_name = $pattern['name'] ?? '';
581 - $file_path = $pattern['filePath'] ?? '';
582 -
583 - // Check if it's a core pattern
584 - $is_core_pattern = false;
585 - $reason = '';
586 -
587 - // Method 1: Check if pattern name starts with 'core/'
588 - if (strpos($pattern_name, 'core/') === 0) {
589 - $is_core_pattern = true;
590 - $reason = 'name starts with core/';
591 - }
592 -
593 - // Method 2: Check if file path contains wp-includes
594 - if (!$is_core_pattern && !empty($file_path) && strpos($file_path, 'wp-includes') !== false) {
595 - $is_core_pattern = true;
596 - $reason = 'file path contains wp-includes';
597 - }
598 -
599 - // Method 3: Check if no file path (likely core pattern)
600 - if (!$is_core_pattern && empty($file_path)) {
601 - $is_core_pattern = true;
602 - $reason = 'no file path';
603 - }
604 -
605 - if ($is_core_pattern) {
606 - unregister_block_pattern($pattern['name']);
607 - // error_log('[PatternsWP] Deregistered core pattern: ' . $pattern['name'] . ' (' . $reason . ')');
608 - }
609 - }
610 - }
611 -
612 - /**
613 - * Filter the block patterns list in the editor
614 - */
615 - public function filter_block_patterns_list($patterns) {
616 - // error_log('[PatternsWP] filter_block_patterns_list called with ' . count($patterns) . ' patterns');
617 -
618 - $hide_theme_patterns = get_option('patternswp_hide_theme_patterns', false);
619 - $hide_uncategorized = get_option('patternswp_hide_uncategorized_patterns', false);
620 - $hide_core_patterns = get_option('patternswp_hide_core_patterns', false);
621 -
622 - // error_log('[PatternsWP] Block patterns filter - Theme: ' . ($hide_theme_patterns ? 'true' : 'false') . ', Uncategorized: ' . ($hide_uncategorized ? 'true' : 'false') . ', Core: ' . ($hide_core_patterns ? 'true' : 'false'));
623 -
624 - // If no filters are enabled, return original patterns
625 - if (!$hide_theme_patterns && !$hide_uncategorized && !$hide_core_patterns) {
626 - return $patterns;
627 - }
628 -
629 - $filtered_patterns = array();
630 -
631 - foreach ($patterns as $pattern) {
632 - $should_include = true;
633 -
634 - // Check theme patterns
635 - if ($hide_theme_patterns && $should_include) {
636 - // Check if pattern name starts with theme prefix
637 - if (isset($pattern['name']) && strpos($pattern['name'], 'theme-') === 0) {
638 - $should_include = false;
639 - // error_log('[PatternsWP] Excluding theme pattern by name: ' . $pattern['name']);
640 - }
641 - // Check if pattern has theme file path
642 - if (isset($pattern['filePath']) && strpos($pattern['filePath'], 'themes/') !== false) {
643 - $should_include = false;
644 - // error_log('[PatternsWP] Excluding theme pattern by path: ' . $pattern['name']);
645 - }
646 - }
647 -
648 - // Check uncategorized patterns
649 - if ($hide_uncategorized && $should_include) {
650 - if (!isset($pattern['categories']) || empty($pattern['categories'])) {
651 - $should_include = false;
652 - // error_log('[PatternsWP] Excluding uncategorized pattern: ' . $pattern['name']);
653 - }
654 - }
655 -
656 - // Check core patterns
657 - if ($hide_core_patterns && $should_include) {
658 - if (isset($pattern['name']) && strpos($pattern['name'], 'core/') === 0) {
659 - $should_include = false;
660 - // error_log('[PatternsWP] Excluding core pattern: ' . $pattern['name']);
661 - }
662 - }
663 -
664 - if ($should_include) {
665 - $filtered_patterns[] = $pattern;
666 - }
667 - }
668 -
669 - // error_log('[PatternsWP] Filtered from ' . count($patterns) . ' to ' . count($filtered_patterns) . ' patterns');
670 -
671 - return $filtered_patterns;
672 - }
673 -
674 - /**
675 - * Filter REST API response for individual blocks
676 - */
677 - public function filter_rest_block_response($response, $post, $request) {
678 - // Only filter in the block editor context
679 - if (!isset($request['context']) || $request['context'] !== 'edit') {
680 - return $response;
681 - }
682 -
683 - $hide_theme_patterns = get_option('patternswp_hide_theme_patterns', false);
684 - $hide_uncategorized = get_option('patternswp_hide_uncategorized_patterns', false);
685 - $hide_core_patterns = get_option('patternswp_hide_core_patterns', false);
686 -
687 - // If no filters are enabled, return original response
688 - if (!$hide_theme_patterns && !$hide_uncategorized && !$hide_core_patterns) {
689 - return $response;
690 - }
691 -
692 - // Get the pattern data
693 - $pattern_data = $response->data;
694 -
695 - // Check if this should be hidden
696 - $should_hide = false;
697 -
698 - // Check theme patterns
699 - if ($hide_theme_patterns) {
700 - // Check if pattern has theme-specific metadata
701 - if (isset($pattern_data['patternswp_source']) && $pattern_data['patternswp_source'] === 'theme') {
702 - $should_hide = true;
703 - }
704 - }
705 -
706 - // Check uncategorized patterns
707 - if (!$should_hide && $hide_uncategorized) {
708 - if (empty($pattern_data['pattern_categories'])) {
709 - $should_hide = true;
710 - }
711 - }
712 -
713 - // Check core patterns
714 - if (!$should_hide && $hide_core_patterns) {
715 - if (isset($pattern_data['name']) && strpos($pattern_data['name'], 'core/') === 0) {
716 - $should_hide = true;
717 - }
718 - }
719 -
720 - if ($should_hide) {
721 - // error_log('[PatternsWP] Filtering REST response for pattern: ' . ($pattern_data['title'] ?? 'Unknown'));
722 - // Return an error response to hide this pattern
723 - return new WP_Error(
724 - 'rest_pattern_hidden',
725 - 'Pattern is hidden by visibility settings',
726 - array('status' => 404)
727 - );
728 - }
729 -
730 - return $response;
731 - }
732 -
733 - /**
734 - * Filter REST API patterns
735 - */
736 - public function filter_rest_patterns($args, $request) {
737 - // Only filter if we're in the block editor context
738 - if (!isset($request['context']) || $request['context'] !== 'edit') {
739 - return $args;
740 - }
741 -
742 - // Get the current registered patterns
743 - $patterns = \WP_Block_Patterns_Registry::get_instance();
744 - $all_patterns = $patterns->get_all_registered();
745 -
746 - $hide_theme_patterns = get_option('patternswp_hide_theme_patterns', false);
747 - $hide_uncategorized = get_option('patternswp_hide_uncategorized_patterns', false);
748 - $hide_core_patterns = get_option('patternswp_hide_core_patterns', false);
749 -
750 - // If no filters are enabled, return original args
751 - if (!$hide_theme_patterns && !$hide_uncategorized && !$hide_core_patterns) {
752 - return $args;
753 - }
754 -
755 - // Get theme info for theme pattern detection
756 - $theme = wp_get_theme();
757 - $paths_to_search = array($theme->stylesheet);
758 - if ($theme->parent()) {
759 - $parent_theme = wp_get_theme($theme->parent()->template);
760 - $paths_to_search[] = $parent_theme->template;
761 - }
762 - $paths_to_search = array_unique($paths_to_search);
763 -
764 - // Build list of pattern names to exclude
765 - $exclude_patterns = array();
766 -
767 - foreach ($all_patterns as $pattern) {
768 - $should_exclude = false;
769 -
770 - // Check if it's a theme pattern
771 - if ($hide_theme_patterns) {
772 - $file_path = $pattern['filePath'] ?? '';
773 - if (!empty($file_path)) {
774 - foreach ($paths_to_search as $path) {
775 - if (false !== strpos($file_path, 'themes/' . $path)) {
776 - $should_exclude = true;
777 - break;
778 - }
779 - }
780 - }
781 - }
782 -
783 - // Check if it's a core pattern
784 - if (!$should_exclude && $hide_core_patterns) {
785 - $pattern_name = $pattern['name'] ?? '';
786 - $file_path = $pattern['filePath'] ?? '';
787 -
788 - // Method 1: Check if pattern name starts with 'core/'
789 - if (strpos($pattern_name, 'core/') === 0) {
790 - $should_exclude = true;
791 - }
792 -
793 - // Method 2: Check if file path contains wp-includes
794 - if (!$should_exclude && !empty($file_path) && strpos($file_path, 'wp-includes') !== false) {
795 - $should_exclude = true;
796 - }
797 -
798 - // Method 3: Check if no file path (likely core pattern)
799 - if (!$should_exclude && empty($file_path)) {
800 - $should_exclude = true;
801 - }
802 - }
803 -
804 - // Check if it's uncategorized
805 - if (!$should_exclude && $hide_uncategorized) {
806 - if (!isset($pattern['categories']) || empty($pattern['categories'])) {
807 - $should_exclude = true;
808 - } else {
809 - $found = false;
810 - $block_categories = $pattern['categories'] ?? array();
811 - foreach ($block_categories as $block_category) {
812 - $categories = \WP_Block_Pattern_Categories_Registry::get_instance();
813 - if ($categories->is_registered($block_category)) {
814 - $found = true;
815 - break;
816 - }
817 - }
818 - if (!$found) {
819 - $should_exclude = true;
820 - }
821 - }
822 - }
823 -
824 - if ($should_exclude) {
825 - $exclude_patterns[] = $pattern['name'];
826 - }
827 - }
828 -
829 - // Add exclude filter using direct SQL for better performance
830 - if (!empty($exclude_patterns)) {
831 - add_filter('posts_where', function($where) use ($exclude_patterns) {
832 - global $wpdb;
833 -
834 - // Prepare placeholders for the IN clause
835 - $placeholders = array_fill(0, count($exclude_patterns), '%s');
836 - $placeholders = implode(',', $placeholders);
837 -
838 - // Prepare the exclusion subquery
839 - $exclude_sql = $wpdb->prepare(
840 - "SELECT post_id
841 - FROM {$wpdb->postmeta}
842 - WHERE meta_key = 'pattern_id'
843 - AND meta_value IN ($placeholders)",
844 - $exclude_patterns
845 - );
846 -
847 - // Add the exclusion to the WHERE clause
848 - $where .= " AND {$wpdb->posts}.ID NOT IN ($exclude_sql)";
849 -
850 - return $where;
851 - });
852 -
853 - // Ensure we don't use post__not_in or meta_query
854 - unset($args['post__not_in']);
855 - unset($args['meta_query']);
856 - }
857 -
858 - return $args;
859 - }
860 -
861 - /**
862 - * Filter patterns based on visibility settings
863 - */
864 - /**
865 - * Check if a pattern is a theme pattern
866 - */
867 - private function is_theme_pattern($pattern) {
868 - // Check source
869 - if (!empty($pattern['source']) && $pattern['source'] === 'theme') {
870 - return true;
871 - }
872 -
873 - // Check name prefix
874 - if (!empty($pattern['name']) && (strpos($pattern['name'], 'theme-') === 0 ||
875 - strpos($pattern['name'], 'tw/') === 0)) {
876 - return true;
877 - }
878 -
879 - // Check categories
880 - if (!empty($pattern['categories'])) {
881 - $categories = is_array($pattern['categories']) ?
882 - $pattern['categories'] :
883 - array_map('trim', explode(',', $pattern['categories']));
884 -
885 - foreach ($categories as $category) {
886 - if (is_string($category) &&
887 - (stripos($category, 'theme') !== false ||
888 - stripos($category, 'template') !== false)) {
889 - return true;
890 - }
891 - }
892 - }
893 -
894 - return false;
895 - }
896 -
897 - /**
898 - * Check if a pattern is a core pattern
899 - */
900 - private function is_core_pattern($pattern) {
901 - // Check source
902 - if (!empty($pattern['source']) && $pattern['source'] === 'core') {
903 - return true;
904 - }
905 -
906 - // Check name prefix
907 - if (!empty($pattern['name']) && strpos($pattern['name'], 'core/') === 0) {
908 - return true;
909 - }
910 -
911 - return false;
912 - }
913 -
914 - /**
915 - * Check if a pattern is uncategorized
916 - */
917 - private function is_uncategorized_pattern($pattern) {
918 - if (empty($pattern['categories'])) {
919 - return true;
920 - }
921 -
922 - $categories = is_array($pattern['categories']) ?
923 - $pattern['categories'] :
924 - array_filter(array_map('trim', explode(',', $pattern['categories'])));
925 -
926 - return empty($categories);
927 - }
928 -
929 - /**
930 - * Filter patterns based on visibility settings
931 - */
932 - public function filter_patterns_by_visibility($patterns) {
933 - // Debug logging
934 - // error_log('[PatternsWP] Filter applied with ' . count((array)$patterns) . ' patterns');
935 -
936 - // If no patterns or not an array, return early
937 - if (empty($patterns) || !is_array($patterns)) {
938 - // error_log('[PatternsWP] No patterns to filter');
939 - return $patterns;
940 - }
941 -
942 - $filtered_patterns = array();
943 - $hide_theme_patterns = get_option('patternswp_hide_theme_patterns', false);
944 - $hide_uncategorized = get_option('patternswp_hide_uncategorized_patterns', false);
945 - $hide_core_patterns = get_option('patternswp_hide_core_patterns', false);
946 -
947 - // error_log('[PatternsWP] Settings - Hide Theme: ' . ($hide_theme_patterns ? 'true' : 'false') .
948 - // ', Hide Uncategorized: ' . ($hide_uncategorized ? 'true' : 'false') .
949 - // ', Hide Core: ' . ($hide_core_patterns ? 'true' : 'false'));
950 -
951 - foreach ($patterns as $pattern) {
952 - // Skip if pattern is not an array
953 - if (!is_array($pattern)) {
954 - $filtered_patterns[] = $pattern;
955 - continue;
956 - }
957 -
958 - // Skip theme patterns if enabled
959 - if ($hide_theme_patterns && $this->is_theme_pattern($pattern)) {
960 - // error_log('[PatternsWP] Skipping theme pattern: ' . ($pattern['title'] ?? 'Unknown'));
961 - continue;
962 - }
963 -
964 - // Skip core patterns if enabled
965 - if ($hide_core_patterns && $this->is_core_pattern($pattern)) {
966 - // error_log('[PatternsWP] Skipping core pattern: ' . ($pattern['title'] ?? 'Unknown'));
967 - continue;
968 - }
969 -
970 - // Skip uncategorized patterns if enabled
971 - if ($hide_uncategorized && $this->is_uncategorized_pattern($pattern)) {
972 - // error_log('[PatternsWP] Skipping uncategorized pattern: ' . ($pattern['title'] ?? 'Unknown'));
973 - continue;
974 - }
975 -
976 - $filtered_patterns[] = $pattern;
977 - }
978 -
979 - // error_log('[PatternsWP] Filtered to ' . count($filtered_patterns) . ' patterns');
980 - return $filtered_patterns;
981 - }
982 -
983 - /**
984 - * Register Pattern Visibility settings
985 - */
986 - public function register_pattern_visibility_settings() {
987 - // Register settings
988 - register_setting('patternswp_visibility_settings', 'patternswp_hide_theme_patterns', array(
989 - 'type' => 'boolean',
990 - 'default' => false,
991 - 'sanitize_callback' => 'rest_sanitize_boolean',
992 - ));
993 -
994 - register_setting('patternswp_visibility_settings', 'patternswp_hide_uncategorized_patterns', array(
995 - 'type' => 'boolean',
996 - 'default' => false,
997 - 'sanitize_callback' => 'rest_sanitize_boolean',
998 - ));
999 -
1000 - register_setting('patternswp_visibility_settings', 'patternswp_hide_core_patterns', array(
1001 - 'type' => 'boolean',
1002 - 'default' => false,
1003 - 'sanitize_callback' => 'rest_sanitize_boolean',
1004 - ));
1005 -
1006 - // Clear cache when settings are updated
1007 - add_action('update_option_patternswp_hide_theme_patterns', array($this, 'clear_pattern_cache'), 10, 2);
1008 - add_action('update_option_patternswp_hide_uncategorized_patterns', array($this, 'clear_pattern_cache'), 10, 2);
1009 - add_action('update_option_patternswp_hide_core_patterns', array($this, 'clear_pattern_cache'), 10, 2);
1010 -
1011 - // Add settings section
1012 - add_settings_section(
1013 - 'patternswp_visibility_section',
1014 - __('Pattern Visibility', 'patternswp'),
1015 - array($this, 'pattern_visibility_section_callback'),
1016 - 'patternswp-settings'
1017 - );
1018 -
1019 - // Add settings fields
1020 - add_settings_field(
1021 - 'patternswp_hide_theme_patterns',
1022 - __('Hide Theme Patterns', 'patternswp'),
1023 - array($this, 'render_checkbox_field'),
1024 - 'patternswp-settings',
1025 - 'patternswp_visibility_section',
1026 - array(
1027 - 'label_for' => 'patternswp_hide_theme_patterns',
1028 - 'description' => __('Prevent patterns registered by the active theme from displaying in the patterns list.', 'patternswp')
1029 - )
1030 - );
1031 -
1032 - add_settings_field(
1033 - 'patternswp_hide_uncategorized_patterns',
1034 - __('Hide Uncategorized Patterns', 'patternswp'),
1035 - array($this, 'render_checkbox_field'),
1036 - 'patternswp-settings',
1037 - 'patternswp_visibility_section',
1038 - array(
1039 - 'label_for' => 'patternswp_hide_uncategorized_patterns',
1040 - 'description' => __('Prevent any patterns not in any registered categories from displaying.', 'patternswp')
1041 - )
1042 - );
1043 -
1044 - add_settings_field(
1045 - 'patternswp_hide_core_patterns',
1046 - __('Hide Core Patterns', 'patternswp'),
1047 - array($this, 'render_checkbox_field'),
1048 - 'patternswp-settings',
1049 - 'patternswp_visibility_section',
1050 - array(
1051 - 'label_for' => 'patternswp_hide_core_patterns',
1052 - 'description' => __('Remove all core patterns from the pattern selector by disabling core patterns.', 'patternswp')
1053 - )
1054 - );
1055 - }
1056 -
1057 - /**
1058 - * Pattern Visibility section callback
1059 - */
1060 - public function pattern_visibility_section_callback() {
1061 - echo '<p>' . esc_html__('Control which patterns are visible in the block editor.', 'patternswp') . '</p>';
1062 - }
1063 -
1064 - /**
1065 - * Render checkbox field
1066 - */
1067 - public function render_checkbox_field($args) {
1068 - $option = get_option($args['label_for']);
1069 - ?>
1070 - <label>
1071 - <input type="checkbox"
1072 - name="<?php echo esc_attr($args['label_for']); ?>"
1073 - value="1"
1074 - <?php checked(1, $option, true); ?>>
1075 - <span class="description"><?php echo esc_html($args['description']); ?></span>
1076 - </label>
1077 - <?php
1078 - }
1079 -
1080 - /**
1081 - * Render Pattern Visibility page
1082 - */
1083 - public function render_pattern_visibility_page() {
1084 - // Check user capabilities
1085 - if (!current_user_can('manage_options')) {
1086 - return;
1087 - }
1088 -
1089 - // Show success/error messages
1090 - if (isset($_GET['settings-updated'])) {
1091 - add_settings_error(
1092 - 'patternswp_messages',
1093 - 'patternswp_message',
1094 - __('Settings Saved', 'patternswp'),
1095 - 'updated'
1096 - );
1097 - }
1098 -
1099 - // Show cache clear messages from transient
1100 - $cache_message = get_transient('patternswp_cache_cleared');
1101 - if ($cache_message) {
1102 - add_settings_error(
1103 - 'patternswp_messages',
1104 - 'patternswp_cache_message',
1105 - $cache_message,
1106 - 'updated'
1107 - );
1108 - // Delete the transient so it only shows once
1109 - delete_transient('patternswp_cache_cleared');
1110 - } elseif (isset($_GET['pt_msg']) && isset($_GET['status'])) {
1111 - // Keep backward compatibility with URL parameters
1112 - $message = sanitize_text_field(wp_unslash($_GET['pt_msg']));
1113 - $status = sanitize_text_field(wp_unslash($_GET['status']));
1114 - $type = $status === 'success' ? 'updated' : 'error';
1115 -
1116 - add_settings_error(
1117 - 'patternswp_messages',
1118 - 'patternswp_cache_message',
1119 - $message,
1120 - $type
1121 - );
1122 - }
1123 -
1124 - settings_errors('patternswp_messages');
1125 - ?>
1126 - <div class="wrap">
1127 - <h1><?php echo esc_html(get_admin_page_title()); ?></h1>
1128 -
1129 - <h2 class="nav-tab-wrapper">
1130 - <?php
1131 - $this->patterswp_tab('');
1132 - ?>
1133 - </h2>
1134 - <div class="patterns-wp-tabs-content">
1135 - <div id="tab-1" class="patterns-wp-tab-content patterns-wp-tab-active">
1136 - <div class="wrap">
1137 - <div class="pw-feature-box big-box" style="max-width: 1280px; padding: 40px; background-color: white; border-radius: 10px; overflow: hidden; margin: 0 auto;">
1138 - <form action="options.php" method="post">
1139 - <?php
1140 - // Output security fields
1141 - settings_fields('patternswp_visibility_settings');
1142 - // Output settings sections and fields
1143 - do_settings_sections('patternswp-settings');
1144 - // Output save settings button
1145 - submit_button(__('Save Settings', 'patternswp'));
1146 - ?>
1147 - </form>
1148 -
1149 - <hr style="margin: 30px 0;">
1150 -
1151 - <h3><?php esc_html_e('Cache Management', 'patternswp'); ?></h3>
1152 - <p><?php esc_html_e('Clear all cached patterns and data. This may be necessary if patterns are not updating correctly or after changing visibility settings.', 'patternswp'); ?></p>
1153 -
1154 - <form method="post" action="">
1155 - <?php wp_nonce_field('patternswp_clear_cache_action', 'patternswp_clear_cache_nonce'); ?>
1156 - <input type="hidden" name="patternswp_clear_cache" value="1">
1157 - <?php
1158 - submit_button(
1159 - __('Clear All Cache', 'patternswp'),
1160 - 'primary',
1161 - 'patternswp_clear_cache',
1162 - false
1163 - );
1164 - ?>
1165 - </form>
1166 - </div>
1167 - </div>
1168 - </div>
1169 - </div>
1170 - </div>
1171 - <?php
1172 330 }
1173 331
1174 332 /**
1175 333 * Redirect to welcome page after activation