├── tests ├── _data │ └── .gitkeep ├── functional │ └── .gitkeep ├── _output │ └── .gitignore ├── _support │ ├── _generated │ │ └── .gitignore │ ├── Helper │ │ ├── Unit.php │ │ ├── Wpunit.php │ │ ├── Acceptance.php │ │ └── Functional.php │ ├── UnitTester.php │ ├── WpunitTester.php │ ├── AcceptanceTester.php │ └── FunctionalTester.php ├── acceptance │ ├── MyTestCest.php │ ├── PluginActivatedCest.php │ └── AdminPageCest.php ├── unit.suite.yml ├── wpunit │ ├── PluginTest.php │ ├── BlockPatternsManagerTest.php │ ├── AdminPageTest.php │ └── BlockPatternsListTableTest.php ├── wpunit.suite.yml ├── unit │ ├── PluginTest.php │ ├── AdminPageTest.php │ ├── bootstrap.php │ ├── BlockPatternsManagerTest.php │ └── RemotePatternsTest.php ├── functional.suite.yml └── acceptance.suite.yml ├── .gitignore ├── Readme.md ├── .env.testing ├── codeception.dist.yml ├── package.json ├── inc ├── Plugin.php ├── RemotePatterns.php ├── BlockPatternsManager.php ├── AdminPage.php └── BlockPatternsListTable.php ├── plugin.php └── composer.json /tests/_data/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/functional/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/_output/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /tests/_support/_generated/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | node_modules/ 3 | tmp 4 | .phpunit.result.cache -------------------------------------------------------------------------------- /tests/_support/Helper/Unit.php: -------------------------------------------------------------------------------- 1 | loginAsAdmin(); 16 | $I->amOnPluginsPage(); 17 | $I->seePluginActivated( 'block-patterns-manager' ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tests/wpunit/PluginTest.php: -------------------------------------------------------------------------------- 1 | plugin_settings_link([]); 23 | 24 | $this->assertIsArray($links); 25 | $this->assertEquals(count( $links ), 1); 26 | $this->assertIsString($links[0]); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /.env.testing: -------------------------------------------------------------------------------- 1 | TEST_SITE_DB_DSN=mysql:host=localhost;dbname=wordpress_test 2 | TEST_SITE_DB_HOST=localhost 3 | TEST_SITE_DB_NAME=wordpress_test 4 | TEST_SITE_DB_USER=werbeagenten 5 | TEST_SITE_DB_PASSWORD=secret 6 | TEST_SITE_TABLE_PREFIX=wp_ 7 | TEST_SITE_ADMIN_USERNAME=admin 8 | TEST_SITE_ADMIN_PASSWORD=admin 9 | TEST_SITE_WP_ADMIN_PATH=/wp-admin 10 | WP_ROOT_FOLDER=/Users/werbeagenten/Sites/wp58rc 11 | TEST_DB_NAME=test 12 | TEST_DB_HOST=localhost 13 | TEST_DB_USER=werbeagenten 14 | TEST_DB_PASSWORD=secret 15 | TEST_TABLE_PREFIX=wp_ 16 | TEST_SITE_WP_URL=https://wp58rc.test 17 | TEST_SITE_WP_DOMAIN=wp58rc.test 18 | TEST_SITE_ADMIN_EMAIL=admin@wp58rc.test 19 | -------------------------------------------------------------------------------- /tests/_support/UnitTester.php: -------------------------------------------------------------------------------- 1 | plugin_settings_link([]); 34 | 35 | $this->assertIsArray($links); 36 | $this->assertEquals(count( $links ), 1); 37 | $this->assertIsString($links[0]); 38 | } 39 | } -------------------------------------------------------------------------------- /tests/wpunit/BlockPatternsManagerTest.php: -------------------------------------------------------------------------------- 1 | register(); 27 | 28 | // global $wp_filter; 29 | // var_dump($wp_filter); 30 | 31 | 32 | // add_action('admin_init', 'test'); 33 | // // $this->assertTrue( has_action('admin_init', 'test' ) ); 34 | 35 | // die(print_r($GLOBALS['wp_filter']['admin_init'][0], true)); 36 | // } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /tests/unit/AdminPageTest.php: -------------------------------------------------------------------------------- 1 | make(BlockPatternsManager::class); 23 | } 24 | 25 | public function testShouldCreateErrorWhenInstantiatedWithoutBlockPatternsManager(): void 26 | { 27 | $this->expectException(Exception::class); 28 | new AdminPage( 'test' ); 29 | } 30 | 31 | public function testShouldReturnMenuSlug(): void 32 | { 33 | $expected_menu_slug = 'block-patterns-manager'; 34 | $received_menu_slug = AdminPage::get_menu_slug(); 35 | 36 | $this->assertEquals( 37 | $expected_menu_slug, 38 | $received_menu_slug 39 | ); 40 | } 41 | } -------------------------------------------------------------------------------- /inc/Plugin.php: -------------------------------------------------------------------------------- 1 | register_admin_hooks(); 14 | } 15 | 16 | public function register_admin_hooks() { 17 | $block_patterns_manager = new BlockPatternsManager(); 18 | $block_patterns_manager->register(); 19 | 20 | $this->admin_page = new AdminPage($block_patterns_manager); 21 | $this->admin_page->register(); 22 | 23 | $remote_patterns = new RemotePatterns( $block_patterns_manager ); 24 | $remote_patterns->register(); 25 | 26 | /** 27 | * Add settings link to plugin page 28 | */ 29 | $plugin = plugin_basename(_get_plugin_directory() . '/plugin.php'); 30 | add_filter("plugin_action_links_$plugin", [$this, 'plugin_settings_link'] ); 31 | } 32 | 33 | /** 34 | * Add a settings link to the plugin page 35 | */ 36 | public function plugin_settings_link( $links ) { 37 | $menu_slug = AdminPage::get_menu_slug(); 38 | 39 | $settings_link = '' . __( 'Settings', 'block-patterns-manager' ) . ''; 40 | $links[] = $settings_link; 41 | 42 | return $links; 43 | } 44 | } -------------------------------------------------------------------------------- /plugin.php: -------------------------------------------------------------------------------- 1 | run(); -------------------------------------------------------------------------------- /tests/functional.suite.yml: -------------------------------------------------------------------------------- 1 | # Codeception Test Suite Configuration 2 | # 3 | # Suite for functional tests 4 | # Emulate web requests and make WordPress process them 5 | 6 | actor: FunctionalTester 7 | modules: 8 | enabled: 9 | - WPDb 10 | - WPBrowser 11 | # - WPFilesystem 12 | - Asserts 13 | - \Helper\Functional 14 | config: 15 | WPDb: 16 | dsn: '%TEST_SITE_DB_DSN%' 17 | user: '%TEST_SITE_DB_USER%' 18 | password: '%TEST_SITE_DB_PASSWORD%' 19 | dump: 'tests/_data/dump.sql' 20 | populate: true 21 | cleanup: true 22 | waitlock: 10 23 | url: '%TEST_SITE_WP_URL%' 24 | urlReplacement: true 25 | tablePrefix: '%TEST_SITE_TABLE_PREFIX%' 26 | WPBrowser: 27 | url: '%TEST_SITE_WP_URL%' 28 | adminUsername: '%TEST_SITE_ADMIN_USERNAME%' 29 | adminPassword: '%TEST_SITE_ADMIN_PASSWORD%' 30 | adminPath: '%TEST_SITE_WP_ADMIN_PATH%' 31 | headers: 32 | X_TEST_REQUEST: 1 33 | X_WPBROWSER_REQUEST: 1 34 | 35 | WPFilesystem: 36 | wpRootFolder: '%WP_ROOT_FOLDER%' 37 | plugins: '/wp-content/plugins' 38 | mu-plugins: '/wp-content/mu-plugins' 39 | themes: '/wp-content/themes' 40 | uploads: '/wp-content/uploads' -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "derweili/block-patterns-manager", 3 | "authors": [ 4 | { 5 | "name": "derweili", 6 | "email": "jw@derweili.de" 7 | } 8 | ], 9 | "require": { 10 | }, 11 | "autoload": { 12 | "psr-4": { 13 | "Derweili\\BlockPatternsManager\\": "inc/" 14 | } 15 | }, 16 | "require-dev": { 17 | "phpunit/phpunit": "^9", 18 | "lucatume/wp-browser": "^3.0", 19 | "codeception/module-asserts": "^1.0", 20 | "codeception/module-phpbrowser": "^1.0", 21 | "codeception/module-webdriver": "^1.0", 22 | "codeception/module-db": "^1.0", 23 | "codeception/module-filesystem": "^1.0", 24 | "codeception/module-cli": "^1.0", 25 | "codeception/util-universalframework": "^1.0", 26 | "brain/monkey": "^2.6", 27 | "mockery/mockery": "^1.4" 28 | }, 29 | "scripts": { 30 | "test:unit" : "vendor/bin/codecept run unit", 31 | "test:wpunit" : "vendor/bin/codecept run wpunit", 32 | "test:functional" : "vendor/bin/codecept run functional", 33 | "test:acceptance" : "vendor/bin/codecept run acceptance" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/acceptance.suite.yml: -------------------------------------------------------------------------------- 1 | # Codeception Test Suite Configuration 2 | # 3 | # Suite for acceptance tests. 4 | # Perform tests in browser using the WPWebDriver or WPBrowser. 5 | # Use WPDb to set up your initial database fixture. 6 | # If you need both WPWebDriver and WPBrowser tests - create a separate suite. 7 | 8 | actor: AcceptanceTester 9 | modules: 10 | enabled: 11 | - WPDb 12 | - WPBrowser 13 | - \Helper\Acceptance 14 | config: 15 | WPDb: 16 | dsn: '%TEST_SITE_DB_DSN%' 17 | user: '%TEST_SITE_DB_USER%' 18 | password: '%TEST_SITE_DB_PASSWORD%' 19 | dump: 'tests/_data/dump.sql' 20 | #import the dump before the tests; this means the test site database will be repopulated before the tests. 21 | populate: true 22 | # re-import the dump between tests; this means the test site database will be repopulated between the tests. 23 | cleanup: true 24 | waitlock: 10 25 | url: '%TEST_SITE_WP_URL%' 26 | urlReplacement: true #replace the hardcoded dump URL with the one above 27 | tablePrefix: '%TEST_SITE_TABLE_PREFIX%' 28 | WPBrowser: 29 | url: '%TEST_SITE_WP_URL%' 30 | adminUsername: '%TEST_SITE_ADMIN_USERNAME%' 31 | adminPassword: '%TEST_SITE_ADMIN_PASSWORD%' 32 | adminPath: '%TEST_SITE_WP_ADMIN_PATH%' 33 | headers: 34 | X_TEST_REQUEST: 1 35 | X_WPBROWSER_REQUEST: 1 -------------------------------------------------------------------------------- /tests/unit/bootstrap.php: -------------------------------------------------------------------------------- 1 | getMockBuilder(BlockPatternsManager::class) 21 | ->getMock(); 22 | 23 | return $mock; 24 | } 25 | 26 | public function testShouldSaveSettings() { 27 | 28 | $nonce_key = 'save-block-patterns-manager-nonce'; 29 | 30 | // create nonce 31 | $nonce = wp_create_nonce( $nonce_key ); 32 | 33 | $_POST['_wpnonce'] = $nonce; 34 | $_POST['capabilities'] = [ 35 | 'twentytwentyone/large-text' => 'manage_options', 36 | ]; 37 | 38 | $mock = $this->getMockBuilder(BlockPatternsManager::class) 39 | ->getMock(); 40 | 41 | $admin_page_instance = new AdminPage( $mock ); 42 | $has_saved_settings = $admin_page_instance->save_settings(); 43 | 44 | $this->assertTrue( $has_saved_settings ); 45 | } 46 | 47 | public function testShouldNotSaveSettingsIfNonceInvalid() { 48 | // create nonce 49 | $nonce = 'invalidnonce'; 50 | 51 | $_POST['_wpnonce'] = $nonce; 52 | $_POST['capabilities'] = [ 53 | 'twentytwentyone/large-text' => 'manage_options', 54 | ]; 55 | 56 | $mock = $this->getMockBuilder(BlockPatternsManager::class) 57 | ->getMock(); 58 | 59 | $admin_page_instance = new AdminPage( $mock ); 60 | $has_saved_settings = $admin_page_instance->save_settings(); 61 | 62 | $this->assertFalse( $has_saved_settings ); 63 | } 64 | 65 | public function testShouldNotSaveSettingsIfNonceNotPresent() { 66 | $_POST['capabilities'] = [ 67 | 'twentytwentyone/large-text' => 'manage_options', 68 | ]; 69 | 70 | $mock = $this->getMockBuilder(BlockPatternsManager::class) 71 | ->getMock(); 72 | 73 | $admin_page_instance = new AdminPage( $mock ); 74 | $has_saved_settings = $admin_page_instance->save_settings(); 75 | 76 | $this->assertFalse( $has_saved_settings ); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /tests/acceptance/AdminPageCest.php: -------------------------------------------------------------------------------- 1 | loginAsAdmin(); 8 | } 9 | 10 | public function testShouldSaveSettings( AcceptanceTester $I ) { 11 | $I->amOnPluginsPage(); 12 | $I->amOnAdminPage('/tools.php?page=block-patterns-manager'); 13 | 14 | /** 15 | * Save settings first time 16 | */ 17 | $first_test_capability = 'manage_options'; 18 | 19 | $I->submitForm( '#block-patterns-manager-admin-page-form', [ 20 | 'capabilities[block_pattern_directory]' => $first_test_capability, 21 | ]); 22 | 23 | $I->seeMessage('.block-patterns-manager-settings-saved'); 24 | 25 | $returned_setting = $I->grabValueFrom('input[name="capabilities[block_pattern_directory]"]'); 26 | $returned_setting_2 = $I->grabValueFrom('select[name="capabilities[block_pattern_directory]-select"]'); 27 | $I->canSeeInField('input[name="capabilities[block_pattern_directory]"]', $first_test_capability); 28 | $I->canSeeInField('select[name="capabilities[block_pattern_directory]-select"]', $first_test_capability); 29 | 30 | /** 31 | * Save settings second time 32 | */ 33 | $second_test_capability = 'edit_posts'; 34 | 35 | $I->submitForm( '#block-patterns-manager-admin-page-form', [ 36 | 'capabilities[block_pattern_directory]' => $second_test_capability, 37 | ]); 38 | 39 | $I->seeMessage('.block-patterns-manager-settings-saved'); 40 | 41 | $returned_setting = $I->grabValueFrom('input[name="capabilities[block_pattern_directory]"]'); 42 | $I->canSeeInField('input[name="capabilities[block_pattern_directory]"]', $second_test_capability); 43 | $I->canSeeInField('select[name="capabilities[block_pattern_directory]-select"]', $second_test_capability); 44 | 45 | } 46 | 47 | public function testShouldSetSelectToCustom( AcceptanceTester $I ) { 48 | $I->amOnPluginsPage(); 49 | $I->amOnAdminPage('/tools.php?page=block-patterns-manager'); 50 | 51 | /** 52 | * Save settings first time 53 | */ 54 | $first_test_capability = 'my_custom_capability'; 55 | 56 | $I->submitForm( '#block-patterns-manager-admin-page-form', [ 57 | 'capabilities[block_pattern_directory]' => $first_test_capability, 58 | ]); 59 | 60 | $I->seeMessage('.block-patterns-manager-settings-saved'); 61 | 62 | $I->canSeeInField('input[name="capabilities[block_pattern_directory]"]', $first_test_capability); // input should be set to our custom capability 63 | $I->canSeeInField('select[name="capabilities[block_pattern_directory]-select"]', 'custom'); // select should be set to custom 64 | } 65 | } 66 | 67 | 68 | -------------------------------------------------------------------------------- /inc/RemotePatterns.php: -------------------------------------------------------------------------------- 1 | block_patterns_manager = $block_patterns_manager; 37 | } 38 | 39 | /** 40 | * Add the required hooks 41 | * 42 | * Todo: add tests to make sure the hooks are added 43 | */ 44 | public function register() { 45 | // add the filter to the block pattern directory 46 | add_filter('should_load_remote_block_patterns', [ $this, 'should_load_remote_block_patterns' ]); 47 | 48 | /** 49 | * add the block pattern directory as a pattern to BlockPatternsManager patterns list 50 | */ 51 | add_filter('block_pattern_manager_all_patterns', [ $this, 'add_pattern_directory_to_pattern_list' ]); 52 | } 53 | 54 | /** 55 | * Add the block pattern directory as a "fake" pattern to the all patterns list 56 | * This function is added to the block_pattern_manager_all_patterns filter 57 | */ 58 | public function add_pattern_directory_to_pattern_list( $block_patterns ) { 59 | /** 60 | * Add block_pattern_directory to the $block_patterns array 61 | */ 62 | $block_patterns[] = [ 63 | 'name' => 'block_pattern_directory', 64 | 'title' => 'Block Pattern Directory', 65 | 'description' => 'Load external block patterns from the Block Pattern Directory' 66 | ]; 67 | 68 | return $block_patterns; 69 | } 70 | 71 | /** 72 | * Load the current settings for the disable remote patterns capability 73 | */ 74 | public function load_disable_remote_patterns_capability_setting () { 75 | $block_patterns_capabilities = $this->block_patterns_manager->get_settings(); 76 | 77 | if( isset( $block_patterns_capabilities[$this->remote_patterns_key] ) && ! empty( $block_patterns_capabilities[ $this->remote_patterns_key ] ) ) { 78 | return $block_patterns_capabilities[$this->remote_patterns_key]; 79 | } else { 80 | false; 81 | } 82 | } 83 | 84 | /** 85 | * Get the current disable remote patterns capability 86 | * If no capability is stored yet, load it from the BlockPatternsManager settings 87 | */ 88 | public function get_setting() { 89 | if( null === $this->disable_remote_patterns_capability ) { 90 | $this->disable_remote_patterns_capability = $this->load_disable_remote_patterns_capability_setting(); 91 | } 92 | return $this->disable_remote_patterns_capability; 93 | } 94 | 95 | /** 96 | * Disable remote block pattern based on settings 97 | * 98 | * @param bool $should_load_remote_block_patterns 99 | * @return bool 100 | */ 101 | public function should_load_remote_block_patterns( $should_load_remote_block_patterns ) { 102 | 103 | $required_capability = $this->get_setting(); 104 | 105 | 106 | if( $required_capability && ! $this->current_user_can( $this->get_setting() ) ) { 107 | return false; 108 | } 109 | 110 | return $should_load_remote_block_patterns; 111 | } 112 | 113 | /** 114 | * Wrapper function for current_user_can to make it testable/mockable 115 | */ 116 | public function current_user_can( $capability ) { 117 | return current_user_can( $capability ); 118 | } 119 | } -------------------------------------------------------------------------------- /inc/BlockPatternsManager.php: -------------------------------------------------------------------------------- 1 | capabilities_settings = $this->load_settings(); 34 | } 35 | 36 | /** 37 | * Register all required hooks 38 | * 39 | * Loads all currently registered patterns and adds them to the registry. 40 | * Register our settings to the WP settings API. 41 | * Load the unregistered patterns method which disables block patterns if the user is not allowed to use them. 42 | * 43 | * Todo: add tests to check if all those functions are registered correctly 44 | */ 45 | public function register() { 46 | add_action( 'admin_init', array($this, 'load_all_patterns'), 50); 47 | add_action( 'admin_init', [ $this, 'register_settings' ] ); 48 | add_action( 'admin_init', [ $this, 'unregister_block_patterns' ], 500 ); 49 | } 50 | 51 | /** 52 | * Load all currently registered patterns and add them to the registry. 53 | * 54 | * Todo: Create Tests 55 | */ 56 | public function load_all_patterns() { 57 | self::$patterns = WP_Block_Patterns_Registry::get_instance()->get_all_registered(); 58 | } 59 | 60 | /** 61 | * Get all registered which we loaded before. 62 | * 63 | * Implents a filter to add additional (fake) patterns. 64 | * This filter is used by the RemovePattern class to add the block pattern directory to the list of patterns. 65 | */ 66 | public function get_all_patterns() { 67 | return apply_filters( 'block_pattern_manager_all_patterns', self::$patterns ); 68 | } 69 | 70 | /** 71 | * Register the settings to the WP settings API. 72 | */ 73 | public function register_settings() { 74 | register_setting( 'block_patterns_manager_settings', $this->capabilities_settings_key, 'array', __('List of block patterns and their required capabilities', 'block-patterns-manager') ); 75 | } 76 | 77 | /** 78 | * Method to save the settings to the database. 79 | */ 80 | public function save_settings( array $settings ) { 81 | update_option( $this->capabilities_settings_key, $settings ); 82 | $this->capabilities_settings = $settings; 83 | } 84 | 85 | /** 86 | * Get the settings from the database. 87 | */ 88 | private function load_settings() { 89 | return get_option( $this->capabilities_settings_key, [] ); 90 | } 91 | 92 | /** 93 | * Get the settings we loaded before. 94 | * If we did not load them before, load them from the Database. 95 | */ 96 | public function get_settings() { 97 | if( null === $this->capabilities_settings ) { 98 | $this->capabilities_settings = $this->load_settings(); 99 | } 100 | return $this->capabilities_settings; 101 | } 102 | 103 | /** 104 | * Unregister Block Patterns based on capabilities 105 | */ 106 | public function unregister_block_patterns() { 107 | $settings = $this->get_settings(); 108 | 109 | foreach ($settings as $pattern_name => $capability) { 110 | // only unregister if the pattern is registered 111 | if( $this->is_pattern_registered( $pattern_name ) ) { 112 | 113 | // unregister if a capability is selected (not empty) and user has not the capability 114 | if( $capability && ! empty( $capability ) && ! $this->current_user_can( $capability ) ) { 115 | $unregister_result = $this->unregister_block_pattern( $pattern_name ); 116 | } 117 | } 118 | } 119 | } 120 | 121 | /** 122 | * Wrapper function for current_user_can() WordPress Capabilities Check 123 | */ 124 | public function current_user_can( $capability ) { 125 | return current_user_can( $capability ); 126 | } 127 | 128 | /** 129 | * Wrapper function for unregister_block_pattern WordPress function 130 | */ 131 | public function unregister_block_pattern( $pattern_name ) { 132 | return unregister_block_pattern( $pattern_name ); 133 | } 134 | 135 | /** 136 | * Check if a pattern is registered 137 | */ 138 | public function is_pattern_registered( $pattern_name ) { 139 | return WP_Block_Patterns_Registry::get_instance()->is_registered( $pattern_name ); 140 | } 141 | } -------------------------------------------------------------------------------- /tests/unit/BlockPatternsManagerTest.php: -------------------------------------------------------------------------------- 1 | 'my-test-capability', 47 | 'pattern2' => 'my-test-capability2', 48 | 'pattern3' => '', 49 | ]; 50 | 51 | 52 | $manager->save_settings( $demo_settings ); 53 | $returned_settings = $manager->get_settings(); 54 | 55 | $this->assertSame( $demo_settings, $returned_settings ); 56 | } 57 | 58 | public function testShouldUnregisterPatternWhenUserDoesNotHaveCapability() { 59 | $demo_settings = [ 60 | 'pattern1' => 'manage_options', 61 | ]; 62 | 63 | $mock = $this->getMockBuilder(BlockPatternsManager::class) 64 | ->setMethods(['get_settings', 'is_pattern_registered', 'current_user_can', 'unregister_block_pattern']) 65 | ->getMock(); 66 | 67 | $mock->method('get_settings') 68 | ->willReturn( $demo_settings ); 69 | 70 | $mock->expects($this->once()) 71 | ->method('is_pattern_registered') 72 | ->with('pattern1') 73 | ->willReturn(true); 74 | 75 | $mock->expects($this->once()) 76 | ->method('current_user_can') 77 | ->with('manage_options') 78 | ->willReturn(false); 79 | 80 | $mock->expects($this->once()) 81 | ->method('unregister_block_pattern') 82 | ->with('pattern1'); 83 | 84 | $mock->unregister_block_patterns(); 85 | } 86 | 87 | public function testShouldNotUnregisterPatternWhenUserDoesHaveCapability() { 88 | 89 | $demo_settings = [ 90 | 'pattern1' => 'manage_options' 91 | ]; 92 | 93 | $mock = $this->getMockBuilder(BlockPatternsManager::class) 94 | ->setMethods(['get_settings', 'is_pattern_registered', 'current_user_can', 'unregister_block_pattern']) 95 | ->getMock(); 96 | 97 | $mock->method('get_settings') 98 | ->willReturn( $demo_settings ); 99 | 100 | $mock->expects($this->once()) 101 | ->method('is_pattern_registered') 102 | ->with('pattern1') 103 | ->willReturn(true); 104 | 105 | $mock->expects($this->once()) 106 | ->method('current_user_can') 107 | ->with('manage_options') 108 | ->willReturn(true); 109 | 110 | $mock->expects($this->never()) 111 | ->method('unregister_block_pattern'); 112 | 113 | $mock->unregister_block_patterns(); 114 | } 115 | 116 | public function testShouldNotUnregisterPatternWhenCapabilityIsEmpty() { 117 | 118 | $demo_settings = [ 119 | 'pattern1' => '' 120 | ]; 121 | 122 | $mock = $this->getMockBuilder(BlockPatternsManager::class) 123 | ->setMethods(['get_settings', 'is_pattern_registered', 'current_user_can', 'unregister_block_pattern']) 124 | ->getMock(); 125 | 126 | $mock->method('get_settings') 127 | ->willReturn( $demo_settings ); 128 | 129 | $mock->expects($this->once()) 130 | ->method('is_pattern_registered') 131 | ->with('pattern1') 132 | ->willReturn(true); 133 | 134 | $mock->expects($this->never()) 135 | ->method('unregister_block_pattern'); 136 | 137 | $mock->unregister_block_patterns(); 138 | } 139 | 140 | public function testShouldNotUnregisterPatternWhenPatternIsNotRegistered() { 141 | 142 | $demo_settings = [ 143 | 'pattern1' => 'manage_options' 144 | ]; 145 | 146 | $mock = $this->getMockBuilder(BlockPatternsManager::class) 147 | ->setMethods(['get_settings', 'is_pattern_registered', 'current_user_can', 'unregister_block_pattern']) 148 | ->getMock(); 149 | 150 | $mock->method('get_settings') 151 | ->willReturn( $demo_settings ); 152 | 153 | $mock->expects($this->once()) 154 | ->method('is_pattern_registered') 155 | ->with('pattern1') 156 | ->willReturn(false); 157 | 158 | $mock->expects($this->never()) 159 | ->method('unregister_block_pattern'); 160 | 161 | $mock->unregister_block_patterns(); 162 | } 163 | 164 | 165 | /** 166 | * Test if register function actually registers admin_menu and set_screen functions 167 | * 168 | */ 169 | public function testShouldAddCallPatternsFilter() { 170 | 171 | $manager = new BlockPatternsManager(); 172 | $manager->get_all_patterns(); 173 | $this->assertTrue( Filters\applied('block_pattern_manager_all_patterns') > 0 ); 174 | } 175 | } -------------------------------------------------------------------------------- /inc/AdminPage.php: -------------------------------------------------------------------------------- 1 | block_patterns_manager = $block_patterns_manager; 43 | } 44 | 45 | public static function get_menu_slug() { 46 | return self::$menu_slug; 47 | } 48 | 49 | /** 50 | * Todo add test to check if actions and filters are registerd 51 | */ 52 | public function register() { 53 | add_action( 'admin_menu', array($this, 'add_menu_page') ); 54 | add_filter( 'set-screen-option', [ __CLASS__, 'set_screen' ], 10, 3 ); 55 | } 56 | 57 | public static function set_screen( $status, $option, $value ) { 58 | return $value; 59 | } 60 | 61 | /** 62 | * Register the menu page. 63 | * 64 | * Todo: add test to check if menu page is registerd 65 | */ 66 | public function add_menu_page() { 67 | $hook = add_management_page( 68 | __('Block Patterns Manager', 'block-patterns-manager'), 69 | __('Block Patterns Manager', 'block-patterns-manager'), 70 | 'manage_options', 71 | self::get_menu_slug(), 72 | array( $this, 'options_page' ), 73 | 'dashicons-align-wide', 74 | null 75 | ); 76 | 77 | add_action( "load-$hook", [ $this, 'screen_option' ] ); 78 | } 79 | 80 | /** 81 | * Screen options 82 | */ 83 | public function screen_option() { 84 | 85 | $option = 'per_page'; 86 | 87 | $this->block_patterns_list_table = new BlockPatternsListTable( $this->block_patterns_manager ); 88 | $this->save_settings(); 89 | } 90 | 91 | /** 92 | * Render the options page 93 | * 94 | * Todo: Write tests 95 | */ 96 | public function options_page() { 97 | $settings = $this->block_patterns_manager->get_settings(); 98 | 99 | ?> 100 |
101 |

102 |
103 |
104 |
105 |
106 |
107 | 108 | block_patterns_list_table->prepare_items(); 110 | $this->block_patterns_list_table->display(); 111 | ?> 112 | 113 | nonce_action ); ?> 114 |
115 |
116 |
117 |
118 | 119 |
120 | 121 |
122 | 123 |

