├── .gitignore ├── .release-please-manifest.json ├── languages ├── types-de_DE.mo ├── types-nl_NL.mo ├── types.pot ├── types-nl_NL.po └── types-de_DE.po ├── autoload.php ├── release-please-config.json ├── .github └── workflows │ └── release.yml ├── composer.json ├── LICENSE.md ├── lib ├── functions.php ├── Post_Type_Page_State.php ├── Post_Type_Query.php ├── Post_Slug.php ├── Post_Type_Page_Option.php ├── Taxonomy.php ├── Post_Type.php ├── Taxonomy_Labels.php ├── Post_Type_Page.php ├── Post_Type_Labels.php └── Post_Type_Columns.php ├── CHANGELOG.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /.release-please-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | ".": "2.5.8" 3 | } 4 | -------------------------------------------------------------------------------- /languages/types-de_DE.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mindkomm/types/HEAD/languages/types-de_DE.mo -------------------------------------------------------------------------------- /languages/types-nl_NL.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mindkomm/types/HEAD/languages/types-nl_NL.mo -------------------------------------------------------------------------------- /autoload.php: -------------------------------------------------------------------------------- 1 | =7.0.0" 8 | }, 9 | "autoload": { 10 | "psr-4": { 11 | "Types\\": "lib/" 12 | }, 13 | "files": [ 14 | "autoload.php" 15 | ] 16 | }, 17 | "scripts": { 18 | "i18n": "wp i18n make-pot . languages/types.pot --domain=mind/types" 19 | }, 20 | "authors": [ 21 | { 22 | "name": "Lukas Gaechter", 23 | "email": "lukas.gaechter@mind.ch", 24 | "homepage": "https://www.mind.ch" 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 MIND Kommunikation GmbH 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /lib/functions.php: -------------------------------------------------------------------------------- 1 | menu_item_parent ) { 17 | return $menu_items; 18 | } 19 | 20 | foreach ( $menu_items as $item ) { 21 | if ( (int) $item->ID === (int) $child->menu_item_parent ) { 22 | if ( $with_parent ) { 23 | $item->current_item_parent = true; 24 | $item->classes[] = 'current-menu-parent'; 25 | } 26 | 27 | $item->current_item_ancestor = true; 28 | $item->classes[] = 'current-menu-ancestor'; 29 | 30 | if ( (int) $item->menu_item_parent ) { 31 | $menu_items = menu_items_ancestors( $item, $menu_items, false ); 32 | } 33 | 34 | break; 35 | } 36 | } 37 | 38 | return $menu_items; 39 | } 40 | -------------------------------------------------------------------------------- /lib/Post_Type_Page_State.php: -------------------------------------------------------------------------------- 1 | post_type = $post_type; 30 | $this->option_name = "page_for_{$this->post_type}"; 31 | } 32 | 33 | /** 34 | * Inits hooks. 35 | */ 36 | public function init() { 37 | if ( ! is_admin() ) { 38 | return; 39 | } 40 | 41 | add_filter( 'display_post_states', [ $this, 'update_post_states' ], 10, 2 ); 42 | } 43 | 44 | /** 45 | * Updates post states with page for event. 46 | * 47 | * @param string[] $post_states An array of post display states. 48 | * @param \WP_Post $post The current post object. 49 | * 50 | * @return string[] Updates post states. 51 | */ 52 | public function update_post_states( $post_states, $post ) { 53 | $post_type_object = get_post_type_object( $this->post_type ); 54 | 55 | if ( 'page' === $post->post_type 56 | && (int) get_option( $this->option_name ) === $post->ID 57 | ) { 58 | $post_states[ $this->option_name ] = sprintf( 59 | /* translators: Post type label. */ 60 | __( 'Page for %s', 'mind/types' ), 61 | $post_type_object->label 62 | ); 63 | } 64 | 65 | return $post_states; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /lib/Post_Type_Query.php: -------------------------------------------------------------------------------- 1 | post_type = $post_type; 31 | $this->query_args = $this->parse_query_args( $query_args ); 32 | } 33 | 34 | /** 35 | * Inits hooks. 36 | */ 37 | public function init() { 38 | add_action( 'pre_get_posts', [ $this, 'pre_get_posts' ], 9 ); 39 | } 40 | 41 | /** 42 | * Parses the query args. 43 | * 44 | * Returns an associative array with key `frontend` and `backend` that each contain query 45 | * settings. 46 | * 47 | * @since 2.2.0 48 | * 49 | * @param array $args An array of query args. 50 | * 51 | * @return array An array of query args. 52 | */ 53 | public function parse_query_args( $args ) { 54 | $query_args = [ 55 | 'frontend' => $args, 56 | 'backend' => $args, 57 | ]; 58 | 59 | if ( isset( $args['frontend'] ) || isset( $args['backend'] ) ) { 60 | foreach ( [ 'frontend', 'backend' ] as $query_type ) { 61 | $query_args[ $query_type ] = isset( $args[ $query_type ] ) 62 | ? $args[ $query_type ] 63 | : []; 64 | } 65 | } 66 | 67 | return $query_args; 68 | } 69 | 70 | /** 71 | * Alters the query. 72 | * 73 | * @param \WP_Query $query A WP_Query object. 74 | */ 75 | public function pre_get_posts( $query ) { 76 | global $typenow; 77 | 78 | /** 79 | * Check if we should modify the query. 80 | * 81 | * As a hint for for future condition updates: We can’t use $query->is_post_type_archive(), 82 | * because some post_types have 'has_archive' set to false. 83 | */ 84 | if ( ! is_admin() ) { 85 | if ( 86 | // Special case for post in a page_for_posts setting. 87 | ( 'post' === $this->post_type && ! $query->is_home() ) 88 | // All other post types. 89 | || ( 'post' !== $this->post_type && $this->post_type !== $query->get( 'post_type' ) ) 90 | ) { 91 | return; 92 | } 93 | } elseif ( ! $query->is_main_query() || $typenow !== $this->post_type ) { 94 | return; 95 | } 96 | 97 | // Differ between frontend and backend queries. 98 | $query_args = $this->query_args[ is_admin() ? 'backend' : 'frontend' ]; 99 | 100 | if ( empty( $query_args ) ) { 101 | return; 102 | } 103 | 104 | /** 105 | * When certain args are explicitly set through a $_GET parameter, then ignore them by 106 | * removing them from the query args. Otherwise, sorting the list table in the admin won’t 107 | * work. 108 | */ 109 | if ( is_admin() ) { 110 | foreach( [ 'order', 'orderby' ] as $arg ) { 111 | if ( ! empty( $_GET[ $arg ] ) && isset( $query_args[ $arg ] ) ) { 112 | unset( $query_args[$arg ] ); 113 | } 114 | } 115 | } 116 | 117 | // Set query args. 118 | foreach ( $query_args as $key => $arg ) { 119 | $query->set( $key, $arg ); 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /lib/Post_Slug.php: -------------------------------------------------------------------------------- 1 | $callback ) { 32 | $this->post_types[ $post_type ] = [ 33 | 'callback' => $callback, 34 | ]; 35 | } 36 | } 37 | 38 | /** 39 | * Registers date suffixes for post slugs. 40 | * 41 | * @param array $post_types An associative array of post types and their suffix args. 42 | */ 43 | public function register_suffix_date( $post_types = [] ) { 44 | foreach ( $post_types as $post_type => $args ) { 45 | $this->post_types[ $post_type ] = [ 46 | 'callback' => function( $post_slug, $post_data, $post_id ) use ( $args ) { 47 | $args = wp_parse_args( $args, [ 48 | 'meta_key' => 'date_start', 49 | 'input_format' => 'Ymd', 50 | 'output_format' => 'Y-m-d', 51 | ] ); 52 | 53 | $meta_value = get_post_meta( $post_id, $args['meta_key'], true ); 54 | 55 | if ( ! $meta_value ) { 56 | return $post_slug; 57 | } 58 | 59 | $date = \DateTime::createFromFormat( $args['input_format'], $meta_value ); 60 | 61 | if ( $date ) { 62 | $post_slug = $post_data['post_title'] . '-' 63 | . $date->format( $args['output_format'] ); 64 | } 65 | 66 | return $post_slug; 67 | }, 68 | ]; 69 | } 70 | } 71 | 72 | /** 73 | * Customizes the post slug. 74 | * 75 | * @param array $data An array of slashed post data. 76 | * @param array $postarr An array of sanitized, but otherwise unmodified post data. 77 | * 78 | * @return array 79 | */ 80 | public function customize_slug( $data, $postarr ) { 81 | $bailout_states = [ 'auto-draft', 'trash' ]; 82 | $post_status = $postarr['post_status']; 83 | 84 | // Bailout if it’s not the right state. 85 | if ( in_array( $post_status, $bailout_states, true ) ) { 86 | return $data; 87 | } 88 | 89 | $post_type = $postarr['post_type']; 90 | 91 | // Bailout if no callback could be found. 92 | if ( ! in_array( $post_type, array_keys( $this->post_types ), true ) 93 | || ! is_callable( $this->post_types[ $post_type ]['callback'] ) 94 | ) { 95 | return $data; 96 | } 97 | 98 | $post_id = $postarr['ID']; 99 | $post_slug = $postarr['post_name']; 100 | $post_parent = $postarr['post_parent']; 101 | 102 | // Filter post slug through user-defined callback. 103 | $post_slug = call_user_func( $this->post_types[ $post_type ]['callback'], $post_slug, $postarr, $post_id ); 104 | 105 | // Make sure the post slug is sanitized and unique. 106 | $post_slug = sanitize_title( $post_slug ); 107 | $post_slug = wp_unique_post_slug( $post_slug, $post_id, $post_status, $post_type, $post_parent ); 108 | 109 | $data['post_name'] = $post_slug; 110 | 111 | return $data; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /lib/Post_Type_Page_Option.php: -------------------------------------------------------------------------------- 1 | post_type = $post_type; 44 | $this->customizer_section = $customizer_section; 45 | $this->option_name = "page_for_{$this->post_type}"; 46 | } 47 | 48 | /** 49 | * Inits hooks. 50 | */ 51 | public function init() { 52 | if ( ! is_admin() && ! is_customize_preview() ) { 53 | return; 54 | } 55 | 56 | add_action( 'customize_register', [ $this, 'register_settings' ] ); 57 | 58 | /** 59 | * Rewrite rules need to be flushed in the next page load after the Custom Post Type was 60 | * registered. That’s why we first need to set a transient that we check on the next admin 61 | * page load. 62 | */ 63 | add_action( 64 | "update_option_{$this->option_name}", 65 | [ $this, 'maybe_set_flush_transient' ], 66 | 10, 2 67 | ); 68 | add_action( 'admin_init', [ $this, 'maybe_flush_rewrite_rules' ] ); 69 | } 70 | 71 | /** 72 | * Adds Customizer setting and control. 73 | * 74 | * @param \WP_Customize_Manager $wp_customize Customizer instance. 75 | */ 76 | public function register_settings( WP_Customize_Manager $wp_customize ) { 77 | $post_type_object = get_post_type_object( $this->post_type ); 78 | 79 | $wp_customize->add_setting( $this->option_name, [ 80 | 'type' => 'option', 81 | ] ); 82 | 83 | $wp_customize->add_control( $this->option_name, [ 84 | 'label' => sprintf( 85 | /* translators: Post type label. */ 86 | __( 'Page for %s', 'mind/types' ), 87 | $post_type_object->label 88 | ), 89 | 'section' => $this->customizer_section, 90 | 'type' => 'dropdown-pages', 91 | 'allow_addition' => true, 92 | ] ); 93 | } 94 | 95 | /** 96 | * Sets transient to flush rewrite rules when option value changes. 97 | * 98 | * @param mixed $old_value The old option value. 99 | * @param mixed $value The new option value. 100 | */ 101 | public function maybe_set_flush_transient( $old_value, $value ) { 102 | if ( $old_value !== $value ) { 103 | set_transient( "flush_{$this->option_name}", true ); 104 | } 105 | } 106 | 107 | /** 108 | * Flushes rewrite rules when transient is set. 109 | */ 110 | public function maybe_flush_rewrite_rules() { 111 | $transient_name = "flush_{$this->option_name}"; 112 | 113 | if ( get_transient( $transient_name ) ) { 114 | delete_transient( $transient_name ); 115 | 116 | add_action( 'shutdown', function() { 117 | flush_rewrite_rules( false ); 118 | } ); 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /lib/Taxonomy.php: -------------------------------------------------------------------------------- 1 | $args ) { 23 | $args = self::parse_args( $args ); 24 | self::register_extensions( $taxonomy, $args ); 25 | 26 | $for_post_types = $args['for_post_types']; 27 | 28 | // Defaults for taxonomy registration. 29 | $args = wp_parse_args( $args['args'], [ 30 | 'public' => false, 31 | 'hierarchical' => false, 32 | 'show_ui' => true, 33 | 'show_admin_column' => true, 34 | 'show_tag_cloud' => false, 35 | ] ); 36 | 37 | register_taxonomy( $taxonomy, $for_post_types, $args ); 38 | } 39 | } 40 | 41 | /** 42 | * Updates settings for a taxonomy. 43 | * 44 | * Here, you use the same settings that you also use for the `register()` funciton. 45 | * 46 | * Run this function before the `init` hook. 47 | * 48 | * @see register_taxonomy() 49 | * @since 2.2.0 50 | * 51 | * @param array $taxonomies An associative array of post types and its arguments that should be updated. See the 52 | * `register()` function for all the arguments that you can use. 53 | */ 54 | public static function update( $taxonomies = [] ) { 55 | foreach ( $taxonomies as $taxonomy => $args ) { 56 | $args = self::parse_args( $args ); 57 | self::register_extensions( $taxonomy, $args ); 58 | 59 | if ( isset( $args['args'] ) ) { 60 | add_filter( 'register_taxonomy_args', function( $defaults, $name ) use ( $taxonomy, $args ) { 61 | if ( $taxonomy !== $name ) { 62 | return $defaults; 63 | } 64 | 65 | $args = wp_parse_args( $args['args'], $defaults ); 66 | 67 | return $args; 68 | }, 10, 2 ); 69 | } 70 | } 71 | } 72 | 73 | /** 74 | * Renames a taxonomy. 75 | * 76 | * Run this function before the `init` hook. 77 | * 78 | * @since 2.2.0 79 | * 80 | * @param string $taxonomy The taxonomy to rename. 81 | * @param string $name_singular The new singular name. 82 | * @param string $name_plural The new plural name. 83 | */ 84 | public static function rename( $taxonomy, $name_singular, $name_plural ) { 85 | if ( ! taxonomy_exists( $taxonomy ) ) { 86 | return; 87 | } 88 | 89 | ( new Taxonomy_Labels( $taxonomy, $name_singular, $name_plural ) )->init(); 90 | } 91 | 92 | /** 93 | * Adds missing arguments for taxonomy. 94 | * 95 | * @since 2.2.0 96 | * 97 | * @param array $args An array of arguments. 98 | * 99 | * @return mixed 100 | */ 101 | private static function parse_args( $args ) { 102 | if ( isset( $args['name_singular'] ) && ! isset( $args['name_plural'] ) ) { 103 | $args['name_plural'] = $args['name_singular']; 104 | } 105 | 106 | return $args; 107 | } 108 | 109 | /** 110 | * Registers extensions. 111 | * 112 | * @since 2.2.0 113 | * 114 | * @param string $taxonomy The taxonomy name. 115 | * @param array $args Arguments for the taxonomy. 116 | */ 117 | private static function register_extensions( $taxonomy, $args ) { 118 | if ( isset( $args['name_singular'] ) ) { 119 | ( new Taxonomy_Labels( $taxonomy, $args['name_singular'], $args['name_plural'] ) )->init(); 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /lib/Post_Type.php: -------------------------------------------------------------------------------- 1 | $args ) { 27 | $args = self::parse_args( $args ); 28 | 29 | self::register_extensions( $post_type, $args ); 30 | 31 | // Defaults for post registration. 32 | $args = wp_parse_args( $args['args'], [ 33 | 'description' => $args['name_plural'], 34 | 'public' => false, 35 | 'show_ui' => true, 36 | 'show_in_nav_menus' => true, 37 | ] ); 38 | 39 | register_post_type( $post_type, $args ); 40 | } 41 | } 42 | 43 | /** 44 | * Updates settings for a post type. 45 | * 46 | * Here, you use the same settings that you also use for the `register()` function. 47 | * Run this function before the `init` hook. 48 | * 49 | * @see register_post_type() 50 | * @since 2.2.0 51 | * 52 | * @param array $post_types An associative array of post types and its arguments that should be 53 | * updated. See the `register()` function for all the arguments that 54 | * you can use. 55 | */ 56 | public static function update( $post_types = [] ) { 57 | foreach ( $post_types as $post_type => $args ) { 58 | $args = self::parse_args( $args ); 59 | 60 | self::register_extensions( $post_type, $args ); 61 | 62 | if ( isset( $args['args'] ) ) { 63 | add_filter( 'register_post_type_args', function( $defaults, $name ) use ( $post_type, $args ) { 64 | if ( $post_type !== $name ) { 65 | return $defaults; 66 | } 67 | 68 | $args = wp_parse_args( $args['args'], $defaults ); 69 | 70 | return $args; 71 | }, 10, 2 ); 72 | } 73 | } 74 | } 75 | 76 | /** 77 | * Renames a post type. 78 | * 79 | * Run this function before the `init` hook. 80 | * 81 | * @since 2.1.1 82 | * 83 | * @param string $post_type The post type to rename. 84 | * @param string $name_singular The new singular name. 85 | * @param string $name_plural The new plural name. 86 | */ 87 | public static function rename( $post_type, $name_singular, $name_plural ) { 88 | ( new Post_Type_Labels( $post_type, $name_singular, $name_plural ) )->init(); 89 | } 90 | 91 | /** 92 | * Registers admin column settings for a post type. 93 | * 94 | * @since 2.1.0 95 | * 96 | * @param array $post_types An associative array of post types, where the name of the post type 97 | * is the key of an array that defines the admin column settings for 98 | * this post type. 99 | */ 100 | public static function admin_columns( $post_types = [] ) { 101 | foreach ( $post_types as $name => $column_settings ) { 102 | ( new Post_Type_Columns( $name, $column_settings ) )->init(); 103 | } 104 | } 105 | 106 | /** 107 | * Adds missing arguments for post type. 108 | * 109 | * @since 2.2.0 110 | * 111 | * @param array $args An array of arguments. 112 | * 113 | * @return mixed 114 | */ 115 | private static function parse_args( $args ) { 116 | if ( isset( $args['name_singular'] ) && ! isset( $args['name_plural'] ) ) { 117 | $args['name_plural'] = $args['name_singular']; 118 | } 119 | 120 | return $args; 121 | } 122 | 123 | /** 124 | * Registers extensions. 125 | * 126 | * @since 2.2.0 127 | * 128 | * @param string $post_type The post type name. 129 | * @param array $args Arguments for the post type. 130 | */ 131 | private static function register_extensions( $post_type, $args ) { 132 | if ( isset( $args['name_singular'] ) ) { 133 | ( new Post_Type_Labels( $post_type, $args['name_singular'], $args['name_plural'] ) )->init(); 134 | } 135 | 136 | if ( isset( $args['query'] ) ) { 137 | ( new Post_Type_Query( $post_type, $args['query'] ) )->init(); 138 | } 139 | 140 | if ( isset( $args['admin_columns'] ) ) { 141 | ( new Post_Type_Columns( $post_type, $args['admin_columns'] ) )->init(); 142 | } 143 | 144 | if ( isset( $args['page_for_archive'] ) ) { 145 | $page_for_archive = wp_parse_args( $args['page_for_archive'], [ 146 | 'post_id' => null, 147 | 'is_singular_public' => true, 148 | 'customizer_section' => '', 149 | 'show_post_state' => true, 150 | ] ); 151 | 152 | ( new Post_Type_Page( 153 | $post_type, 154 | $page_for_archive['post_id'], 155 | $page_for_archive 156 | ) )->init(); 157 | 158 | if ( ! empty( $page_for_archive['customizer_section'] ) ) { 159 | ( new Post_Type_Page_Option( 160 | $post_type, 161 | $page_for_archive['customizer_section'] 162 | ) )->init(); 163 | } 164 | 165 | if ( $page_for_archive['show_post_state'] ) { 166 | ( new Post_Type_Page_State( $post_type ) )->init(); 167 | } 168 | } 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Types 2 | 3 | ## [2.5.8](https://github.com/mindkomm/types/compare/v2.5.7...v2.5.8) (2025-12-12) 4 | 5 | 6 | ### Bug Fixes 7 | 8 | * Fix a bug when post types couldn’t be renamed ([4a33bb2](https://github.com/mindkomm/types/commit/4a33bb2634b4f32d2a39524b61830db7b17fa283)) 9 | 10 | ## [2.5.7](https://github.com/mindkomm/types/compare/2.5.6...v2.5.7) (2025-04-23) 11 | 12 | 13 | ### Bug Fixes 14 | 15 | * Fix a bug with environments where site_url() is different to home_url() ([#9](https://github.com/mindkomm/types/issues/9)) ([77ee7f7](https://github.com/mindkomm/types/commit/77ee7f75d2e1550093383c42c58225313d37f6d0)) 16 | * **i18n:** Update post type labels ([#10](https://github.com/mindkomm/types/issues/10)) ([6d3c6d6](https://github.com/mindkomm/types/commit/6d3c6d64da9a7e2407ba6ce1e5758217e323b5a6)) 17 | 18 | ## 2.5.6 - 2022-10-13 19 | 20 | - Fixed return type for label filter. 21 | - Fixed bugs when trying to access column data on columns that are not arrays (e.g. when column is set to `false`). 22 | - Improved default compatibility with custom permalink structure when using the `page_for_archive` option. 23 | 24 | ## 2.5.5 - 2021-11-30 25 | 26 | - Fixed a bug with orderby query parameters that are arrays. 27 | 28 | ## 2.5.4 - 2020-08-19 29 | 30 | - Fixed error notice. 31 | 32 | ## 2.5.3 - 2020-08-18 33 | 34 | - Added a new `column_order` argument for the `admin_columns` option. 35 | - Added a new `image` type for the `admin_columns` option. 36 | - Added a new `custom` type for the `admin_columns` option. 37 | - Fixed a bug when updating the label for a custom post type didn’t work properly. 38 | - Fixed a bug when search for meta for title. 39 | 40 | ## 2.5.2 - 2020-07-29 41 | 42 | - Fixed a bug when wrong translation loaded in admin. 43 | - Fixed a bug when searching for post titles didn’t work in combination with meta value searches. 44 | - Fixed some code style issues (thanks, @szepeviktor). 45 | 46 | ## 2.5.1 - 2020-07-08 47 | 48 | - Fixed a PHP notice. 49 | 50 | ## 2.5.0 - 2020-07-08 51 | 52 | - Fixed `sortable` parameter for the `admin_columns` option. Sorting didn’t work properly before. It should work now. 53 | - Changed default for `sortable` parameter for the `admin_columns` option from `true` to `false`. 54 | - Added new `orderby` parameter for the `admin_columns` option that will be used in combination with the `sortable` parameter. 55 | - Fixed a bug when the `query` option was interfering with sorting queries in post list tables. 56 | 57 | ## 2.4.4 - 2020-02-11 58 | 59 | - Added Dutch translations. 60 | - Added `searchable` option as an option for admin columns to make meta columns searchable. 61 | - Changed default column type for admin columns from `default` to `meta` to be more explicit. 62 | 63 | ## 2.4.3 - 2019-11-13 64 | 65 | This release improves compatibility with multisite environments, especially when working with [MultilingualPress](https://multilingualpress.de/). 66 | 67 | - Fixed bug when page archives didn’t work in multilingual multisite environments. 68 | - Fixed bug when proper archive link couldn’t be selected in multisite environment. 69 | 70 | ## 2.4.2 - 2019-10-17 71 | 72 | - Fixed bug when no page was selected for a custom post type archive in `Post_Type_Page_Option`. 73 | 74 | ## 2.4.1 - 2019-08-05 75 | 76 | - Added filter to update the title for the post type archive when using `post_type_archive_title()`. 77 | 78 | ## 2.4.0 - 2019-06-28 79 | 80 | - Added `Post_Type_Page_Option` class, which registers an option to select the page to act as the custom post type archive in the Customizer for you. 81 | - Added `Post_Type_Page_State` class, which adds a post state label to the the pages overview in the admin to recognize pages that act as custom post type archives quicker. 82 | 83 | Read more about these functionalities in the [`page_for_archive`](https://github.com/mindkomm/types#page_for_archive) section in the README. 84 | 85 | ## 2.3.2 - 2019-06-07 86 | 87 | - Added edit page link to admin bar for archive pages. 88 | 89 | ## 2.3.1 - 2019-03-18 90 | 91 | - Fixed CSS classes added to parent and ancestor menu items. 92 | 93 | ## 2.3.0 - 2019-03-06 94 | 95 | - Added new `Types\Post_Type_Page` class. This class allows you to define a page that should act as the archive for a Custom Post Type [using the `page_for_archive` option](https://github.com/mindkomm/types#page_for_archive). 96 | - Fixed a bug with an undefined function #2 (Thanks @roylodder). 97 | 98 | ## 2.2.3 - 2019-01-17 99 | 100 | - Added new post type labels (). 101 | - Fixed wrong label assignment. 102 | 103 | ## 2.2.2 - 2018-10-08 104 | 105 | - Updated post slug feature to not run when a post is being trashed. 106 | - Fixed bug when a post slug couldn’t be set when a date couldn’t be parsed. When the date can’t be parsed, the post slug won’t be changed. 107 | 108 | ## 2.2.1 - 2018-09-20 109 | 110 | - Fixed bug when certain values didn’t exist yet for a post. 111 | 112 | ## 2.2.0 - 2018-08-29 113 | 114 | - Added better handling of labels by updating the messages displayed in the backend and making it possible to properly translate the labels. This will open up the repository for additional languages. 115 | - Added new function `update()` to update the settings for existing post types and taxonomies. 116 | - Added new function `rename()` to rename existing post types and taxonomies. 117 | - Added option for post types to have separate queries with a `frontend` and `backend` argument for `query`. 118 | - Fixed when a post slug wasn’t updated on first save. 119 | 120 | ## 2.1.0 - 2018-08-27 121 | 122 | - Added new function `admin_columns()` to customize admin columns for already registered post types. 123 | - Added special `thumbnail` column key to display the featured image. 124 | - Added `sortable` option for a column to define whether it’s sortable. 125 | 126 | ## 2.0.1 - 2018-08-23 127 | 128 | - Fixed undefined index notices. 129 | 130 | ## 2.0.0 - 2018-08-22 131 | 132 | - Renamed package from «Custom Types» to just «Types». 133 | - Renamed classes. 134 | - Renamed registration methods. 135 | - Added helper class to customize post slugs when posts are saved. 136 | 137 | ## 1.1.0 - 2018-08-21 138 | 139 | - Added `query` argument to define how posts are queried in the front- and backend. 140 | - Added `admin_columns` argument to define the admin columns that should be displayed. 141 | 142 | ## 1.0.0 - 2018-02-13 143 | 144 | Initial release. 145 | -------------------------------------------------------------------------------- /lib/Taxonomy_Labels.php: -------------------------------------------------------------------------------- 1 | taxonomy = $taxonomy; 50 | $this->name_singular = $name_singular; 51 | $this->name_plural = $name_plural; 52 | $this->labels = $this->get_labels( $name_singular, $name_plural ); 53 | } 54 | 55 | /** 56 | * Inits hooks. 57 | */ 58 | public function init() { 59 | add_filter( "taxonomy_labels_{$this->taxonomy}", function() { 60 | return $this->labels; 61 | } ); 62 | 63 | if ( is_admin() ) { 64 | // Update messages in backend. 65 | add_filter( 'term_updated_messages', [ $this, 'add_term_updated_messages' ] ); 66 | } 67 | } 68 | 69 | /** 70 | * Get labels for taxonomy base on singular and plural name. 71 | * 72 | * The following labels are not translated, because they don’t contain the post type name: 73 | * most_used 74 | * 75 | * @link https://developer.wordpress.org/reference/functions/get_taxonomy_labels/ 76 | * 77 | * @param string $name_singular Singular name for taxonomy. 78 | * @param string $name_plural Plural name for taxonomy. 79 | * 80 | * @return array The translated labels. 81 | */ 82 | public function get_labels( $name_singular, $name_plural ) { 83 | $labels = [ 84 | 'name' => $name_plural, 85 | 'singular_name' => $name_singular, 86 | /* translators: %s: Singular taxonomy name */ 87 | 'add_new_item' => sprintf( __( 'Add New %s', 'mind/types' ), $name_singular ), 88 | /* translators: %s: Plural taxonomy name */ 89 | 'add_or_remove_items' => sprintf( __( 'Add or remove %s', 'mind/types' ), $name_plural ), 90 | /* translators: %s: Plural taxonomy name */ 91 | 'all_items' => sprintf( __( 'All %s', 'mind/types' ), $name_plural ), 92 | /* translators: %s: Singular post type name */ 93 | 'archives' => sprintf( __( '%s Archives', 'mind/types' ), $name_singular ), 94 | /* translators: %s: Plural taxonomy name */ 95 | 'back_to_items' => sprintf( __( '← Back to %s', 'mind/types' ), $name_plural ), 96 | /* translators: %s: Plural taxonomy name */ 97 | 'choose_from_most_used' => sprintf( __( 'Choose from the most used %s', 'mind/types' ), $name_plural ), 98 | /* translators: %s: Singular taxonomy name */ 99 | 'edit_item' => sprintf( __( 'Edit %s', 'mind/types' ), $name_singular ), 100 | /* translators: %s: Plural taxonomy name */ 101 | 'items_list' => sprintf( __( '%s list', 'mind/types' ), $name_plural ), 102 | /* translators: %s: Plural taxonomy name */ 103 | 'items_list_navigation' => sprintf( __( '%s list navigation', 'mind/types' ), $name_plural ), 104 | /* translators: %s: Singular taxonomy name */ 105 | 'new_item_name' => sprintf( __( 'New %s Name', 'mind/types' ), $name_singular ), 106 | /* translators: %s: Plural taxonomy name */ 107 | 'no_terms' => sprintf( __( 'No %s', 'mind/types' ), $name_plural ), 108 | /* translators: %s: Plural taxonomy name */ 109 | 'not_found' => sprintf( __( 'No %s found.', 'mind/types' ), $name_plural ), 110 | /* translators: %s: Singular taxonomy name */ 111 | 'parent_item' => sprintf( __( 'Parent %s', 'mind/types' ), $name_singular ), 112 | /* translators: %s: Singular taxonomy name */ 113 | 'parent_item_colon' => sprintf( __( 'Parent %s:', 'mind/types' ), $name_singular ), 114 | /* translators: %s: Plural taxonomy name */ 115 | 'popular_items' => sprintf( __( 'Popular %s', 'mind/types' ), $name_plural ), 116 | /* translators: %s: Plural taxonomy name */ 117 | 'search_items' => sprintf( __( 'Search %s', 'mind/types' ), $name_plural ), 118 | /* translators: %s: Plural taxonomy name */ 119 | 'separate_items_with_commas' => sprintf( __( 'Separate %s with commas', 'mind/types' ), $name_plural ), 120 | /* translators: %s: Singular taxonomy name */ 121 | 'update_item' => sprintf( __( 'Update %s', 'mind/types' ), $name_singular ), 122 | /* translators: %s: Singular taxonomy name */ 123 | 'view_item' => sprintf( __( 'View %s', 'mind/types' ), $name_singular ), 124 | 'name_admin_bar' => $name_singular, 125 | 'menu_name' => $name_plural, 126 | ]; 127 | 128 | return $labels; 129 | } 130 | 131 | /** 132 | * Sets term updated messages for custom taxonomies. 133 | * 134 | * Check out the `term_updated_messages` in wp-admin/includes/edit-tag-messages.php. 135 | * 136 | * @param array $messages An associative array of taxonomies and their messages. 137 | * 138 | * @return array The filtered messages. 139 | */ 140 | public function add_term_updated_messages( $messages ) { 141 | $messages[ $this->taxonomy ] = [ 142 | 0 => '', 143 | /* translators: %s: Singular taxonomy name */ 144 | 1 => sprintf( __( '%s added.', 'mind/types' ), $this->name_singular ), 145 | /* translators: %s: Singular taxonomy name */ 146 | 2 => sprintf( __( '%s deleted.', 'mind/types' ), $this->name_singular ), 147 | /* translators: %s: Singular taxonomy name */ 148 | 3 => sprintf( __( '%s updated.', 'mind/types' ), $this->name_singular ), 149 | /* translators: %s: Singular taxonomy name */ 150 | 4 => sprintf( __( '%s not added.', 'mind/types' ), $this->name_singular ), 151 | /* translators: %s: Singular taxonomy name */ 152 | 5 => sprintf( __( '%s not updated.', 'mind/types' ), $this->name_singular ), 153 | /* translators: %s: Plural taxonomy name */ 154 | 6 => sprintf( __( '%s deleted.', 'mind/types' ), $this->name_plural ), 155 | ]; 156 | 157 | return $messages; 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /languages/types.pot: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: \n" 4 | "Report-Msgid-Bugs-To: \n" 5 | "Last-Translator: FULL NAME \n" 6 | "Language-Team: LANGUAGE \n" 7 | "MIME-Version: 1.0\n" 8 | "Content-Type: text/plain; charset=UTF-8\n" 9 | "Content-Transfer-Encoding: 8bit\n" 10 | "POT-Creation-Date: 2025-04-23T15:32:16+02:00\n" 11 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 12 | "X-Generator: WP-CLI 2.11.0\n" 13 | "X-Domain: mind/types\n" 14 | 15 | #: lib/Post_Type_Columns.php:37 16 | msgid "Featured Image" 17 | msgstr "" 18 | 19 | #: lib/Post_Type_Labels.php:77 20 | msgid "Add New" 21 | msgstr "" 22 | 23 | #. translators: %s: Singular post type name 24 | #. translators: %s: Singular taxonomy name 25 | #: lib/Post_Type_Labels.php:80 26 | #: lib/Taxonomy_Labels.php:87 27 | msgid "Add New %s" 28 | msgstr "" 29 | 30 | #. translators: %s: Plural post type name 31 | #. translators: %s: Plural taxonomy name 32 | #: lib/Post_Type_Labels.php:85 33 | #: lib/Taxonomy_Labels.php:91 34 | msgid "All %s" 35 | msgstr "" 36 | 37 | #. translators: %s: Singular post type name 38 | #: lib/Post_Type_Labels.php:90 39 | #: lib/Taxonomy_Labels.php:93 40 | msgid "%s Archives" 41 | msgstr "" 42 | 43 | #. translators: %s: Singular post type name 44 | #: lib/Post_Type_Labels.php:95 45 | msgid "%s Attributes" 46 | msgstr "" 47 | 48 | #. translators: %s: Singular post type name 49 | #. translators: %s: Singular taxonomy name 50 | #: lib/Post_Type_Labels.php:100 51 | #: lib/Taxonomy_Labels.php:99 52 | msgid "Edit %s" 53 | msgstr "" 54 | 55 | #. translators: %s: Singular post type name 56 | #: lib/Post_Type_Labels.php:105 57 | msgid "Featured Image for %s" 58 | msgstr "" 59 | 60 | #. translators: %s: Plural post type name 61 | #: lib/Post_Type_Labels.php:110 62 | msgid "Filter %s list" 63 | msgstr "" 64 | 65 | #. translators: %s: Singular post type name 66 | #: lib/Post_Type_Labels.php:115 67 | msgid "Insert into %s" 68 | msgstr "" 69 | 70 | #. translators: %s: Plural post type name 71 | #. translators: %s: Plural taxonomy name 72 | #: lib/Post_Type_Labels.php:120 73 | #: lib/Taxonomy_Labels.php:101 74 | msgid "%s list" 75 | msgstr "" 76 | 77 | #. translators: %s: Plural post type name 78 | #. translators: %s: Plural taxonomy name 79 | #: lib/Post_Type_Labels.php:125 80 | #: lib/Taxonomy_Labels.php:103 81 | msgid "%s list navigation" 82 | msgstr "" 83 | 84 | #. translators: %s: Singular post type name 85 | #: lib/Post_Type_Labels.php:130 86 | #: lib/Post_Type_Labels.php:251 87 | msgid "%s published." 88 | msgstr "" 89 | 90 | #. translators: %s: Singular post type name 91 | #: lib/Post_Type_Labels.php:135 92 | msgid "%s published privately." 93 | msgstr "" 94 | 95 | #. translators: %s: Singular post type name 96 | #: lib/Post_Type_Labels.php:140 97 | msgid "%s reverted to draft." 98 | msgstr "" 99 | 100 | #. translators: %s: Singular post type name 101 | #: lib/Post_Type_Labels.php:145 102 | msgid "%s trashed." 103 | msgstr "" 104 | 105 | #. translators: %s: Singular post type name 106 | #: lib/Post_Type_Labels.php:150 107 | msgid "%s scheduled." 108 | msgstr "" 109 | 110 | #. translators: %s: Singular post type name 111 | #. translators: %s: Singular taxonomy name 112 | #: lib/Post_Type_Labels.php:155 113 | #: lib/Post_Type_Labels.php:247 114 | #: lib/Post_Type_Labels.php:249 115 | #: lib/Taxonomy_Labels.php:148 116 | msgid "%s updated." 117 | msgstr "" 118 | 119 | #. translators: %s: Singular post type name 120 | #: lib/Post_Type_Labels.php:160 121 | msgid "New %s" 122 | msgstr "" 123 | 124 | #. translators: %s: Plural post type name 125 | #. translators: %s: Plural taxonomy name 126 | #: lib/Post_Type_Labels.php:165 127 | #: lib/Taxonomy_Labels.php:109 128 | msgid "No %s found." 129 | msgstr "" 130 | 131 | #. translators: %s: Plural post type name 132 | #: lib/Post_Type_Labels.php:170 133 | msgid "No %s found in trash." 134 | msgstr "" 135 | 136 | #. translators: %s: Singular post type name 137 | #. translators: %s: Singular taxonomy name 138 | #: lib/Post_Type_Labels.php:175 139 | #: lib/Taxonomy_Labels.php:111 140 | msgid "Parent %s" 141 | msgstr "" 142 | 143 | #. translators: %s: Singular post type name 144 | #. translators: %s: Singular taxonomy name 145 | #: lib/Post_Type_Labels.php:180 146 | #: lib/Taxonomy_Labels.php:113 147 | msgid "Parent %s:" 148 | msgstr "" 149 | 150 | #. translators: %s: Plural post type name 151 | #. translators: %s: Plural taxonomy name 152 | #: lib/Post_Type_Labels.php:185 153 | #: lib/Taxonomy_Labels.php:117 154 | msgid "Search %s" 155 | msgstr "" 156 | 157 | #. translators: %s: Singular post type name 158 | #: lib/Post_Type_Labels.php:190 159 | msgid "Uploaded to this %s" 160 | msgstr "" 161 | 162 | #. translators: %s: Singular post type name 163 | #. translators: %s: Plural post type name 164 | #. translators: %s: Singular taxonomy name 165 | #: lib/Post_Type_Labels.php:195 166 | #: lib/Post_Type_Labels.php:200 167 | #: lib/Post_Type_Labels.php:237 168 | #: lib/Taxonomy_Labels.php:123 169 | msgid "View %s" 170 | msgstr "" 171 | 172 | #. translators: %s: Singular post type name 173 | #: lib/Post_Type_Labels.php:228 174 | msgid "Preview %s" 175 | msgstr "" 176 | 177 | #. translators: %s: Singular post type name 178 | #: lib/Post_Type_Labels.php:253 179 | msgid "%s saved." 180 | msgstr "" 181 | 182 | #. translators: %s: Singular post type name 183 | #: lib/Post_Type_Labels.php:255 184 | msgid "%s submitted." 185 | msgstr "" 186 | 187 | #. translators: %s: Singular post type name 188 | #: lib/Post_Type_Labels.php:257 189 | msgid "%s draft updated." 190 | msgstr "" 191 | 192 | #. translators: Plural name of the post type 193 | #: lib/Post_Type_Page.php:233 194 | msgid "Edit page for %s" 195 | msgstr "" 196 | 197 | #. translators: Post type label. 198 | #: lib/Post_Type_Page_Option.php:86 199 | #: lib/Post_Type_Page_State.php:60 200 | msgid "Page for %s" 201 | msgstr "" 202 | 203 | #. translators: %s: Plural taxonomy name 204 | #: lib/Taxonomy_Labels.php:89 205 | msgid "Add or remove %s" 206 | msgstr "" 207 | 208 | #. translators: %s: Plural taxonomy name 209 | #: lib/Taxonomy_Labels.php:95 210 | msgid "← Back to %s" 211 | msgstr "" 212 | 213 | #. translators: %s: Plural taxonomy name 214 | #: lib/Taxonomy_Labels.php:97 215 | msgid "Choose from the most used %s" 216 | msgstr "" 217 | 218 | #. translators: %s: Singular taxonomy name 219 | #: lib/Taxonomy_Labels.php:105 220 | msgid "New %s Name" 221 | msgstr "" 222 | 223 | #. translators: %s: Plural taxonomy name 224 | #: lib/Taxonomy_Labels.php:107 225 | msgid "No %s" 226 | msgstr "" 227 | 228 | #. translators: %s: Plural taxonomy name 229 | #: lib/Taxonomy_Labels.php:115 230 | msgid "Popular %s" 231 | msgstr "" 232 | 233 | #. translators: %s: Plural taxonomy name 234 | #: lib/Taxonomy_Labels.php:119 235 | msgid "Separate %s with commas" 236 | msgstr "" 237 | 238 | #. translators: %s: Singular taxonomy name 239 | #: lib/Taxonomy_Labels.php:121 240 | msgid "Update %s" 241 | msgstr "" 242 | 243 | #. translators: %s: Singular taxonomy name 244 | #: lib/Taxonomy_Labels.php:144 245 | msgid "%s added." 246 | msgstr "" 247 | 248 | #. translators: %s: Singular taxonomy name 249 | #. translators: %s: Plural taxonomy name 250 | #: lib/Taxonomy_Labels.php:146 251 | #: lib/Taxonomy_Labels.php:154 252 | msgid "%s deleted." 253 | msgstr "" 254 | 255 | #. translators: %s: Singular taxonomy name 256 | #: lib/Taxonomy_Labels.php:150 257 | msgid "%s not added." 258 | msgstr "" 259 | 260 | #. translators: %s: Singular taxonomy name 261 | #: lib/Taxonomy_Labels.php:152 262 | msgid "%s not updated." 263 | msgstr "" 264 | -------------------------------------------------------------------------------- /lib/Post_Type_Page.php: -------------------------------------------------------------------------------- 1 | post_type = $post_type; 45 | $this->post_id = (int) $post_id; 46 | 47 | $this->args = wp_parse_args( $args, [ 48 | 'is_singular_public' => true, 49 | ] ); 50 | } 51 | 52 | /** 53 | * Inits hooks. 54 | */ 55 | public function init() { 56 | /** 57 | * Bail out if no valid post ID is provided or post ID is 0, which happens when no page is 58 | * selected from dropdown-pages. 59 | */ 60 | if ( ! $this->post_id ) { 61 | return; 62 | } 63 | 64 | add_filter( 'register_post_type_args', [ $this, 'update_archive_slug' ], 10, 2 ); 65 | add_filter( 'post_type_archive_link', [ $this, 'update_archive_link' ], 10, 2 ); 66 | 67 | add_filter( 'wp_nav_menu_objects', [ $this, 'filter_wp_nav_menu_objects' ], 1 ); 68 | add_filter( 'post_type_archive_title', [ $this, 'set_post_type_archive_title' ], 10, 2 ); 69 | 70 | if ( ! $this->args['is_singular_public'] ) { 71 | add_action( 'template_redirect', [ $this, 'template_redirect' ] ); 72 | } 73 | 74 | if ( ! is_admin() ) { 75 | add_action( 'admin_bar_menu', [ $this, 'add_page_edit_link' ], 80 ); 76 | } 77 | } 78 | 79 | /** 80 | * Update the archive slug to be the same as the page that should be used for the archive. 81 | * 82 | * Setting the `has_archive` property will set the proper rewrite rules so that the page URL 83 | * will be used as the archive page. 84 | * 85 | * @param array $args Post type registration arguments. 86 | * @param string $post_type Post type name. 87 | * 88 | * @return mixed 89 | */ 90 | public function update_archive_slug( $args, $post_type ) { 91 | if ( $post_type !== $this->post_type ) { 92 | return $args; 93 | } 94 | 95 | $link = get_permalink( $this->post_id ); 96 | 97 | /** 98 | * We need to strip away the current base URL from the link, so that we 99 | * get the relative link. It’s not enough to use wp_make_link_relative(), 100 | * because then WordPress websites in subfolders wouldn’t work. This is 101 | * often the case in multisite environments. 102 | */ 103 | $link = str_replace( home_url(), '', $link ); 104 | 105 | // Trim leading and trailing slashes. 106 | $link = trim( $link, '/' ); 107 | 108 | $args['has_archive'] = $link; 109 | 110 | /** 111 | * Make sure with_front is set to false by default. 112 | * 113 | * If you set a custom permalink base in Settings → Permalink, then that permalink is 114 | * prepended to all the custom post types that have with_front set to true. For example: 115 | * 116 | * If you have a post type called book and set /blog/%postname%/ as your permalink 117 | * structure, then /blog/ will be prepended to your custom post type, which will result in 118 | * /blog/book/. 119 | * 120 | * Mostly, this is not what we want and why we set with_front to false by default. 121 | */ 122 | $args['rewrite'] = wp_parse_args( $args['rewrite'] ?? [], [ 123 | 'with_front' => false, 124 | ] ); 125 | 126 | return $args; 127 | } 128 | 129 | /** 130 | * Filters the post type archive permalink. 131 | * 132 | * This filter is needed for links to be returned properly in multisite environments, when the 133 | * get_post_type_archive_link() function is called after switch_to_blog() was used. 134 | * 135 | * @since 2.4.3 136 | * @see \get_post_type_archive_link() 137 | * 138 | * @param string $link The post type archive permalink. 139 | * @param string $post_type Post type name. 140 | * 141 | * @return string 142 | */ 143 | public function update_archive_link( $link, $post_type ) { 144 | if ( $post_type !== $this->post_type ) { 145 | return $link; 146 | } 147 | 148 | return get_permalink( $this->post_id ); 149 | } 150 | 151 | /** 152 | * Redirects singular page views to the post type archive page. 153 | */ 154 | public function template_redirect() { 155 | if ( is_singular( $this->post_type ) ) { 156 | wp_safe_redirect( get_post_type_archive_link( $this->post_type ), 301 ); 157 | exit; 158 | } 159 | } 160 | 161 | /** 162 | * Make sure menu items for our pages get the correct classes assigned. 163 | * 164 | * @param array $menu_items Array of menu items. 165 | * 166 | * @return array 167 | */ 168 | public function filter_wp_nav_menu_objects( $menu_items ) { 169 | foreach ( $menu_items as &$item ) { 170 | if ( 'page' !== $item->object || (int) $item->object_id !== $this->post_id ) { 171 | continue; 172 | } 173 | 174 | if ( is_singular( $this->post_type ) ) { 175 | $item->current_item_parent = true; 176 | $item->classes[] = 'current-menu-parent'; 177 | 178 | $menu_items = \Types\menu_items_ancestors( $item, $menu_items ); 179 | } 180 | 181 | if ( is_post_type_archive( $this->post_type ) ) { 182 | $item->classes[] = 'current-menu-item'; 183 | $item->current = true; 184 | 185 | $menu_items = \Types\menu_items_ancestors( $item, $menu_items ); 186 | } 187 | } 188 | 189 | return $menu_items; 190 | } 191 | 192 | /** 193 | * Filters the post type archive title to match the title of the post type archive page. 194 | * 195 | * @since 2.4.1 196 | * @see post_type_archive_title() 197 | * 198 | * @param string $title The archive title. 199 | * @param string $post_type The post type. 200 | * 201 | * @return string The title for the archive. 202 | */ 203 | public function set_post_type_archive_title( $title, $post_type ) { 204 | if ( $this->post_type !== $post_type ) { 205 | return $title; 206 | } 207 | 208 | return get_the_title( $this->post_id ); 209 | } 210 | 211 | /** 212 | * Adds a page edit link for the page that acts as the archive to the admin bar. 213 | * 214 | * @see wp_admin_bar_edit_menu() 215 | * 216 | * @since 2.3.2 217 | * @param \WP_Admin_Bar $wp_admin_bar WP_Admin_Bar instance. 218 | */ 219 | public function add_page_edit_link( $wp_admin_bar ) { 220 | $object = get_queried_object(); 221 | 222 | if ( empty( $object ) 223 | || ! $object instanceof \WP_Post_Type 224 | || $object->name !== $this->post_type 225 | || ! $object->show_in_admin_bar 226 | || ! current_user_can( 'edit_pages', $this->post_id ) 227 | ) { 228 | return; 229 | } 230 | 231 | $wp_admin_bar->add_menu( [ 232 | 'id' => 'edit', 233 | /* translators: Plural name of the post type */ 234 | 'title' => sprintf( __( 'Edit page for %s', 'mind/types' ), $object->labels->name ), 235 | 'href' => get_edit_post_link( $this->post_id ), 236 | ] ); 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /languages/types-nl_NL.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: \n" 4 | "Report-Msgid-Bugs-To: \n" 5 | "POT-Creation-Date: 2019-06-28T09:32:42+02:00\n" 6 | "PO-Revision-Date: 2020-02-11 18:22+0100\n" 7 | "Language-Team: \n" 8 | "Language: nl_NL\n" 9 | "MIME-Version: 1.0\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | "X-Generator: Poedit 2.3\n" 13 | "X-Domain: mind/types\n" 14 | "Last-Translator: Roy van Toren\n" 15 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 16 | 17 | #. translators: Plural name of the post type 18 | #: lib/Post_Type_Page.php:153 19 | msgid "Edit page for %s" 20 | msgstr "Pagina voor %s bewerken" 21 | 22 | #. translators: Post type label. 23 | #: lib/Post_Type_Page_Option.php:107 lib/Post_Type_Page_Option.php:159 24 | msgid "Page for %s" 25 | msgstr "Pagina voor %s" 26 | 27 | #. translators: %s: Singular taxonomy name 28 | #. translators: %s: Singular post type name 29 | #: lib/Taxonomy_Labels.php:87 lib/Post_Type_Labels.php:90 30 | msgid "Add New %s" 31 | msgstr "Nieuwe %s toevoegen" 32 | 33 | #. translators: %s: Plural taxonomy name 34 | #: lib/Taxonomy_Labels.php:89 35 | msgid "Add or remove %s" 36 | msgstr "%s toevoegen of verwijderen" 37 | 38 | #. translators: %s: Plural taxonomy name 39 | #. translators: %s: Plural post type name 40 | #: lib/Taxonomy_Labels.php:91 lib/Post_Type_Labels.php:92 41 | msgid "All %s" 42 | msgstr "Alle %s" 43 | 44 | #. translators: %s: Singular post type name 45 | #: lib/Taxonomy_Labels.php:93 lib/Post_Type_Labels.php:94 46 | msgid "%s Archives" 47 | msgstr "%s archief" 48 | 49 | #. translators: %s: Plural taxonomy name 50 | #: lib/Taxonomy_Labels.php:95 51 | msgid "← Back to %s" 52 | msgstr "← Terug naar %s" 53 | 54 | #. translators: %s: Plural taxonomy name 55 | #: lib/Taxonomy_Labels.php:97 56 | msgid "Choose from the most used %s" 57 | msgstr "Kies uit de meest gebruikte %s" 58 | 59 | #. translators: %s: Singular taxonomy name 60 | #. translators: %s: Singular post type name 61 | #: lib/Taxonomy_Labels.php:99 lib/Post_Type_Labels.php:98 62 | msgid "Edit %s" 63 | msgstr "%s bewerken" 64 | 65 | #. translators: %s: Plural taxonomy name 66 | #. translators: %s: Plural post type name 67 | #: lib/Taxonomy_Labels.php:101 lib/Post_Type_Labels.php:106 68 | msgid "%s list" 69 | msgstr "%slijst" 70 | 71 | #. translators: %s: Plural taxonomy name 72 | #. translators: %s: Plural post type name 73 | #: lib/Taxonomy_Labels.php:103 lib/Post_Type_Labels.php:108 74 | msgid "%s list navigation" 75 | msgstr "Navigatie door %slijst" 76 | 77 | #. translators: %s: Singular taxonomy name 78 | #: lib/Taxonomy_Labels.php:105 79 | msgid "New %s Name" 80 | msgstr "Nieuwe %snaam" 81 | 82 | #. translators: %s: Plural taxonomy name 83 | #: lib/Taxonomy_Labels.php:107 84 | msgid "No %s" 85 | msgstr "Geen %s" 86 | 87 | #. translators: %s: Plural taxonomy name 88 | #. translators: %s: Plural post type name 89 | #: lib/Taxonomy_Labels.php:109 lib/Post_Type_Labels.php:122 90 | msgid "No %s found." 91 | msgstr "Geen %s gevonden." 92 | 93 | #. translators: %s: Singular taxonomy name 94 | #. translators: %s: Singular post type name 95 | #: lib/Taxonomy_Labels.php:111 lib/Post_Type_Labels.php:126 96 | msgid "Parent %s" 97 | msgstr "Hoofd %s" 98 | 99 | #. translators: %s: Singular taxonomy name 100 | #. translators: %s: Singular post type name 101 | #: lib/Taxonomy_Labels.php:113 lib/Post_Type_Labels.php:128 102 | msgid "Parent %s:" 103 | msgstr "Hoofd %s:" 104 | 105 | #. translators: %s: Plural taxonomy name 106 | #: lib/Taxonomy_Labels.php:115 107 | msgid "Popular %s" 108 | msgstr "Populaire %s" 109 | 110 | #. translators: %s: Plural taxonomy name 111 | #. translators: %s: Plural post type name 112 | #: lib/Taxonomy_Labels.php:117 lib/Post_Type_Labels.php:130 113 | msgid "Search %s" 114 | msgstr "%s zoeken" 115 | 116 | #. translators: %s: Plural taxonomy name 117 | #: lib/Taxonomy_Labels.php:119 118 | msgid "Separate %s with commas" 119 | msgstr "%s scheiden door komma’s" 120 | 121 | #. translators: %s: Singular taxonomy name 122 | #: lib/Taxonomy_Labels.php:121 123 | msgid "Update %s" 124 | msgstr "%s bijwerken" 125 | 126 | #. translators: %s: Singular taxonomy name 127 | #. translators: %s: Singular post type name 128 | #. translators: %s: Plural post type name 129 | #: lib/Taxonomy_Labels.php:123 lib/Post_Type_Labels.php:134 130 | #: lib/Post_Type_Labels.php:136 lib/Post_Type_Labels.php:173 131 | msgid "View %s" 132 | msgstr "%s bekijken" 133 | 134 | #. translators: %s: Singular taxonomy name 135 | #: lib/Taxonomy_Labels.php:144 136 | msgid "%s added." 137 | msgstr "%s toegevoegd." 138 | 139 | #. translators: %s: Singular taxonomy name 140 | #. translators: %s: Plural taxonomy name 141 | #: lib/Taxonomy_Labels.php:146 lib/Taxonomy_Labels.php:154 142 | msgid "%s deleted." 143 | msgstr "%s verwijderd." 144 | 145 | #. translators: %s: Singular taxonomy name 146 | #. translators: %s: Singular post type name 147 | #: lib/Taxonomy_Labels.php:148 lib/Post_Type_Labels.php:118 148 | #: lib/Post_Type_Labels.php:183 lib/Post_Type_Labels.php:185 149 | msgid "%s updated." 150 | msgstr "%s bijgewerkt." 151 | 152 | #. translators: %s: Singular taxonomy name 153 | #: lib/Taxonomy_Labels.php:150 154 | msgid "%s not added." 155 | msgstr "%s niet toegevoegd." 156 | 157 | #. translators: %s: Singular taxonomy name 158 | #: lib/Taxonomy_Labels.php:152 159 | msgid "%s not updated." 160 | msgstr "%s niet bijgewerkt." 161 | 162 | #: lib/Post_Type_Columns.php:37 163 | msgid "Featured Image" 164 | msgstr "Uitgelichte afbeelding" 165 | 166 | #: lib/Post_Type_Labels.php:88 167 | msgid "Add New" 168 | msgstr "Nieuwe toevoegen" 169 | 170 | #. translators: %s: Singular post type name 171 | #: lib/Post_Type_Labels.php:96 172 | msgid "%s Attributes" 173 | msgstr "%s-attributen" 174 | 175 | #. translators: %s: Singular post type name 176 | #: lib/Post_Type_Labels.php:100 177 | msgid "Featured Image for %s" 178 | msgstr "Uitgelichte afbeelding voor %s" 179 | 180 | #. translators: %s: Plural post type name 181 | #: lib/Post_Type_Labels.php:102 182 | msgid "Filter %s list" 183 | msgstr "%s filteren" 184 | 185 | #. translators: %s: Singular post type name 186 | #: lib/Post_Type_Labels.php:104 187 | msgid "Insert into %s" 188 | msgstr "In %s invoegen" 189 | 190 | #. translators: %s: Singular post type name 191 | #: lib/Post_Type_Labels.php:110 lib/Post_Type_Labels.php:187 192 | msgid "%s published." 193 | msgstr "%s gepubliceerd." 194 | 195 | #. translators: %s: Singular post type name 196 | #: lib/Post_Type_Labels.php:112 197 | msgid "%s published privately." 198 | msgstr "%s privé gepubliceerd." 199 | 200 | #. translators: %s: Singular post type name 201 | #: lib/Post_Type_Labels.php:114 202 | msgid "%s reverted to draft." 203 | msgstr "%s teruggezet naar concept." 204 | 205 | #. translators: %s: Singular post type name 206 | #: lib/Post_Type_Labels.php:116 207 | msgid "%s scheduled." 208 | msgstr "%s gepland." 209 | 210 | #. translators: %s: Singular post type name 211 | #: lib/Post_Type_Labels.php:120 212 | msgid "New %s" 213 | msgstr "Nieuwe %s" 214 | 215 | #. translators: %s: Plural post type name 216 | #: lib/Post_Type_Labels.php:124 217 | msgid "No %s found in Trash." 218 | msgstr "Geen %s gevonden in de prullenbak." 219 | 220 | #. translators: %s: Singular post type name 221 | #: lib/Post_Type_Labels.php:132 222 | msgid "Uploaded to this %s" 223 | msgstr "Geüpload naar deze %s" 224 | 225 | #. translators: %s: Singular post type name 226 | #: lib/Post_Type_Labels.php:164 227 | msgid "Preview %s" 228 | msgstr "Voorbeeld %s" 229 | 230 | #. translators: %s: Singular post type name 231 | #: lib/Post_Type_Labels.php:189 232 | msgid "%s saved." 233 | msgstr "%s opgeslagen." 234 | 235 | #. translators: %s: Singular post type name 236 | #: lib/Post_Type_Labels.php:191 237 | msgid "%s submitted." 238 | msgstr "%s ingediend." 239 | 240 | #. translators: %s: Singular post type name 241 | #: lib/Post_Type_Labels.php:193 242 | msgid "%s draft updated." 243 | msgstr "%s concept bijgewerkt." 244 | -------------------------------------------------------------------------------- /languages/types-de_DE.po: -------------------------------------------------------------------------------- 1 | # 2 | msgid "" 3 | msgstr "" 4 | "Project-Id-Version: \n" 5 | "Report-Msgid-Bugs-To: \n" 6 | "POT-Creation-Date: 2025-04-23T15:32:16+02:00\n" 7 | "PO-Revision-Date: 2025-04-23 15:32+0200\n" 8 | "Last-Translator: Lukas Gächter \n" 9 | "Language-Team: \n" 10 | "Language: de\n" 11 | "MIME-Version: 1.0\n" 12 | "Content-Type: text/plain; charset=UTF-8\n" 13 | "Content-Transfer-Encoding: 8bit\n" 14 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 15 | "X-Generator: Poedit 3.6\n" 16 | "X-Domain: mind/types\n" 17 | 18 | #: lib/Post_Type_Columns.php:37 19 | msgid "Featured Image" 20 | msgstr "Beitragsbild" 21 | 22 | #: lib/Post_Type_Labels.php:77 23 | msgid "Add New" 24 | msgstr "Hinzufügen" 25 | 26 | #. translators: %s: Singular post type name 27 | #. translators: %s: Singular taxonomy name 28 | #: lib/Post_Type_Labels.php:80 lib/Taxonomy_Labels.php:87 29 | msgid "Add New %s" 30 | msgstr "%s hinzufügen" 31 | 32 | #. translators: %s: Plural post type name 33 | #. translators: %s: Plural taxonomy name 34 | #: lib/Post_Type_Labels.php:85 lib/Taxonomy_Labels.php:91 35 | msgid "All %s" 36 | msgstr "Alle %s" 37 | 38 | #. translators: %s: Singular post type name 39 | #: lib/Post_Type_Labels.php:90 lib/Taxonomy_Labels.php:93 40 | msgid "%s Archives" 41 | msgstr "Archive für %s" 42 | 43 | #. translators: %s: Singular post type name 44 | #: lib/Post_Type_Labels.php:95 45 | msgid "%s Attributes" 46 | msgstr "Attribute für %s" 47 | 48 | #. translators: %s: Singular post type name 49 | #. translators: %s: Singular taxonomy name 50 | #: lib/Post_Type_Labels.php:100 lib/Taxonomy_Labels.php:99 51 | msgid "Edit %s" 52 | msgstr "%s bearbeiten" 53 | 54 | #. translators: %s: Singular post type name 55 | #: lib/Post_Type_Labels.php:105 56 | msgid "Featured Image for %s" 57 | msgstr "Beitragsbild für %s" 58 | 59 | #. translators: %s: Plural post type name 60 | #: lib/Post_Type_Labels.php:110 61 | msgid "Filter %s list" 62 | msgstr "% filtern" 63 | 64 | #. translators: %s: Singular post type name 65 | #: lib/Post_Type_Labels.php:115 66 | msgid "Insert into %s" 67 | msgstr "In %s einfügen" 68 | 69 | #. translators: %s: Plural post type name 70 | #. translators: %s: Plural taxonomy name 71 | #: lib/Post_Type_Labels.php:120 lib/Taxonomy_Labels.php:101 72 | msgid "%s list" 73 | msgstr "%s" 74 | 75 | #. translators: %s: Plural post type name 76 | #. translators: %s: Plural taxonomy name 77 | #: lib/Post_Type_Labels.php:125 lib/Taxonomy_Labels.php:103 78 | msgid "%s list navigation" 79 | msgstr "Navigation für %s" 80 | 81 | #. translators: %s: Singular post type name 82 | #: lib/Post_Type_Labels.php:130 lib/Post_Type_Labels.php:251 83 | msgid "%s published." 84 | msgstr "%s veröffentlicht." 85 | 86 | #. translators: %s: Singular post type name 87 | #: lib/Post_Type_Labels.php:135 88 | msgid "%s published privately." 89 | msgstr "%s privat veröffentlicht." 90 | 91 | #. translators: %s: Singular post type name 92 | #: lib/Post_Type_Labels.php:140 93 | msgid "%s reverted to draft." 94 | msgstr "%s auf Entwurf zurückgesetzt." 95 | 96 | #. translators: %s: Singular post type name 97 | #: lib/Post_Type_Labels.php:145 98 | msgid "%s trashed." 99 | msgstr "%s in den Papierkorb verschoben." 100 | 101 | #. translators: %s: Singular post type name 102 | #: lib/Post_Type_Labels.php:150 103 | msgid "%s scheduled." 104 | msgstr "%s geplant." 105 | 106 | #. translators: %s: Singular post type name 107 | #. translators: %s: Singular taxonomy name 108 | #: lib/Post_Type_Labels.php:155 lib/Post_Type_Labels.php:247 109 | #: lib/Post_Type_Labels.php:249 lib/Taxonomy_Labels.php:148 110 | msgid "%s updated." 111 | msgstr "%s aktualisiert." 112 | 113 | #. translators: %s: Singular post type name 114 | #: lib/Post_Type_Labels.php:160 115 | msgid "New %s" 116 | msgstr "%s hinzufügen" 117 | 118 | #. translators: %s: Plural post type name 119 | #. translators: %s: Plural taxonomy name 120 | #: lib/Post_Type_Labels.php:165 lib/Taxonomy_Labels.php:109 121 | msgid "No %s found." 122 | msgstr "Keine %s gefunden." 123 | 124 | #. translators: %s: Plural post type name 125 | #: lib/Post_Type_Labels.php:170 126 | msgid "No %s found in trash." 127 | msgstr "Keine %s im Papierkorb gefunden." 128 | 129 | #. translators: %s: Singular post type name 130 | #. translators: %s: Singular taxonomy name 131 | #: lib/Post_Type_Labels.php:175 lib/Taxonomy_Labels.php:111 132 | msgid "Parent %s" 133 | msgstr "Eltern-%s" 134 | 135 | #. translators: %s: Singular post type name 136 | #. translators: %s: Singular taxonomy name 137 | #: lib/Post_Type_Labels.php:180 lib/Taxonomy_Labels.php:113 138 | msgid "Parent %s:" 139 | msgstr "Eltern-%s:" 140 | 141 | #. translators: %s: Plural post type name 142 | #. translators: %s: Plural taxonomy name 143 | #: lib/Post_Type_Labels.php:185 lib/Taxonomy_Labels.php:117 144 | msgid "Search %s" 145 | msgstr "%s suchen" 146 | 147 | #. translators: %s: Singular post type name 148 | #: lib/Post_Type_Labels.php:190 149 | msgid "Uploaded to this %s" 150 | msgstr "Zu diesem %s hochgeladen" 151 | 152 | #. translators: %s: Singular post type name 153 | #. translators: %s: Plural post type name 154 | #. translators: %s: Singular taxonomy name 155 | #: lib/Post_Type_Labels.php:195 lib/Post_Type_Labels.php:200 156 | #: lib/Post_Type_Labels.php:237 lib/Taxonomy_Labels.php:123 157 | msgid "View %s" 158 | msgstr "%s anschauen" 159 | 160 | #. translators: %s: Singular post type name 161 | #: lib/Post_Type_Labels.php:228 162 | msgid "Preview %s" 163 | msgstr "%s-Vorschau ansehen" 164 | 165 | #. translators: %s: Singular post type name 166 | #: lib/Post_Type_Labels.php:253 167 | msgid "%s saved." 168 | msgstr "%s gespeichert." 169 | 170 | #. translators: %s: Singular post type name 171 | #: lib/Post_Type_Labels.php:255 172 | msgid "%s submitted." 173 | msgstr "%s übertragen." 174 | 175 | #. translators: %s: Singular post type name 176 | #: lib/Post_Type_Labels.php:257 177 | msgid "%s draft updated." 178 | msgstr "%s-Entwurf aktualisiert." 179 | 180 | #. translators: Plural name of the post type 181 | #: lib/Post_Type_Page.php:233 182 | msgid "Edit page for %s" 183 | msgstr "Seite für %s bearbeiten" 184 | 185 | #. translators: Post type label. 186 | #: lib/Post_Type_Page_Option.php:86 lib/Post_Type_Page_State.php:60 187 | msgid "Page for %s" 188 | msgstr "Seite für %s" 189 | 190 | #. translators: %s: Plural taxonomy name 191 | #: lib/Taxonomy_Labels.php:89 192 | msgid "Add or remove %s" 193 | msgstr "%s hinzufügen oder entfernen" 194 | 195 | #. translators: %s: Plural taxonomy name 196 | #: lib/Taxonomy_Labels.php:95 197 | msgid "← Back to %s" 198 | msgstr "← Zurück zu %s" 199 | 200 | #. translators: %s: Plural taxonomy name 201 | #: lib/Taxonomy_Labels.php:97 202 | msgid "Choose from the most used %s" 203 | msgstr "Wähle aus den am häufigsten verwendeten %s" 204 | 205 | #. translators: %s: Singular taxonomy name 206 | #: lib/Taxonomy_Labels.php:105 207 | msgid "New %s Name" 208 | msgstr "Neuer Name für %s" 209 | 210 | #. translators: %s: Plural taxonomy name 211 | #: lib/Taxonomy_Labels.php:107 212 | msgid "No %s" 213 | msgstr "Keine %s" 214 | 215 | #. translators: %s: Plural taxonomy name 216 | #: lib/Taxonomy_Labels.php:115 217 | msgid "Popular %s" 218 | msgstr "Beliebte %s" 219 | 220 | #. translators: %s: Plural taxonomy name 221 | #: lib/Taxonomy_Labels.php:119 222 | msgid "Separate %s with commas" 223 | msgstr "%s mit Komma trennen" 224 | 225 | #. translators: %s: Singular taxonomy name 226 | #: lib/Taxonomy_Labels.php:121 227 | msgid "Update %s" 228 | msgstr "%s aktualisieren" 229 | 230 | #. translators: %s: Singular taxonomy name 231 | #: lib/Taxonomy_Labels.php:144 232 | msgid "%s added." 233 | msgstr "%s hinzugefügt." 234 | 235 | #. translators: %s: Singular taxonomy name 236 | #. translators: %s: Plural taxonomy name 237 | #: lib/Taxonomy_Labels.php:146 lib/Taxonomy_Labels.php:154 238 | msgid "%s deleted." 239 | msgstr "%s gelöscht." 240 | 241 | #. translators: %s: Singular taxonomy name 242 | #: lib/Taxonomy_Labels.php:150 243 | msgid "%s not added." 244 | msgstr "%s nicht hinzugefügt." 245 | 246 | #. translators: %s: Singular taxonomy name 247 | #: lib/Taxonomy_Labels.php:152 248 | msgid "%s not updated." 249 | msgstr "%s nicht aktualisiert." 250 | 251 | #~ msgid "%s page" 252 | #~ msgstr "%s-Seite" 253 | 254 | #, fuzzy 255 | #~| msgid "All %s" 256 | #~ msgid "All %" 257 | #~ msgstr "Alle %s" 258 | 259 | #~ msgid "View %" 260 | #~ msgstr "%s ansehen" 261 | -------------------------------------------------------------------------------- /lib/Post_Type_Labels.php: -------------------------------------------------------------------------------- 1 | post_type = $post_type; 45 | $this->name_singular = $name_singular; 46 | $this->name_plural = $name_plural; 47 | } 48 | 49 | /** 50 | * Inits hooks. 51 | */ 52 | public function init() { 53 | add_filter( "post_type_labels_{$this->post_type}", function() { 54 | return (object) $this->get_labels(); 55 | } ); 56 | 57 | if ( is_admin() ) { 58 | // Update messages in backend. 59 | add_filter( 'post_updated_messages', [ $this, 'add_post_updated_messages' ] ); 60 | } 61 | } 62 | 63 | /** 64 | * Gets labels for a post type based on singular and plural name. 65 | * 66 | * The following labels are not translated, because they don’t contain the post type name: 67 | * set_feature_image, remove_featured_image 68 | * 69 | * @link https://developer.wordpress.org/reference/functions/get_post_type_labels/ 70 | * 71 | * @return array The translated labels. 72 | */ 73 | public function get_labels() { 74 | return [ 75 | 'name' => $this->name_plural, 76 | 'singular_name' => $this->name_singular, 77 | 'add_new' => __( 'Add New', 'mind/types' ), 78 | 'add_new_item' => sprintf( 79 | /* translators: %s: Singular post type name */ 80 | __( 'Add New %s', 'mind/types' ), 81 | $this->name_singular 82 | ), 83 | 'all_items' => sprintf( 84 | /* translators: %s: Plural post type name */ 85 | __( 'All %s', 'mind/types' ), 86 | $this->name_plural 87 | ), 88 | 'archives' => sprintf( 89 | /* translators: %s: Singular post type name */ 90 | __( '%s Archives', 'mind/types' ), 91 | $this->name_singular 92 | ), 93 | 'attributes' => sprintf( 94 | /* translators: %s: Singular post type name */ 95 | __( '%s Attributes', 'mind/types' ), 96 | $this->name_singular 97 | ), 98 | 'edit_item' => sprintf( 99 | /* translators: %s: Singular post type name */ 100 | __( 'Edit %s', 'mind/types' ), 101 | $this->name_singular 102 | ), 103 | 'featured_image' => sprintf( 104 | /* translators: %s: Singular post type name */ 105 | __( 'Featured Image for %s', 'mind/types' ), 106 | $this->name_singular 107 | ), 108 | 'filter_items_list' => sprintf( 109 | /* translators: %s: Plural post type name */ 110 | __( 'Filter %s list', 'mind/types' ), 111 | $this->name_plural 112 | ), 113 | 'insert_into_item' => sprintf( 114 | /* translators: %s: Singular post type name */ 115 | __( 'Insert into %s', 'mind/types' ), 116 | $this->name_singular 117 | ), 118 | 'items_list' => sprintf( 119 | /* translators: %s: Plural post type name */ 120 | __( '%s list', 'mind/types' ), 121 | $this->name_plural 122 | ), 123 | 'items_list_navigation' => sprintf( 124 | /* translators: %s: Plural post type name */ 125 | __( '%s list navigation', 'mind/types' ), 126 | $this->name_plural 127 | ), 128 | 'item_published' => sprintf( 129 | /* translators: %s: Singular post type name */ 130 | __( '%s published.', 'mind/types' ), 131 | $this->name_singular 132 | ), 133 | 'item_published_privately' => sprintf( 134 | /* translators: %s: Singular post type name */ 135 | __( '%s published privately.', 'mind/types' ), 136 | $this->name_singular 137 | ), 138 | 'item_reverted_to_draft' => sprintf( 139 | /* translators: %s: Singular post type name */ 140 | __( '%s reverted to draft.', 'mind/types' ), 141 | $this->name_singular 142 | ), 143 | 'item_trashed' => sprintf( 144 | /* translators: %s: Singular post type name */ 145 | __( '%s trashed.', 'mind/types' ), 146 | $this->name_singular 147 | ), 148 | 'item_scheduled' => sprintf( 149 | /* translators: %s: Singular post type name */ 150 | __( '%s scheduled.', 'mind/types' ), 151 | $this->name_singular 152 | ), 153 | 'item_updated' => sprintf( 154 | /* translators: %s: Singular post type name */ 155 | __( '%s updated.', 'mind/types' ), 156 | $this->name_singular 157 | ), 158 | 'new_item' => sprintf( 159 | /* translators: %s: Singular post type name */ 160 | __( 'New %s', 'mind/types' ), 161 | $this->name_singular 162 | ), 163 | 'not_found' => sprintf( 164 | /* translators: %s: Plural post type name */ 165 | __( 'No %s found.', 'mind/types' ), 166 | $this->name_plural 167 | ), 168 | 'not_found_in_trash' => sprintf( 169 | /* translators: %s: Plural post type name */ 170 | __( 'No %s found in trash.', 'mind/types' ), 171 | $this->name_plural 172 | ), 173 | 'parent_item' => sprintf( 174 | /* translators: %s: Singular post type name */ 175 | __( 'Parent %s', 'mind/types' ), 176 | $this->name_singular 177 | ), 178 | 'parent_item_colon' => sprintf( 179 | /* translators: %s: Singular post type name */ 180 | __( 'Parent %s:', 'mind/types' ), 181 | $this->name_singular 182 | ), 183 | 'search_items' => sprintf( 184 | /* translators: %s: Plural post type name */ 185 | __( 'Search %s', 'mind/types' ), 186 | $this->name_plural 187 | ), 188 | 'uploaded_to_this_item' => sprintf( 189 | /* translators: %s: Singular post type name */ 190 | __( 'Uploaded to this %s', 'mind/types' ), 191 | $this->name_singular 192 | ), 193 | 'view_item' => sprintf( 194 | /* translators: %s: Singular post type name */ 195 | __( 'View %s', 'mind/types' ), 196 | $this->name_singular 197 | ), 198 | 'view_items' => sprintf( 199 | /* translators: %s: Plural post type name */ 200 | __( 'View %s', 'mind/types' ), 201 | $this->name_plural 202 | ), 203 | 'menu_name' => $this->name_plural, 204 | 'name_admin_bar' => $this->name_singular, 205 | ]; 206 | } 207 | 208 | /** 209 | * Sets post updated messages for custom post types. 210 | * 211 | * Check out the `post_updated_messages` in wp-admin/edit-form-advanced.php. 212 | * 213 | * @param array $messages An associative array of post types and their messages. 214 | * 215 | * @return array The filtered messages. 216 | */ 217 | public function add_post_updated_messages( $messages ) { 218 | if (!post_type_exists($this->post_type)) { 219 | return $messages; 220 | } 221 | 222 | global $post_id; 223 | 224 | $preview_url = get_preview_post_link( $post_id ); 225 | $permalink = get_permalink( $post_id ); 226 | 227 | // Preview post link. 228 | $preview_post_link_html = is_post_type_viewable( $this->post_type ) 229 | ? sprintf( ' %2$s', 230 | esc_url( $preview_url ), 231 | /* translators: %s: Singular post type name */ 232 | sprintf( __( 'Preview %s', 'mind/types' ), $this->name_singular ) 233 | ) 234 | : ''; 235 | 236 | // View post link. 237 | $view_post_link_html = is_post_type_viewable( $this->post_type ) 238 | ? sprintf( ' %2$s', 239 | esc_url( $permalink ), 240 | /* translators: %s: Singular post type name */ 241 | sprintf( __( 'View %s', 'mind/types' ), $this->name_singular ) 242 | ) 243 | : ''; 244 | 245 | /** 246 | * Message indices 2, 3, 5 and 9 are not handled, because they are edge cases or they would be too difficult 247 | * to reproduce. 248 | */ 249 | $messages[ $this->post_type ] = [ 250 | /* translators: %s: Singular post type name */ 251 | 1 => sprintf( __( '%s updated.', 'mind/types' ), $this->name_singular ) . $view_post_link_html, 252 | /* translators: %s: Singular post type name */ 253 | 4 => sprintf( __( '%s updated.', 'mind/types' ), $this->name_singular ), 254 | /* translators: %s: Singular post type name */ 255 | 6 => sprintf( __( '%s published.', 'mind/types' ), $this->name_singular ) . $view_post_link_html, 256 | /* translators: %s: Singular post type name */ 257 | 7 => sprintf( __( '%s saved.', 'mind/types' ), $this->name_singular ), 258 | /* translators: %s: Singular post type name */ 259 | 8 => sprintf( __( '%s submitted.', 'mind/types' ), $this->name_singular ) . $preview_post_link_html, 260 | /* translators: %s: Singular post type name */ 261 | 10 => sprintf( __( '%s draft updated.', 'mind/types' ), $this->name_singular ) . $preview_post_link_html, 262 | ]; 263 | 264 | return $messages; 265 | } 266 | } 267 | -------------------------------------------------------------------------------- /lib/Post_Type_Columns.php: -------------------------------------------------------------------------------- 1 | post_type = $post_type; 31 | 32 | foreach ( $columns as $slug => $column ) { 33 | if ( false !== $column ) { 34 | // Set defaults for thumbnail. 35 | if ( 'thumbnail' === $slug ) { 36 | $column = wp_parse_args( $column, [ 37 | 'title' => __( 'Featured Image', 'mind/types' ), 38 | 'width' => 80, 39 | 'height' => 80, 40 | 'sortable' => false, 41 | ] ); 42 | } 43 | 44 | // Set defaults for each field. 45 | $column = wp_parse_args( $column, [ 46 | 'title' => '', 47 | 'type' => 'meta', 48 | 'transform' => null, 49 | 'sortable' => false, 50 | 'orderby' => 'meta_value', 51 | 'column_order' => 10, 52 | 'searchable' => false, 53 | ] ); 54 | } 55 | 56 | $this->columns[ $slug ] = $column; 57 | } 58 | } 59 | 60 | /** 61 | * Inits hooks. 62 | */ 63 | public function init() { 64 | add_filter( 'manage_edit-' . $this->post_type . '_columns', [ $this, 'columns' ] ); 65 | add_filter( 'manage_edit-' . $this->post_type . '_sortable_columns', [ 66 | $this, 67 | 'columns_sortable', 68 | ] ); 69 | add_action( 'manage_' . $this->post_type . '_posts_custom_column', [ 70 | $this, 71 | 'column_content', 72 | ], 10, 2 ); 73 | 74 | if ( is_admin() ) { 75 | add_action( 'pre_get_posts', [ $this, 'search_meta_or_title' ] ); 76 | add_action( 'pre_get_posts', [ $this, 'sort_by_meta' ] ); 77 | } 78 | } 79 | 80 | /** 81 | * Filters columns for post list view. 82 | * 83 | * @param array $columns An array of existing columns. 84 | * 85 | * @return array Filtered array. 86 | */ 87 | public function columns( $columns ) { 88 | $sorted = []; 89 | $return = []; 90 | 91 | // Move existing columns into sort array. 92 | foreach ( $columns as $key => $column ) { 93 | $sorted[ $key ] = [ 94 | 'title' => $column, 95 | // The checkbox should always be first, hence the order key '-1'. 96 | 'column_order' => 'cb' === $key ? - 1 : 10, 97 | ]; 98 | } 99 | 100 | // Merge in user-defined columns and settings for existing columns. 101 | foreach ( $this->columns as $key => $column ) { 102 | if ( isset( $sorted[ $key ] ) ) { 103 | // Columns can be removed when they are set to 'false' 104 | if ( false === $column ) { 105 | unset( $sorted[ $key ] ); 106 | continue; 107 | } 108 | 109 | if ( ! empty( $column['title'] ) ) { 110 | $sorted[ $key ]['title'] = $column['title']; 111 | } 112 | 113 | if ( 10 !== $column['column_order'] ) { 114 | $sorted[ $key ]['column_order'] = $column['column_order']; 115 | } 116 | } else { 117 | $sorted[ $key ] = $column; 118 | } 119 | } 120 | 121 | $sorted = wp_list_sort( $sorted, 'column_order', 'ASC', true ); 122 | 123 | foreach ( $sorted as $slug => $column ) { 124 | $return[ $slug ] = $column['title']; 125 | } 126 | 127 | return $return; 128 | } 129 | 130 | /** 131 | * Filters sortable columns. 132 | * 133 | * @param array $columns An array of existing columns. 134 | * 135 | * @return array Filtered array. 136 | */ 137 | public function columns_sortable( $columns ) { 138 | foreach ( $this->columns as $slug => $column ) { 139 | // Remove column when it’s not sortable. 140 | if ( isset( $column['sortable'] ) && ! $column['sortable'] ) { 141 | unset( $columns[ $slug ] ); 142 | continue; 143 | } elseif ( ! isset( $columns[ $slug ] ) ) { 144 | if ( ! empty( $column['type'] ) && 'meta' === $column['type'] ) { 145 | $columns[ $slug ] = $slug; 146 | } 147 | } 148 | } 149 | 150 | return $columns; 151 | } 152 | 153 | /** 154 | * Includes searchable custom fields in the search. 155 | * 156 | * @param \WP_Query $query WordPress query object. 157 | */ 158 | public function sort_by_meta( \WP_Query $query ) { 159 | global $typenow; 160 | $orderby = $query->get( 'orderby' ); 161 | 162 | if ( ! $query->is_main_query() 163 | || $typenow !== $this->post_type 164 | || empty( $orderby ) 165 | || ! is_string( $orderby ) 166 | || ! isset( $this->columns[ $orderby ] ) 167 | || 'meta' !== $this->columns[ $orderby ]['type'] 168 | ) { 169 | return; 170 | } 171 | 172 | $new_orderby = $this->columns[ $orderby ]['orderby']; 173 | 174 | $query->set( 'orderby', $new_orderby ); 175 | $query->set( 'meta_key', $orderby ); 176 | } 177 | 178 | /** 179 | * Update column contents for post list view. 180 | * 181 | * @param string $column_name The column slug. 182 | * @param int $post_id The post ID. 183 | */ 184 | public function column_content( $column_name, $post_id ) { 185 | // Bail out. 186 | if ( ! array_key_exists( $column_name, $this->columns ) ) { 187 | return; 188 | } 189 | 190 | $column = $this->columns[ $column_name ]; 191 | 192 | if ( 'thumbnail' === $column_name ) { 193 | $src = get_the_post_thumbnail_url( $post_id, 'thumbnail' ); 194 | 195 | if ( empty( $src ) ) { 196 | return; 197 | } 198 | 199 | $styles = ''; 200 | 201 | foreach ( [ 'width', 'height' ] as $attr ) { 202 | if ( isset( $column[ $attr ] ) ) { 203 | $styles .= $attr . ':' . $column[ $attr ] . 'px;'; 204 | } 205 | } 206 | 207 | if ( ! empty( $styles ) ) { 208 | $styles = ' style="' . $styles . '"'; 209 | } 210 | 211 | echo ''; 212 | 213 | return; 214 | } 215 | 216 | $value = ''; 217 | if ( 'acf' === $column['type'] ) { 218 | $value = get_field( $column_name, $post_id ); 219 | } elseif ( 'meta' === $column['type'] ) { 220 | $value = get_post_meta( $post_id, $column_name, true ); 221 | } elseif ( 'image' === $column['type'] ) { 222 | $value = get_post_meta( $post_id, $column_name, true ); 223 | 224 | if ( is_numeric( $value ) ) { 225 | $src = wp_get_attachment_image_src( 226 | $value, 227 | $column['image_size'] ?? 'thumbnail' 228 | ); 229 | 230 | if ( $src ) { 231 | echo $this->column_content_image( $src[0], $column ); 232 | 233 | return; 234 | } 235 | } 236 | } elseif ( 'custom' === $column['type'] && is_callable( $column['value'] ) ) { 237 | $value = call_user_func( $column['value'], $post_id ); 238 | } 239 | 240 | if ( in_array( $column['type'], [ 'meta', 'acf' ], true ) 241 | && is_callable( $column['transform'] ) 242 | ) { 243 | $value = call_user_func( $column['transform'], $value, $post_id ); 244 | } 245 | 246 | echo $value; 247 | } 248 | 249 | /** 250 | * Gets the HTML for the 'image' type. 251 | * 252 | * @param string $src An image source. 253 | * @param array $args An array of column arguments. 254 | * 255 | * @return string 256 | */ 257 | protected function column_content_image( $src, $args ) { 258 | if ( empty( $src ) ) { 259 | return ''; 260 | } 261 | 262 | $styles = 'max-width: 100%;'; 263 | 264 | foreach ( [ 'width', 'height' ] as $attr ) { 265 | if ( isset( $args[ $attr ] ) ) { 266 | $styles .= $attr . ':' . esc_attr( $args[ $attr ] ) . 'px;'; 267 | } 268 | } 269 | 270 | $styles = ' style="' . $styles . '"'; 271 | 272 | return ''; 273 | } 274 | 275 | /** 276 | * Includes searchable custom fields in the search. 277 | * 278 | * @param \WP_Query $query WordPress query object. 279 | */ 280 | public function search_meta_or_title( \WP_Query $query ) { 281 | global $typenow; 282 | $searchterm = $query->query_vars['s']; 283 | 284 | if ( 285 | ! $query->is_main_query() 286 | || $typenow !== $this->post_type 287 | || empty( $searchterm ) 288 | ) { 289 | return; 290 | } 291 | 292 | $meta_columns = array_filter( $this->columns, function( $column ) { 293 | return ! empty( $column ) && 'meta' === $column['type'] && $column['searchable']; 294 | } ); 295 | 296 | // Bail out and use default search if no searchable meta columns are defined. 297 | if ( empty( $meta_columns ) ) { 298 | return; 299 | } 300 | 301 | $meta_query = [ 'relation' => 'OR' ]; 302 | 303 | foreach ( $meta_columns as $key => $column ) { 304 | $meta_query[] = [ 305 | 'key' => $key, 306 | 'value' => $searchterm, 307 | 'compare' => 'LIKE', 308 | ]; 309 | } 310 | 311 | $query->set( 'meta_query', $meta_query ); 312 | 313 | /** 314 | * Remove search parameter. 315 | * 316 | * The search parameter needs to be removed from the query, otherwise posts can’t be found. 317 | * A disadvantage of this is that all the logic in \WP_Query::parse_search() is lost. 318 | * 319 | * @see \WP_Query::parse_search() 320 | */ 321 | $query->set( 's', '' ); 322 | 323 | // Fixes the "Search results for …" label in the post list table. 324 | add_filter( 'get_search_query', function( $query ) use ( $searchterm ) { 325 | return $searchterm; 326 | } ); 327 | 328 | /** 329 | * Update meta query to include search for title. 330 | * 331 | * This logic is taken from a StackOverflow answer: 332 | * 333 | * @link https://wordpress.stackexchange.com/a/178492/22506 334 | */ 335 | add_filter( 'get_meta_sql', function( $sql ) use ( $searchterm ) { 336 | global $wpdb; 337 | 338 | // Run only once. 339 | static $nr = 0; 340 | 341 | if ( 0 != $nr ++ ) { 342 | return $sql; 343 | } 344 | 345 | // Modify the WHERE part. 346 | $sql['where'] = sprintf( 347 | " AND ( %s OR %s ) ", 348 | $wpdb->prepare( "{$wpdb->posts}.post_title like '%%%s%%'", $searchterm ), 349 | mb_substr( $sql['where'], 5 ) 350 | ); 351 | 352 | return $sql; 353 | } ); 354 | } 355 | } 356 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Types 2 | 3 | Custom Post Types and Taxonomy helper classes for WordPress projects. 4 | 5 | - Register Custom Post Types and Taxonomies through an array notation. Labels will be set accordingly. Currently, English, German and Dutch are supported. 6 | - Change the query arguments for posts in the front- and backend via a list of arguments, e.g. to set a custom post order wit the [`query`](#query) option. 7 | - Change the admin columns for posts in the backend via a list of arguments. 8 | - Set a specific page as the archive page for a custom post type with the [`page_for_archive`](#page_for_archive) option. 9 | - Set post slugs dynamically when posts are saved. 10 | 11 | ## Table of Contents 12 | 13 | 14 | 15 | - [Table of Contents](#table-of-contents) 16 | - [Installation](#installation) 17 | - [Register post types](#register-post-types) 18 | - [query](#query) 19 | - [admin_columns](#admin_columns) 20 | - [Type](#type) 21 | - [The `meta` and `acf` types](#the-meta-and-acf-types) 22 | - [The `thumbnail` type](#the-thumbnail-type) 23 | - [The `image` type](#the-image-type) 24 | - [Existing columns and column order](#existing-columns-and-column-order) 25 | - [page_for_archive](#page_for_archive) 26 | - [is_singular_public](#is_singular_public) 27 | - [customizer_section](#customizer_section) 28 | - [show_post_state](#show_post_state) 29 | - [Use page in template](#use-page-in-template) 30 | - [Update existing post types](#update-existing-post-types) 31 | - [Change settings for a post type](#change-settings-for-a-post-type) 32 | - [Change post type support](#change-post-type-support) 33 | - [Rename a post type](#rename-a-post-type) 34 | - [Change admin column settings for existing post type](#change-admin-column-settings-for-existing-post-type) 35 | - [Register taxonomies](#register-taxonomies) 36 | - [Update existing taxonomies](#update-existing-taxonomies) 37 | - [Change settings for a taxonomy](#change-settings-for-a-taxonomy) 38 | - [Rename a taxonomy](#rename-a-taxonomy) 39 | - [Unregister taxonomies](#unregister-taxonomies) 40 | - [Customize a post slug](#customize-a-post-slug) 41 | - [Support](#support) 42 | 43 | 44 | 45 | ## Installation 46 | 47 | You can install the package via Composer: 48 | 49 | ```bash 50 | composer require mindkomm/types 51 | ``` 52 | 53 | ## Register post types 54 | 55 | ```php 56 | [ 67 | 'name_singular' => 'Example', 68 | 'name_plural' => 'Examples', 69 | 'args' => [ 70 | /** 71 | * For a list of possible menu-icons see 72 | * https://developer.wordpress.org/resource/dashicons/ 73 | */ 74 | 'menu_icon' => 'dashicons-building', 75 | 'hierarchical' => false, 76 | 'has_archive' => false, 77 | 'supports' => [ 78 | 'title', 79 | 'editor', 80 | ], 81 | // Whether post is accessible in the frontend 82 | 'public' => false, 83 | ], 84 | 'admin_columns' => [ 85 | 'date' => false, 86 | ], 87 | ], 88 | ] ); 89 | } ); 90 | ``` 91 | 92 | The `args` parameter is used for the arguments that are passed to `register_post_type`. The `name_singular` and `name_plural` parameters are used for the generating the labels in the backend. 93 | 94 | You can use more options: 95 | 96 | ### query 97 | 98 | Arguments that are used for querying this post type in the back- and frontend. You can use this to define the sort order. Here’s an example for a post type `event`, where we want to order the posts by the value of a custom field named `date_start`. 99 | 100 | ```php 101 | 'query' => [ 102 | 'meta_key' => 'date_start', 103 | 'orderby' => 'meta_value_num', 104 | 'order' => 'DESC', 105 | ], 106 | ``` 107 | 108 | If you want to have different queries for the front- and the backend, you can use separate `frontend` and `backend` keys: 109 | 110 | ```php 111 | 'query' => [ 112 | 'frontend' => [ 113 | 'meta_key' => 'date_start', 114 | 'orderby' => 'meta_value_num', 115 | 'order' => 'ASC', 116 | ], 117 | 'backend' => [ 118 | 'meta_key' => 'date_start', 119 | 'orderby' => 'meta_value_num', 120 | 'order' => 'DESC', 121 | ], 122 | ], 123 | ``` 124 | 125 | If you only use one key and omit the other, then the query will only be applied to your choice. 126 | 127 | ### admin_columns 128 | 129 | Arguments that are used to add and remove admin columns in the backend. Pass an associative array of column names with arguments. 130 | 131 | Here’s an example for a Custom Post Type `event`. 132 | 133 | ```php 134 | 'admin_columns' => [ 135 | 'date' => false, 136 | 'date_start' => [ 137 | 'title' => 'Start Date', 138 | 'transform' => function( $value ) { 139 | return date_i18n( 140 | get_option( 'date_format' ), 141 | DateTime::createFromFormat( 'Ymd', $value )->getTimeStamp() 142 | ); 143 | }, 144 | ], 145 | 'location' => [ 146 | 'title' => 'Location', 147 | 'sortable' => true, 148 | 'searchable' => true, 149 | 'transform' => function( $post_id ) { 150 | return get_the_title( $post_id ); 151 | }, 152 | ], 153 | 'width' => [ 154 | 'title' => 'Width', 155 | 'sortable' => true, 156 | 'orderby' => 'meta_value_num', 157 | ], 158 | ], 159 | ``` 160 | 161 | These are the possible arguments: 162 | 163 | - **title** – *(string)* The title to use for the column. Default empty. 164 | - **transform** – *(callable)* The function to use on the value that is displayed. The function defined here will get a `$value` parameter that you can transform. E.g., if you have a post ID saved in post meta, you could display the post’s title. Default `null`. 165 | - **type** – *(string)* The type for the column. One of `meta`, `acf`, `thumbnail`, `image` or `custom`. Default `meta`. 166 | - **sortable** – *(bool)* Whether the column is sortable. Default `false`. 167 | - **orderby** – *(string)* What to order by when the `sortable` argument is used. You don’t need to provide a `meta_key` parameter, because it is automatically set. Default `meta_value`. 168 | - **searchable** – *(bool)* Whether the column is searchable. Will include the meta values when searching the post list. Only applied if using the default type `meta`. Default `false`. 169 | - **column_order** – *(int)* An order number to sort by. You can use this to change the order of your columns. Default `10`. 170 | 171 | If you need more possibilities for defining admin columns you could use the fantastic [Admin Columns](https://www.admincolumns.com/) plugin. 172 | 173 | #### Type 174 | 175 | The `type` argument defines how your column is interpreted. The following types exist: 176 | 177 | - `meta` 178 | - `acf` 179 | - `thumbnail` 180 | - `image` 181 | - `custom` 182 | 183 | #### The `meta` and `acf` types 184 | 185 | With the `meta` type, the column name is the name of the meta field you want to display. 186 | 187 | You can also use `acf` as a type you use Advanced Custom Fields and want to apply its filters to the value. 188 | 189 | #### The `thumbnail` type 190 | 191 | Use this key to display the featured image thumbnail for a post. 192 | 193 | ```php 194 | 'location' => [ 195 | 'thumbnail' => true, 196 | ], 197 | ``` 198 | 199 | You can also set the width and height. The defaults are `80` × `80` pixels. 200 | 201 | ```php 202 | 'location' => [ 203 | 'thumbnail' => [ 204 | 'width' => 100, 205 | 'height' => 100, 206 | ], 207 | ], 208 | ``` 209 | 210 | #### The `image` type 211 | 212 | The `type` allows you to display an image other than the featured image. This type will also use the key of the column to get an **attachment ID** from the post’s meta values. 213 | 214 | ```php 215 | 'admin_columns' => [ 216 | 'profile_image' => [ 217 | 'title' => 'Profile image', 218 | 'type' => 'image', 219 | ], 220 | ], 221 | ``` 222 | 223 | By default, it will display the `thumbnail` size. If you want to request a different size, use the `image_size` parameter. 224 | 225 | ```php 226 | 'admin_columns' => [ 227 | 'profile_image' => [ 228 | 'title' => 'Profile image', 229 | 'type' => 'image', 230 | 'image_size' => 'medium', 231 | ], 232 | ], 233 | ``` 234 | 235 | And if you want to restrict the width and the height of the image, you can provide pixel values for `width` and `height`. 236 | 237 | ```php 238 | 'admin_columns' => [ 239 | 'profile_image' => [ 240 | 'title' => 'Profile image', 241 | 'type' => 'image', 242 | 'width' => 100, 243 | 'height' => 100, 244 | ], 245 | ], 246 | ``` 247 | 248 | #### The `custom` type 249 | 250 | If you want to do something custom, you can use the `custom` type. If you use the `custom` type, you need to provide a `value`, which is a callback function that receives the post’s ID as a single parameter. 251 | 252 | In this example, we call a function that extracts an attribute value from a certain block of the post. 253 | 254 | ```php 255 | 'email' => [ 256 | 'title' => __( 'Email', 'theme-module-teammember' ), 257 | 'type' => 'custom', 258 | 'value' => function( $post_id ) { 259 | return $this->get_block_attribute( $post_id, 'email' ); 260 | }, 261 | ], 262 | ``` 263 | 264 | #### Existing columns and column order 265 | 266 | You can also update or hide existing columns. Existing columns can be updated with the `title` and the `column_order` argument. 267 | 268 | If you want to the change the title of an existing column, use the `title` attribute. 269 | 270 | ```php 271 | 'admin_columns' => [ 272 | 'title' => [ 273 | 'title' => 'Event title', 274 | ], 275 | ], 276 | ``` 277 | 278 | If you want to move the column, you can use the `column_order` argument. In this example, we would move the `date` column to the end. 279 | 280 | ```php 281 | 'admin_columns' => [ 282 | 'date' => [ 283 | 'column_order' => 100, 284 | ], 285 | ], 286 | ``` 287 | 288 | The default order is `10`. So if you wanted to move a column like the thumbnail to the start, you could use `5`. 289 | 290 | ```php 291 | 'admin_columns' => [ 292 | 'thumbnail' => [ 293 | 'column_order' => 5, 294 | ], 295 | ], 296 | ``` 297 | 298 | To hide an existing column, you can use `false`. 299 | 300 | ```php 301 | 'admin_columns' => [ 302 | 'date' => false, 303 | ], 304 | ``` 305 | 306 | ### page_for_archive 307 | 308 | The `page_for_archive` option allows you to set a specific page as the archive page for a custom post type: 309 | 310 | ```php 311 | 'event' => [ 312 | 'args' => [ 313 | 'public' => true, 314 | ], 315 | 'page_for_archive' => [ 316 | 'post_id' => get_option( 'page_for_event' ), 317 | 'is_singular_public' => false, 318 | ], 319 | ], 320 | ``` 321 | 322 | In this example, the ID for the page that’s saved in the `page_for_event` option will act as the archive page for the `event` post type. 323 | 324 | You need to **flush your permalinks** whenever you make changes to this option. 325 | 326 | Behind the curtains, Types uses the `has_archive` option when registering a post type and sets the slug of the page you passed in the `page_for_archive` option. 327 | 328 | #### is_singular_public 329 | 330 | The `is_singular_public` option allows you to set, whether singular templates for this post type should be accessible in the frontend. Singular template requests will then be redirected to the archive page. We can’t use the `public` or `publicly_queryable` option for this, because then the archive page wouldn’t work either. 331 | 332 | #### customizer_section 333 | 334 | If you want Types to register an option to select the page you want to use as your archive in the Customizer, you can use the `customizer_section` argument: 335 | 336 | ```php 337 | 'event' => [ 338 | 'page_for_archive' => [ 339 | 'post_id' => get_option( 'page_for_event' ), 340 | 'customizer_section' => 'event', 341 | ], 342 | ], 343 | ``` 344 | 345 | With `customizer_section`, you can define in which Customizer section the option should be displayed. This needs to be an existing section. This way, you can decide yourself whether you want to have a separate Customizer section for each custom post type, or whether you want to list all of your custom post type pages in the same section. 346 | 347 | #### show_post_state 348 | 349 | Types will display a post state in the pages overview for the page that you selected. If you want to disable this functionality, use the `show_post_state` option. 350 | 351 | ```php 352 | 'event' => [ 353 | 'page_for_archive' => [ 354 | 'post_id' => get_option( 'page_for_event' ), 355 | 'show_post_state' => false, 356 | ], 357 | ], 358 | ``` 359 | 360 | #### Use page in template 361 | 362 | To make use of that page, you will use it in your archive page where you can now use your page as the main post. 363 | 364 | **archive-event.php** 365 | 366 | ```php 367 | $post = get_post( get_option( 'page_for_event' ) ); 368 | ``` 369 | 370 | ## Update existing post types 371 | 372 | ### Change settings for a post type 373 | 374 | Use the `update()` function to change the settings for an existing post type. Here’s an example for changing the settings for posts to make them not directly accessible in the frontend. 375 | 376 | **functions.php** 377 | 378 | ```php 379 | Types\Post_Type::update( [ 380 | 'post' => [ 381 | 'args' => [ 382 | 'public' => false, 383 | 'show_ui' => true, 384 | 'show_in_nav_menus' => true, 385 | ], 386 | 'admin_columns' => [ 387 | 'date' => false, 388 | ], 389 | ], 390 | ] ); 391 | ``` 392 | 393 | The `update()` function accepts an associative array of post types and their arguments. Make sure you use this function before the `init` hook. 394 | 395 | ### Change post type support 396 | 397 | Please be aware that it’s not possible to change post type support features through the `update()` function. To remove support for an existing feature, you will have to use the `remove_post_type_support` function. 398 | 399 | ```php 400 | add_action( 'init', function() { 401 | remove_post_type_support( 'post', 'thumbnail' ); 402 | } ); 403 | ``` 404 | 405 | In the same manner, if you want to add features, you should do it through the `add_post_type_support` function: 406 | 407 | ```php 408 | add_post_type_support( 'page', 'excerpt' ); 409 | ``` 410 | 411 | ### Rename a post type 412 | 413 | Sometimes you might want to rename an existing post type to better reflect what it’s used for. 414 | 415 | **functions.php** 416 | 417 | ```php 418 | Types\Post_Type::rename( 'post', 'Beispiel', 'Beispiele' ); 419 | ``` 420 | 421 | The `rename()` function accepts a post type as the first parameter, the new singular name of the post type as a second parameter and the plural name of the post type as a third parameter. If you omit the third parameter, the second parameter will be used as the plural form instead. Make sure you use this function before the `init` hook. 422 | 423 | This is practically a shorthand function for: 424 | 425 | ```php 426 | Types\Post_Type::update( [ 427 | 'post' => [ 428 | 'name_singular' => 'Beispiel', 429 | 'name_plural' => 'Beispiele', 430 | ], 431 | ] ); 432 | ``` 433 | 434 | If you only want to rename one of the labels, e.g. the menu label, you can use the `post_type_labels_{$post_type}` filter. Here’s an example for changing the menu name for posts: 435 | 436 | ```php 437 | add_filter( 'post_type_labels_post', function( $labels ) { 438 | $labels->menu_name = 'Aktuelles'; 439 | 440 | return $labels; 441 | }, 11 ); 442 | ``` 443 | 444 | ## Change admin column settings for existing post type 445 | 446 | To change the admin column settings for existing post types like `post` or `page`, you can use the `admin_columns()` function, which accepts an associative array of post types and their admin column settings. 447 | 448 | Here’s an example that disables the date, comments and author columns and adds a thumbnail instead: 449 | 450 | ```php 451 | Types\Post_Type::admin_columns( [ 452 | 'page' => [ 453 | 'date' => false, 454 | 'comments' => false, 455 | 'author' => false, 456 | 'thumbnail' => [ 457 | 'width' => 80, 458 | 'height' => 80, 459 | ], 460 | ], 461 | ] ); 462 | ``` 463 | 464 | ## Register taxonomies 465 | 466 | ```php 467 | [ 480 | 'name_singular' => 'Example Category', 481 | 'name_plural' => 'Example Categories', 482 | // For which post types do you want to register this taxonomy? 483 | 'for_post_types' => [ 'example' ], 484 | 'args' => [ 485 | // Hide the meta box from the edit view 486 | // 'meta_box_cb' => false, 487 | // 488 | // Make it selectable in the navigation menus 489 | // 'show_in_nav_menus' => true, 490 | ], 491 | ], 492 | ] ); 493 | } ); 494 | ``` 495 | 496 | The `args` parameter is used for the arguments that are passed to `register_taxonomy`. Use the `for_post_types` parameter to assign taxonomies to certain post types. The `name_singular` and `name_plural` parameters are used for the generating the labels in the backend. 497 | 498 | ## Update existing taxonomies 499 | 500 | ### Change settings for a taxonomy 501 | 502 | Use the `update()` function to change the settings for an existing taxonomy. Here’s an example for changing the settings for categories to make them not directly accessible in the frontend. 503 | 504 | **functions.php** 505 | 506 | ```php 507 | 508 | Types\Taxonomy::update( [ 509 | 'category' => [ 510 | 'args' => [ 511 | 'public' => false, 512 | 'show_ui' => true, 513 | 'show_in_nav_menus' => true, 514 | ], 515 | ], 516 | ] ); 517 | ``` 518 | 519 | The `update()` function accepts an associative array of taxonomies and their arguments. Make sure you use this function before the `init` hook. 520 | 521 | ### Rename a taxonomy 522 | 523 | Sometimes you might want to rename an existing taxonomy to better reflect what it’s used for. 524 | 525 | **functions.php** 526 | 527 | ```php 528 | Types\Taxonomy::rename( 'category', 'Topic', 'Topics' ); 529 | ``` 530 | 531 | The `rename()` function accepts a taxonomy as the first parameter, the new singular name of the taxonomy as a second parameter and the plural name of the taxonomy as a third parameter. If you omit the third parameter, the second parameter will be used as the plural form instead. Make sure you use this function before the `init` hook. 532 | 533 | This is practically a shorthand function for: 534 | 535 | ```php 536 | Types\Taxonomy::update( [ 537 | 'category' => [ 538 | 'name_singular' => 'Topic', 539 | 'name_plural' => 'Topics, 540 | ], 541 | ] ); 542 | ``` 543 | 544 | If you only want to rename one of the labels, e.g. the menu label, you can use the `taxonomy_labels_{$post_type}` filter. Here’s an example for changing the menu name for posts: 545 | 546 | ```php 547 | add_filter( 'taxonomy_labels_category', function( $labels ) { 548 | $labels->menu_name = 'Topics'; 549 | 550 | return $labels; 551 | }, 11 ); 552 | ``` 553 | 554 | ### Unregister taxonomies 555 | 556 | If you want to unregister taxonomies for certain post types, it’s best to do it through the `unregister_taxonomy_for_object_type()` function. 557 | 558 | ```php 559 | /** 560 | * Unregister post categories and post tags. 561 | */ 562 | add_action( 'init', function() { 563 | unregister_taxonomy_for_object_type( 'category', 'post' ); 564 | unregister_taxonomy_for_object_type( 'post_tag', 'post' ); 565 | } ); 566 | ``` 567 | 568 | ## Customize a post slug 569 | 570 | Sometimes you want to overwrite the post slug when a post is saved. Possible use cases: 571 | 572 | - Common post duplication plugin often only add `-copy` or `-2` as a suffix to the post slug. You want to use your own pattern. 573 | - Multiple posts have the same name, but differ in the meta data they display. For example, event posts could have a different date. You want to add the date of the event to the post slug automatically. 574 | 575 | To initalize, you’ll need to create an instance of `Post_Slug` and call the `init()` function: 576 | 577 | ```php 578 | $post_slugs = new Types\Post_Slug(); 579 | $post_slugs->init(); 580 | ``` 581 | 582 | Here’s an example for a custom post slug for a post type `course`, where you want the permalink to be built from the post title. In this scenario, the post title would be a course number. 583 | 584 | ```php 585 | $post_slugs = new Types\Post_Slug(); 586 | $post_slugs->init(); 587 | 588 | $post_slugs->register( [ 589 | 'course' => function( $post_slug, $post_data, $post_id ) { 590 | return $post_data['post_title']; 591 | }, 592 | ] ); 593 | ``` 594 | 595 | You don’t have to use `sanitize_title` in the callback, because the class uses that function internally. 596 | 597 | Here’s another example for the event post mentioned earlier: 598 | 599 | ```php 600 | $post_slugs->register_suffix_date( [ 601 | 'event' => [ 602 | 'meta_key' => 'date_start', 603 | ], 604 | ] ); 605 | ``` 606 | 607 | The `register_suffix_date` function is a special function that makes it easier to append a date taken from a post’s meta data and append it to the slug. The function takes an associative array of post types and their args for the function: 608 | 609 | - **meta_key** – Used to define the name of the meta key that is used. Default `date_start`. 610 | - **input_format** – Defines the date format of the meta value. Default `Ymd`. 611 | - **output_format** – Defines the output date format that should be used for the suffix. Default `Y-m-d`. 612 | 613 | ## Support 614 | 615 | This is a library that we use at MIND to develop WordPress themes. You’re free to use it, but currently, we don’t provide any support. 616 | --------------------------------------------------------------------------------