126 | 127 |
128 |

129 | You can add a required capability to each block pattern individually. You can also add a required capability for loading external block patterns from the block pattern directory. 130 |

131 |

132 | You can disable block patterns completely by selecting "Disable for all users". 133 |

134 |
135 | 136 | 137 |
138 | 139 | 140 |
141 | 142 | 143 |
144 | 145 |
146 | 147 | 148 |
149 |
150 |
151 | nonce_action ) ) return false; 165 | 166 | 167 | $block_patterns_capabilities = []; 168 | 169 | // sanitize input 170 | foreach ($_POST['capabilities'] as $key => $value) { 171 | $block_patterns_capabilities[ sanitize_text_field( $key ) ] = sanitize_text_field( $value ); 172 | } 173 | 174 | $this->block_patterns_manager->save_settings( $block_patterns_capabilities ); 175 | 176 | add_action( 'admin_notices', [$this, 'save_settings_notice'] ); 177 | 178 | return true; 179 | } 180 | 181 | /** 182 | * Render the admin notice 183 | */ 184 | public function save_settings_notice() { 185 | ?> 186 |
187 |

188 |
189 | getMockBuilder(BlockPatternsManager::class) 27 | ->getMock(); 28 | 29 | return $mock; 30 | } 31 | 32 | public function testShouldAddPatternDirectoryToPatternList() { 33 | $remote_patterns = new RemotePatterns( $this->getDefaultBlockPatternsManagerMock() ); 34 | 35 | $block_patterns = $remote_patterns->add_pattern_directory_to_pattern_list( [] ); 36 | 37 | $this->assertIsArray( $block_patterns ); 38 | $this->assertEquals( count( $block_patterns ) , 1 ); 39 | $this->assertEquals( $block_patterns[0]['name'], 'block_pattern_directory' ); 40 | } 41 | 42 | /** 43 | * Test if add_pattern_directory_to_pattern_list() actually adds 44 | * the pattern to the pattern-array and preserve existing patterns 45 | */ 46 | public function testShouldAddPatternDirectoryToExistingPatternList() { 47 | /** 48 | * check if it is instance of AdminPage class 49 | */ 50 | $remote_patterns = new RemotePatterns( $this->getDefaultBlockPatternsManagerMock() ); 51 | 52 | $block_patterns = $remote_patterns->add_pattern_directory_to_pattern_list( [ 53 | [ 54 | 'name' => 'existingItem', 55 | 'title' => 'existingItemTitle', 56 | 'description' => 'Description' 57 | ] 58 | ] ); 59 | 60 | $this->assertIsArray( $block_patterns ); 61 | $this->assertEquals( count( $block_patterns ) , 2 ); 62 | $this->assertEquals( $block_patterns[0]['name'], 'existingItem' ); // make sure exising array is not overwritten 63 | $this->assertEquals( $block_patterns[1]['name'], 'block_pattern_directory' ); // make sure new item is added to array 64 | } 65 | 66 | public function testShouldLoadSettingsFromBlockPatternsManager() { 67 | 68 | $mock = Mockery::mock(BlockPatternsManager::class); 69 | $mock->shouldReceive('get_settings')->andReturn(''); 70 | 71 | $remote_patterns = new RemotePatterns( $mock ); 72 | 73 | $block_patterns = $remote_patterns->load_disable_remote_patterns_capability_setting(); 74 | } 75 | 76 | public function testShouldLoadRemotePatternsCapabilityFromGeneralSettings() { 77 | $mock = $this->getMockBuilder(BlockPatternsManager::class) 78 | ->setMethods(['get_settings']) 79 | ->getMock(); 80 | 81 | $mock->expects($this->once()) 82 | ->method('get_settings') 83 | ->willReturn([ 84 | 'block_pattern_directory' => 'manage_options' 85 | ]); 86 | 87 | $remote_patterns = new RemotePatterns( $mock ); 88 | $capability = $remote_patterns->load_disable_remote_patterns_capability_setting(); 89 | 90 | $this->assertEquals( $capability, 'manage_options' ); 91 | } 92 | 93 | public function testShouldReturnFalseIfNoRemovePatternsCapabilitiesFound() { 94 | $mock = $this->getMockBuilder(BlockPatternsManager::class) 95 | ->setMethods(['get_settings']) 96 | ->getMock(); 97 | 98 | $mock->expects($this->once()) 99 | ->method('get_settings') 100 | ->willReturn([]); 101 | 102 | $remote_patterns = new RemotePatterns( $mock ); 103 | $capability = $remote_patterns->load_disable_remote_patterns_capability_setting(); 104 | 105 | $this->assertEquals( $capability, false ); 106 | } 107 | 108 | public function testShouldLoadCapability() { 109 | $mock = $this->getMockBuilder(RemotePatterns::class) 110 | ->setConstructorArgs( [ $this->getDefaultBlockPatternsManagerMock() ] ) 111 | ->setMethods(['load_disable_remote_patterns_capability_setting']) 112 | ->getMock(); 113 | 114 | $demo_capability = 'manage_options'; 115 | 116 | $mock->expects($this->once()) 117 | ->method('load_disable_remote_patterns_capability_setting') 118 | ->willReturn($demo_capability); 119 | 120 | $setting_1 = $mock->get_setting(); 121 | 122 | $this->assertEquals( $setting_1, $demo_capability ); 123 | } 124 | 125 | public function testShouldStoreAndReuseStoredCapability() { 126 | $mock = $this->getMockBuilder(RemotePatterns::class) 127 | ->setConstructorArgs( [ $this->getDefaultBlockPatternsManagerMock() ] ) 128 | ->setMethods(['load_disable_remote_patterns_capability_setting']) 129 | ->getMock(); 130 | 131 | $demo_capability = 'manage_options'; 132 | 133 | $mock->expects($this->once()) 134 | ->method('load_disable_remote_patterns_capability_setting') 135 | ->willReturn($demo_capability); 136 | 137 | $setting_1 = $mock->get_setting(); 138 | $setting_2 = $mock->get_setting(); 139 | 140 | $this->assertEquals( $setting_1, $demo_capability ); 141 | $this->assertEquals( $setting_2, $demo_capability ); 142 | } 143 | 144 | public function testShouldDisableRemotePatternsWhenUserNotHasThePermission() { 145 | $mock = $this->getMockBuilder(RemotePatterns::class) 146 | ->setConstructorArgs( [ $this->getDefaultBlockPatternsManagerMock() ] ) 147 | ->setMethods(['get_setting', 'current_user_can']) 148 | ->getMock(); 149 | 150 | $demo_capability = 'manage_options'; 151 | 152 | $mock->expects( $this->exactly(2) ) 153 | ->method('get_setting') 154 | ->willReturn($demo_capability); 155 | 156 | $mock->expects( $this->once() ) 157 | ->method('current_user_can') 158 | ->with($demo_capability) 159 | ->willReturn(false); 160 | 161 | $this->assertEquals( 162 | $mock->should_load_remote_block_patterns( true ), 163 | false 164 | ); 165 | } 166 | 167 | public function testShouldNotDisableRemotePatternsWhenUserHasThePermission() { 168 | $mock = $this->getMockBuilder(RemotePatterns::class) 169 | ->setConstructorArgs( [ $this->getDefaultBlockPatternsManagerMock() ] ) 170 | ->setMethods(['get_setting', 'current_user_can']) 171 | ->getMock(); 172 | 173 | $demo_capability = 'manage_options'; 174 | 175 | $mock->expects( $this->exactly(2) ) 176 | ->method('get_setting') 177 | ->willReturn($demo_capability); 178 | 179 | $mock->expects( $this->once() ) 180 | ->method('current_user_can') 181 | ->with($demo_capability) 182 | ->willReturn(true); 183 | 184 | $this->assertEquals( 185 | $mock->should_load_remote_block_patterns( true ), 186 | true 187 | ); 188 | } 189 | 190 | public function testShouldNotDisableRemotePatternsWhenrequiredCapabilityIsEmpty() { 191 | $mock = $this->getMockBuilder(RemotePatterns::class) 192 | ->setConstructorArgs( [ $this->getDefaultBlockPatternsManagerMock() ] ) 193 | ->setMethods(['get_setting', 'current_user_can']) 194 | ->getMock(); 195 | 196 | $demo_capability = ''; 197 | 198 | $mock->expects( $this->exactly(1) ) 199 | ->method('get_setting') 200 | ->willReturn($demo_capability); 201 | 202 | $this->assertEquals( 203 | $mock->should_load_remote_block_patterns( true ), 204 | true 205 | ); 206 | } 207 | } -------------------------------------------------------------------------------- /tests/wpunit/BlockPatternsListTableTest.php: -------------------------------------------------------------------------------- 1 | 'C - My Pattern Title', 26 | 'categories' => [ 'category1', 'category2' ], 27 | 'viewportWidth' => 1440, 28 | 'description' => 'My Description', 29 | 'content' => '

My Pattern title

', 30 | 'name' => 'b-manager/mypatterntitle' 31 | ], 32 | [ 33 | 'title' => 'A - My Pattern Title', 34 | 'categories' => [ 'category1', 'category2' ], 35 | 'viewportWidth' => 1440, 36 | 'description' => 'My Description', 37 | 'content' => '

My Pattern title

', 38 | 'name' => 'x-manager/mypatterntitle' 39 | ], 40 | [ 41 | 'title' => 'X - My Pattern Title', 42 | 'categories' => [ 'category1', 'category2' ], 43 | 'viewportWidth' => 1440, 44 | 'description' => 'My Description', 45 | 'content' => '

My Pattern title

', 46 | 'name' => 'a-manager/mypatterntitle' 47 | ], 48 | [ 49 | 'title' => 'B - My Pattern Title', 50 | 'categories' => [ 'category1', 'category2' ], 51 | 'viewportWidth' => 1440, 52 | 'description' => 'My Description', 53 | 'content' => '

My Pattern title

', 54 | 'name' => 'c-manager/mypatterntitle' 55 | ] 56 | ]; 57 | } 58 | 59 | public function getDefaultBlockPatternsManagerMock() { 60 | $mock = $this->getMockBuilder(BlockPatternsManager::class) 61 | ->setMethods(['get_all_patterns']) 62 | ->getMock(); 63 | 64 | $mock 65 | ->method('get_all_patterns') 66 | ->willReturn($this->get_demo_patterns()); 67 | 68 | return $mock; 69 | } 70 | 71 | public function getDefaultBlockPatternsListTableMock() { 72 | $listTableMock = $this->getMockBuilder(BlockPatternsListTable::class) 73 | ->setConstructorArgs( [ $this->getDefaultBlockPatternsManagerMock() ] ) 74 | ->setMethods(['process_bulk_action', 'get_pagenum', 'set_pagination_args', 'get_column_info' ]) 75 | ->getMock(); 76 | 77 | $listTableMock 78 | ->method('get_pagenum') 79 | ->willReturn(1); 80 | 81 | $listTableMock 82 | ->method('set_pagination_args') 83 | ->willReturn(null); 84 | 85 | $listTableMock 86 | ->method('get_column_info') 87 | ->willReturn([ 88 | [ 89 | "title" => "Block pattern title", 90 | "name" => "Block pattern name", 91 | "description" => "Block pattern description", 92 | "capability" => "Required capability to use the block pattern", 93 | ] 94 | ]); 95 | 96 | 97 | $listTableMock 98 | ->method('process_bulk_action') 99 | ->willReturn(null); 100 | 101 | return $listTableMock; 102 | } 103 | 104 | public function testCapabilitiesShouldBeStrings() { 105 | $listTable = new BlockPatternsListTable( $this->getDefaultBlockPatternsManagerMock() ); 106 | 107 | $default_capability_groups = $listTable->get_default_capability_groups(); 108 | 109 | 110 | foreach ($default_capability_groups as $group) { 111 | $this->assertIsString($group['label']); 112 | 113 | foreach ($group['capabilities'] as $capability) { 114 | 115 | $this->assertIsString($capability); 116 | } 117 | } 118 | } 119 | 120 | public function testShouldGetDefaultCapabilitiesAsArrayOfStrings() { 121 | $listTable = new BlockPatternsListTable( $this->getDefaultBlockPatternsManagerMock() ); 122 | 123 | $capabilities = $listTable->get_default_capabilities(); 124 | 125 | 126 | foreach ($capabilities as $capability) { 127 | 128 | $this->assertIsString($capability); 129 | } 130 | } 131 | 132 | public function testShouldInclude() { 133 | $listTable = new BlockPatternsListTable( $this->getDefaultBlockPatternsManagerMock() ); 134 | 135 | $capabilities = $listTable->get_default_capabilities(); 136 | 137 | $this->assertTrue( in_array( 'disable_for_all_users', $capabilities ) ); 138 | } 139 | 140 | public function testShouldReturnFalseForBuildInCapabilities() { 141 | $listTable = new BlockPatternsListTable( $this->getDefaultBlockPatternsManagerMock() ); 142 | 143 | $capabilities = $listTable->get_default_capabilities(); 144 | 145 | 146 | foreach ($capabilities as $capability) { 147 | $this->assertFalse( $listTable->is_custom_capability( $capability ) ); 148 | } 149 | } 150 | 151 | public function testShouldReturnTrueForCustomCapabilities() { 152 | $listTable = new BlockPatternsListTable( $this->getDefaultBlockPatternsManagerMock() ); 153 | 154 | $this->assertTrue( $listTable->is_custom_capability( 'my_custom_capability' ) ); 155 | } 156 | 157 | public function testShouldSetItems() { 158 | 159 | $listTableMock = $this->getDefaultBlockPatternsListTableMock(); 160 | 161 | $listTableMock->prepare_items(); 162 | 163 | // dbut output items 164 | // codecept_debug($listTableMock->items); 165 | 166 | // test if items is array 167 | $this->assertIsArray($listTableMock->items); 168 | 169 | // test if items is not empty 170 | $this->assertNotEmpty($listTableMock->items); 171 | 172 | // test if items has two items 173 | $this->assertCount(4, $listTableMock->items); 174 | } 175 | 176 | public function testShouldSortItemsAscByTitle() { 177 | 178 | $listTableMock = $this->getDefaultBlockPatternsListTableMock(); 179 | 180 | $_GET['orderby'] = 'title'; 181 | $_GET['order'] = 'asc'; 182 | 183 | $listTableMock->prepare_items(); 184 | 185 | // test if items are ordered asc 186 | $this->assertEquals( 'A - My Pattern Title', $listTableMock->items[0]['title'] ); 187 | } 188 | 189 | public function testShouldSortItemsDescByTitle() { 190 | 191 | $listTableMock = $this->getDefaultBlockPatternsListTableMock(); 192 | 193 | 194 | $_GET['orderby'] = 'title'; 195 | $_GET['order'] = 'desc'; 196 | 197 | $listTableMock->prepare_items(); 198 | 199 | // test if items are ordered asc 200 | $this->assertEquals( 'X - My Pattern Title', $listTableMock->items[0]['title'] ); 201 | } 202 | 203 | public function testShouldSortItemsAscByName() { 204 | 205 | $listTableMock = $this->getDefaultBlockPatternsListTableMock(); 206 | 207 | $_GET['orderby'] = 'name'; 208 | $_GET['order'] = 'asc'; 209 | 210 | $listTableMock->prepare_items(); 211 | 212 | // test if items are ordered asc 213 | $this->assertEquals( 'a-manager/mypatterntitle', $listTableMock->items[0]['name'] ); 214 | } 215 | 216 | public function testShouldSortItemsDescByName() { 217 | 218 | $listTableMock = $this->getDefaultBlockPatternsListTableMock(); 219 | 220 | 221 | $_GET['orderby'] = 'name'; 222 | $_GET['order'] = 'desc'; 223 | 224 | $listTableMock->prepare_items(); 225 | 226 | // test if items are ordered asc 227 | $this->assertEquals( 'x-manager/mypatterntitle', $listTableMock->items[0]['name'] ); 228 | } 229 | 230 | 231 | public function testShouldHaveFourColumns() { 232 | $listTable = new BlockPatternsListTable( $this->getDefaultBlockPatternsManagerMock() ); 233 | 234 | $columns = $listTable->get_columns(); 235 | 236 | // test if items is array 237 | $this->assertIsArray($columns); 238 | 239 | // test if items has two items 240 | $this->assertCount(4, $columns); 241 | } 242 | 243 | public function testSholdBeAssoziativeArrayOfStrings() { 244 | $listTable = new BlockPatternsListTable( $this->getDefaultBlockPatternsManagerMock() ); 245 | 246 | $columns = $listTable->get_columns(); 247 | 248 | foreach ($columns as $key => $value) { 249 | $this->assertIsString($key); 250 | $this->assertIsString($value); 251 | } 252 | } 253 | 254 | public function testShouldReturnSortableColumns() { 255 | $listTable = new BlockPatternsListTable( $this->getDefaultBlockPatternsManagerMock() ); 256 | 257 | $sortableColumns = $listTable->get_sortable_columns(); 258 | 259 | $this->assertCount(2, $sortableColumns); 260 | 261 | foreach ($sortableColumns as $key => $value) { 262 | $this->assertIsString($key); 263 | $this->assertIsString($value[0]); 264 | $this->assertTrue($value[1]); 265 | } 266 | 267 | $this->assertArrayHasKey('title', $sortableColumns); 268 | $this->assertArrayHasKey('name', $sortableColumns); 269 | } 270 | 271 | public function testShouldRenderTable() { 272 | $listTableMock = $this->getMockBuilder(BlockPatternsListTable::class) 273 | ->setConstructorArgs( [ $this->getDefaultBlockPatternsManagerMock() ] ) 274 | ->setMethods(['process_bulk_action', 'get_pagenum', 'set_pagination_args' ]) 275 | ->getMock(); 276 | 277 | $listTableMock 278 | ->method('get_pagenum') 279 | ->willReturn(1); 280 | 281 | $listTableMock 282 | ->method('set_pagination_args') 283 | ->willReturn(null); 284 | 285 | $listTableMock 286 | ->method('process_bulk_action') 287 | ->willReturn(null); 288 | 289 | $listTableMock->prepare_items(); 290 | 291 | ob_start(); 292 | 293 | $listTableMock->display(); 294 | 295 | $output = ob_get_clean(); 296 | 297 | $this->assertNotEmpty($output); 298 | 299 | $this->assertStringContainsString('wp-list-table', $output); 300 | } 301 | } 302 | -------------------------------------------------------------------------------- /inc/BlockPatternsListTable.php: -------------------------------------------------------------------------------- 1 | block_patterns_manager = $block_patterns_manager; 31 | 32 | parent::__construct( [ 33 | 'singular' => __( 'Block Pattern', 'block-patterns-manager' ), //singular name of the listed records 34 | 'plural' => __( 'Block Patterns', 'block-patterns-manager' ), //plural name of the listed records 35 | 'ajax' => false //should this table support ajax? 36 | ] ); 37 | } 38 | 39 | /** 40 | * Helper function to get all default capabilities we show in the table 41 | * The Capabilities are grouped by type so we can use them as in the select input 42 | */ 43 | public function get_default_capability_groups() { 44 | return [ 45 | [ 46 | 'label' => __('Posts', 'block-patterns-manager'), 47 | 'capabilities' => [ 48 | 'edit_posts', 49 | 'edit_others_posts', 50 | 'edit_published_posts', 51 | 'publish_posts', 52 | 'edit_pages', 53 | 'delete_posts', 54 | 'delete_published_posts', 55 | 'delete_private_posts', 56 | 'delete_others_posts', 57 | 'edit_private_posts', 58 | 'read_private_posts', 59 | 'manage_categories', 60 | ] 61 | ], 62 | [ 63 | 'label' => __('Pages', 'block-patterns-manager'), 64 | 'capabilities' => [ 65 | 'publish_pages', 66 | 'edit_others_pages', 67 | 'edit_published_pages', 68 | 'delete_pages', 69 | 'delete_others_pages', 70 | 'delete_published_pages', 71 | 'delete_private_pages', 72 | 'edit_private_pages', 73 | 'read_private_pages', 74 | ] 75 | ], 76 | [ 77 | 'label' => __('Media', 'block-patterns-manager'), 78 | 'capabilities' => [ 79 | 'edit_files', 80 | 'upload_files', 81 | 'unfiltered_upload', 82 | ] 83 | ], 84 | [ 85 | 'label' => __('Users', 'block-patterns-manager'), 86 | 'capabilities' => [ 87 | 'edit_users', 88 | 'delete_users', 89 | 'create_users', 90 | 'promote_users', 91 | ] 92 | ], 93 | [ 94 | 'label' => __('Themes', 'block-patterns-manager'), 95 | 'capabilities' => [ 96 | 'switch_themes', 97 | 'edit_themes', 98 | 'edit_theme_options', 99 | 'install_themes', 100 | 'delete_themes', 101 | 'update_themes', 102 | ] 103 | ], 104 | [ 105 | 'label' => __('Plugins', 'block-patterns-manager'), 106 | 'capabilities' => [ 107 | 'activate_plugins', 108 | 'edit_plugins', 109 | 'install_plugins', 110 | 'update_plugins', 111 | 'delete_plugins', 112 | ] 113 | ], 114 | [ 115 | 'label' => __('Admin', 'block-patterns-manager'), 116 | 'capabilities' => [ 117 | 'manage_options', 118 | 'import', 119 | 'unfiltered_html', 120 | 'customize', 121 | 'update_core', 122 | ] 123 | ], 124 | [ 125 | 'label' => __('Network', 'block-patterns-manager'), 126 | 'capabilities' => [ 127 | 'create_sites', 128 | 'delete_sites', 129 | 'manage_network', 130 | 'manage_sites', 131 | 'manage_network_users', 132 | 'manage_network_themes', 133 | 'manage_network_options', 134 | 'manage_network_plugins', 135 | 'upgrade_network', 136 | 'setup_network', 137 | 'upload_plugins', 138 | 'upload_themes', 139 | 'delete_site', 140 | ] 141 | ], 142 | [ 143 | 'label' => __('Comments', 'block-patterns-manager'), 144 | 'capabilities' => [ 145 | 'moderate_comments', 146 | 'edit_comment', 147 | ] 148 | ], 149 | [ 150 | 'label' => __('Other', 'block-patterns-manager'), 151 | 'capabilities' => [ 152 | 'read', 153 | 'export', 154 | 'manage_links', 155 | 'edit_dashboard', 156 | ] 157 | ] 158 | ]; 159 | } 160 | 161 | /** 162 | * Get all the default capabilities from the get_default_capability_groups() function but ungrouped 163 | */ 164 | public function get_default_capabilities() { 165 | $default_capabilities = []; 166 | 167 | foreach ( $this->get_default_capability_groups() as $group ) { 168 | $default_capabilities = array_merge( $default_capabilities, $group['capabilities'] ); 169 | } 170 | 171 | $default_capabilities[] = 'disable_for_all_users'; 172 | 173 | return $default_capabilities; 174 | } 175 | 176 | /** 177 | * Check if a given capability is in our default capabilities array or if it is a custom capability 178 | * 179 | * @return bool | True if it is a default capability, false if it is a custom capability 180 | */ 181 | public function is_custom_capability( $capability ) { 182 | if( empty( $capability ) ) return false; 183 | 184 | if( in_array( $capability, $this->get_default_capabilities() ) ) return false; 185 | 186 | return true; 187 | } 188 | 189 | /** 190 | * Render the capabilities Dropdown for a given block pattern 191 | */ 192 | public function capabilities_dropdown( $item ) { 193 | ob_start(); 194 | $capability_settings = $this->block_patterns_manager->get_settings(); 195 | 196 | $item_capability = isset( $capability_settings[ $item[ 'name' ] ] ) && $capability_settings[$item['name']] ? $capability_settings[$item['name']] : ''; 197 | 198 | ?> 199 |
200 | 214 | 215 |
216 | 217 | capabilities_dropdown( $item ); 241 | default: 242 | return $item[ $column_name ]; 243 | } 244 | } 245 | 246 | /** 247 | * Handles data query and filter, sorting, and pagination. 248 | */ 249 | public function prepare_items() { 250 | 251 | $block_patterns = $this->block_patterns_manager->get_all_patterns(); 252 | 253 | $this->_column_headers = $this->get_column_info(); 254 | 255 | /** Process bulk action */ 256 | $this->process_bulk_action(); 257 | 258 | // $per_page = $this->get_items_per_page( 'customers_per_page', 5 ); 259 | $current_page = $this->get_pagenum(); 260 | $total_items = count($block_patterns); 261 | 262 | $this->set_pagination_args( [ 263 | 'total_items' => $total_items, //WE have to calculate the total number of items 264 | 'per_page' => $total_items //WE have to determine how many items to show on a page 265 | ] ); 266 | 267 | /** 268 | * Sort items optionally based on GET Parameters 269 | */ 270 | if( isset( $_GET['orderby'] ) && ! empty( $_GET['orderby'] ) && \array_key_exists( $_GET['orderby'], $this->get_sortable_columns() ) ) { 271 | \usort( $block_patterns, function( $a, $b ) { 272 | if( $_GET['order'] === 'asc' ) { 273 | return $a[ $_GET['orderby'] ] > $b[ $_GET['orderby'] ]; 274 | } else { 275 | return $a[ $_GET['orderby'] ] < $b[ $_GET['orderby'] ]; 276 | } 277 | } ); 278 | } 279 | 280 | $this->items = $block_patterns; 281 | } 282 | 283 | /** 284 | * Associative array of columns 285 | * 286 | * @return array 287 | */ 288 | function get_columns() { 289 | $columns = [ 290 | // 'cb' => '', 291 | 'title' => __( 'Block pattern title', 'block-patterns-manager' ), 292 | 'name' => __( 'Block pattern name', 'block-patterns-manager' ), 293 | 'description' => __( 'Block pattern description', 'block-patterns-manager' ), 294 | 'capability' => __('Required capability to use the block pattern', 'block-patterns-manager') 295 | ]; 296 | 297 | return $columns; 298 | } 299 | 300 | /** 301 | * Columns to make sortable. 302 | * 303 | * @return array 304 | */ 305 | public function get_sortable_columns() { 306 | $sortable_columns = array( 307 | 'title' => array( 'title', true ), 308 | 'name' => array( 'name', true ), 309 | ); 310 | 311 | return $sortable_columns; 312 | } 313 | 314 | /** 315 | * Render the default table from the WP_List_Table class and out custom Javascript 316 | */ 317 | public function display() { 318 | parent::display(); 319 | 320 | ?> 321 | 345 | 346 |