├── .gitattributes ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── composer.json └── src └── CPT.php /.gitattributes: -------------------------------------------------------------------------------- 1 | /examples export-ignore 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | composer.phar 3 | composer.lock 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ##### v1.4 4 | * fix error with taxonomy arrays when generating columns 5 | * run `custom_populate_columns` callbacks using `call_user_func_array()` 6 | * add post updated messages 7 | * add flush method 8 | 9 | ##### v1.3.3 10 | * add check if exisiting_taxonomies is an array 11 | 12 | ##### v1.3.2 13 | * fix register taxonomies exisiting taxonomies after post type is regitered 14 | * fix `add_admin_columns` to work with 3rd party plugins 15 | * capital P dangit 16 | 17 | ##### v1.3.1 18 | * register taxonomies before post type to fix issues with taxonomy permalinks 19 | 20 | ##### v1.3.0 21 | * fix translation issues 22 | * new method to set custom textdomain with `set_textdomain()` 23 | 24 | ##### v1.2.4 25 | * add check if `$filter` array is empty 26 | 27 | ##### v1.2.3 28 | * add array check for `$this->taxonomy_settings` 29 | 30 | ##### v1.2.2 31 | * fix issues when registering taxonomy across multiple post type 32 | * remove wrapper function `options_merge` 33 | 34 | ##### v1.2.1 35 | * reduce the defaults within the class 36 | * replace contents of `options_merge` function with `array_replace_recursive` 37 | 38 | ##### v1.2.0 39 | * allow taxonomies to be sorted with the `sortable()` method 40 | * use of `.gitattributes` to make package lighter when deploying for production. 41 | 42 | ##### v1.1.0 43 | * make repository a composer package 44 | 45 | ##### v1.0.2 46 | * ability to use dashicons with `menu_icon()` method 47 | * removed old custom icon functions 48 | 49 | ##### v1.0.1 50 | * fixed issue with registering taxonomies 51 | * fixed issue with options merge, now accepts boolean 52 | * register_taxonomy method can now register exisiting taxonomies to post type 53 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 jjgrainger 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **N.B** I've released an updated version of the project to a new repository, [PostTypes](https://github.com/jjgrainger/PostTypes). 2 | 3 | # WP Custom Post Type Class v1.4 4 | 5 | [![Latest Stable Version](https://poser.pugx.org/jjgrainger/wp-custom-post-type-class/v/stable)](https://packagist.org/packages/jjgrainger/wp-custom-post-type-class) [![Total Downloads](https://poser.pugx.org/jjgrainger/wp-custom-post-type-class/downloads)](https://packagist.org/packages/jjgrainger/wp-custom-post-type-class) [![License](https://poser.pugx.org/jjgrainger/wp-custom-post-type-class/license)](https://packagist.org/packages/jjgrainger/wp-custom-post-type-class) 6 | 7 | > A single class to help you build more advanced custom post types quickly. 8 | 9 | ## Installation 10 | 11 | #### Install with Composer 12 | 13 | Add the package to your projects `composer.json` file. Visit [getcomposer.org](http://getcomposer.org/) more information. 14 | 15 | ```json 16 | { 17 | "require": { 18 | "jjgrainger/wp-custom-post-type-class": "dev-master" 19 | } 20 | } 21 | ``` 22 | 23 | #### Install Manually 24 | 25 | Download and include the class file into your themes `functions.php` like so: 26 | 27 | ```php 28 | include_once('CPT.php'); 29 | ``` 30 | 31 | and your ready to roll! 32 | 33 | ## Creating a new Custom Post type 34 | 35 | To create the post type simply create a new object 36 | 37 | ```php 38 | $books = new CPT('book'); 39 | ``` 40 | 41 | The first parameter is the post type name and is required. ideally the post type name is all lowercase and words separated with an underscore `_`. 42 | 43 | to be specific about other post types names you can pass an associative array: 44 | 45 | `post_type_name` - the name of post type (singular, lowercase, underscores) 46 | 47 | `singular` - the singular label of the post type (Book, Person) 48 | 49 | `plural` - the plural of the post type (Books, People) 50 | 51 | `slug` - the permalink slug for the post type (plural, lowercase, hyphens) 52 | 53 | you pass these names through the first parameter as an array like so: 54 | 55 | ```php 56 | $people = new CPT(array( 57 | 'post_type_name' => 'person', 58 | 'singular' => 'Person', 59 | 'plural' => 'People', 60 | 'slug' => 'people' 61 | )); 62 | ``` 63 | 64 | The optional second parameter is the arguments for the post_type. 65 | see [WordPress codex](http://codex.wordpress.org/Function_Reference/register_post_type#Parameters) for available options. 66 | 67 | The Class uses the WordPress defaults where possible. 68 | 69 | To override the default options simply pass an array of options as the second parameter. Not all options have to be passed just the ones you want to add/override like so: 70 | 71 | ```php 72 | $books = new CPT('book', array( 73 | 'supports' => array('title', 'editor', 'thumbnail', 'comments') 74 | )); 75 | ``` 76 | 77 | See the [WordPress codex](http://codex.wordpress.org/Function_Reference/register_post_type#Parameters) for all available options. 78 | 79 | ## Existing Post Types 80 | 81 | To work with exisiting post types, simply pass the post type name into the class constructor 82 | 83 | ```php 84 | $blog = new CPT('post'); 85 | ``` 86 | 87 | ## Adding Taxonomies 88 | 89 | You can add taxonomies easily using the `register_taxonomy()` method like so: 90 | 91 | ```php 92 | $books->register_taxonomy('genres'); 93 | ``` 94 | 95 | this method accepts two arguments, names and options. The taxonomy name is required and can be string (the taxonomy name), or an array of names following same format as post types: 96 | 97 | ```php 98 | $books->register_taxonomy(array( 99 | 'taxonomy_name' => 'genre', 100 | 'singular' => 'Genre', 101 | 'plural' => 'Genres', 102 | 'slug' => 'genre' 103 | )); 104 | ``` 105 | 106 | Again options can be passed optionally as an array. see the [WordPress codex](http://codex.wordpress.org/Function_Reference/register_taxonomy#Parameters) for all possible options. 107 | 108 | ### Existing Taxonomies 109 | 110 | You can add exisiting taxonomies to the post type by passing the taxonomy name through the `register_taxonomy` method. You will only need to specify the options for the custom taxonomy **once**, when its first registered. 111 | 112 | ## Admin Edit Screen 113 | 114 | ### Filters 115 | 116 | When you register a taxonomy it is *automagically* added to the admin edit screen as a filter and a column. 117 | 118 | You can define what filters you want to appear by using the `filters()` method: 119 | 120 | ```php 121 | $books->filters(array('genre')); 122 | ``` 123 | 124 | By passing an array of taxonomy names you can choose the filters that appear and the order they appear in. If you pass an empty array, no drop down filters will appear on the admin edit screen. 125 | 126 | ### Columns 127 | 128 | The Class has a number of methods to help you modify the admin columns. 129 | Taxonomies registered with this class are automagically added to the admin edit screen as columns. 130 | 131 | You can add your own custom columns to include what ever value you want, for example with our books post type we will add custom fields for a price and rating. 132 | 133 | This class doesn't have any methods for adding custom fields as [Advanced Custom Fields (ACF)](http://advancedcustomfields.com) is way more awesome than anything this class could do! 134 | 135 | You can define what columns you want to appear on the admin edit screen with the `columns()` method by passing an array like so: 136 | 137 | ```php 138 | $books->columns(array( 139 | 'cb' => '', 140 | 'title' => __('Title'), 141 | 'genre' => __('Genres'), 142 | 'price' => __('Price'), 143 | 'rating' => __('Rating'), 144 | 'date' => __('Date') 145 | )); 146 | ``` 147 | 148 | The key defines the name of the column, the value is the label that appears for that column. The following column names are *automagically* populated by the class: 149 | 150 | - any taxonomy registered through the object 151 | - `cb` the checkbox for bulk editing 152 | - `title` the post title with the edit link 153 | - `author` the post author 154 | - `post_id` the posts id 155 | - `icon` the posts thumbnail 156 | 157 | 158 | #### Populating Columns 159 | 160 | You will need to create a function to populate a column that isn't *automagically* populated. 161 | 162 | You do so with the `populate_column()` method like so: 163 | 164 | ```php 165 | $books->populate_column('column_name', function($column, $post) { 166 | 167 | // your code goes here… 168 | 169 | }); 170 | ``` 171 | 172 | so we can populate our price column like so: 173 | 174 | ```php 175 | $books->populate_column('price', function($column, $post) { 176 | 177 | echo "£" . get_field('price'); // ACF get_field() function 178 | 179 | }); 180 | ``` 181 | 182 | The method will pass two variables into the function: 183 | 184 | * `$column` - The column name (not the label) 185 | * `$post` - The current post object 186 | 187 | These are passed to help you populate the column appropriately. 188 | 189 | #### Sorting Columns 190 | 191 | If it makes sense that column should be sortable by ascending/descending you can define custom sortable columns like so: 192 | 193 | ```php 194 | $books->sortable(array( 195 | 'column_name' => array('meta_key', true) 196 | )); 197 | ``` 198 | 199 | The `true/false` is used to define whether the meta value is a string or integer, 200 | reason being is that if numbers are ordered as a string, numbers such as: 201 | 202 | 1, 3, 5, 11, 14, 21, 33 203 | 204 | Would be ordered as: 205 | 206 | 1, 11, 14, 21, 3, 33, 5 207 | 208 | By adding the option true value the values will be sorted as integers, if false or undefined, the class will sort columns as string. 209 | 210 | so for our books example you will use: 211 | 212 | ```php 213 | $books->sortable(array( 214 | 'price' => array('price', true), 215 | 'rating' => array('rating', true) 216 | )); 217 | ``` 218 | 219 | ### Menu Icons 220 | 221 | #### Dashicons 222 | 223 | With WordPress 3.8 comes [dashicons](https://developer.wordpress.org/resource/dashicons/) an icon font you can use with your custom post types. To use simply pass the icon name through the `menu_icon()` method like so: 224 | 225 | ```php 226 | $books->menu_icon("dashicons-book-alt"); 227 | ``` 228 | 229 | For a full list of icons and the class names to use visit [https://developer.wordpress.org/resource/dashicons/](https://developer.wordpress.org/resource/dashicons/) 230 | 231 | ### Translation 232 | 233 | The class is setup for translation, but if you need to set your own textdomain to work with your theme or plugin use the `set_textdomain()` method: 234 | 235 | ```php 236 | $books->set_textdomain('your-textdomain'); 237 | ``` 238 | 239 | ### Flush Rewrite Rules 240 | 241 | You can programmatically recreate the sites rewrite rules with the `flush()` method. 242 | This is an expensive operation and should be used with caution, see [codex](https://codex.wordpress.org/Function_Reference/flush_rewrite_rules) for more. 243 | 244 | ```php 245 | $books->flush(); 246 | ``` 247 | 248 | ## Notes 249 | 250 | * The class has no methods for making custom fields for post types, use [Advanced Custom Fields](http://advancedcustomfields.com) 251 | * The books example used in the README.md can be found in the [books-post-type.php](examples/books-post-type.php) 252 | * Licensed under the [MIT License](https://github.com/jjgrainger/wp-custom-post-type-class/blob/master/LICENSE) 253 | * Maintained under the [Semantic Versioning Guide](http://semver.org) 254 | 255 | ## Author 256 | 257 | **Joe Grainger** 258 | * [http://jjgrainger.co.uk](http://jjgrainger.co.uk) 259 | * [http://twitter.com/jjgrainger](http://twitter.com/jjgrainger) 260 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jjgrainger/wp-custom-post-type-class", 3 | "description": "A single class to help you build more advanced custom post types quickly.", 4 | "homepage": "https://github.com/jjgrainger/wp-custom-post-type-class", 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "Joe Grainger", 9 | "email": "hello@joegrainger.co.uk", 10 | "homepage": "http://jjgrainger.co.uk" 11 | } 12 | ], 13 | "minimum-stability": "dev", 14 | "require": { 15 | "php": ">=5.3.0" 16 | }, 17 | "autoload": { 18 | "psr-0": { "CPT": "src/" } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/CPT.php: -------------------------------------------------------------------------------- 1 | post_type_name = $post_type_names['post_type_name']; 134 | 135 | // Cycle through possible names. 136 | foreach ( $names as $name ) { 137 | 138 | // If the name has been set by user. 139 | if ( isset( $post_type_names[ $name ] ) ) { 140 | 141 | // Use the user setting 142 | $this->$name = $post_type_names[ $name ]; 143 | 144 | // Else generate the name. 145 | } else { 146 | 147 | // define the method to be used 148 | $method = 'get_' . $name; 149 | 150 | // Generate the name 151 | $this->$name = $this->$method(); 152 | } 153 | } 154 | 155 | // Else the post type name is only supplied. 156 | } else { 157 | 158 | // Apply to post type name. 159 | $this->post_type_name = $post_type_names; 160 | 161 | // Set the slug name. 162 | $this->slug = $this->get_slug(); 163 | 164 | // Set the plural name label. 165 | $this->plural = $this->get_plural(); 166 | 167 | // Set the singular name label. 168 | $this->singular = $this->get_singular(); 169 | } 170 | 171 | // Set the user submitted options to the object. 172 | $this->options = $options; 173 | 174 | // Register taxonomies. 175 | $this->add_action( 'init', array( &$this, 'register_taxonomies' ) ); 176 | 177 | // Register the post type. 178 | $this->add_action( 'init', array( &$this, 'register_post_type' ) ); 179 | 180 | // Register exisiting taxonomies. 181 | $this->add_action( 'init', array( &$this, 'register_exisiting_taxonomies' ) ); 182 | 183 | // Add taxonomy to admin edit columns. 184 | $this->add_filter( 'manage_edit-' . $this->post_type_name . '_columns', array( &$this, 'add_admin_columns' ) ); 185 | 186 | // Populate the taxonomy columns with the posts terms. 187 | $this->add_action( 'manage_' . $this->post_type_name . '_posts_custom_column', array( &$this, 'populate_admin_columns' ), 10, 2 ); 188 | 189 | // Add filter select option to admin edit. 190 | $this->add_action( 'restrict_manage_posts', array( &$this, 'add_taxonomy_filters' ) ); 191 | 192 | // rewrite post update messages 193 | $this->add_filter( 'post_updated_messages', array( &$this, 'updated_messages' ) ); 194 | $this->add_filter( 'bulk_post_updated_messages', array( &$this, 'bulk_updated_messages' ), 10, 2 ); 195 | } 196 | 197 | /** 198 | * Get 199 | * 200 | * Helper function to get an object variable. 201 | * 202 | * @param string $var The variable you would like to retrieve. 203 | * @return mixed Returns the value on success, boolean false whe it fails. 204 | */ 205 | function get( $var ) { 206 | 207 | // If the variable exists. 208 | if ( $this->$var ) { 209 | 210 | // On success return the value. 211 | return $this->$var; 212 | 213 | } else { 214 | 215 | // on fail return false 216 | return false; 217 | } 218 | } 219 | 220 | /** 221 | * Set 222 | * 223 | * Helper function used to set an object variable. Can overwrite existsing 224 | * variables or create new ones. Cannot overwrite reserved variables. 225 | * 226 | * @param mixed $var The variable you would like to create/overwrite. 227 | * @param mixed $value The value you would like to set to the variable. 228 | */ 229 | function set( $var, $value ) { 230 | 231 | // An array of reserved variables that cannot be overwritten. 232 | $reserved = array( 233 | 'config', 234 | 'post_type_name', 235 | 'singular', 236 | 'plural', 237 | 'slug', 238 | 'options', 239 | 'taxonomies' 240 | ); 241 | 242 | // If the variable is not a reserved variable 243 | if ( ! in_array( $var, $reserved ) ) { 244 | 245 | // Write variable and value 246 | $this->$var = $value; 247 | } 248 | } 249 | 250 | /** 251 | * Add Action 252 | * 253 | * Helper function to add add_action WordPress filters. 254 | * 255 | * @param string $action Name of the action. 256 | * @param string $function Function to hook that will run on action. 257 | * @param integet $priority Order in which to execute the function, relation to other functions hooked to this action. 258 | * @param integer $accepted_args The number of arguments the function accepts. 259 | */ 260 | function add_action( $action, $function, $priority = 10, $accepted_args = 1 ) { 261 | 262 | // Pass variables into WordPress add_action function 263 | add_action( $action, $function, $priority, $accepted_args ); 264 | } 265 | 266 | /** 267 | * Add Filter 268 | * 269 | * Create add_filter WordPress filter. 270 | * 271 | * @see http://codex.wordpress.org/Function_Reference/add_filter 272 | * 273 | * @param string $action Name of the action to hook to, e.g 'init'. 274 | * @param string $function Function to hook that will run on @action. 275 | * @param int $priority Order in which to execute the function, relation to other function hooked to this action. 276 | * @param int $accepted_args The number of arguements the function accepts. 277 | */ 278 | function add_filter( $action, $function, $priority = 10, $accepted_args = 1 ) { 279 | 280 | // Pass variables into Wordpress add_action function 281 | add_filter( $action, $function, $priority, $accepted_args ); 282 | } 283 | 284 | /** 285 | * Get slug 286 | * 287 | * Creates an url friendly slug. 288 | * 289 | * @param string $name Name to slugify. 290 | * @return string $name Returns the slug. 291 | */ 292 | function get_slug( $name = null ) { 293 | 294 | // If no name set use the post type name. 295 | if ( ! isset( $name ) ) { 296 | 297 | $name = $this->post_type_name; 298 | } 299 | 300 | // Name to lower case. 301 | $name = strtolower( $name ); 302 | 303 | // Replace spaces with hyphen. 304 | $name = str_replace( " ", "-", $name ); 305 | 306 | // Replace underscore with hyphen. 307 | $name = str_replace( "_", "-", $name ); 308 | 309 | return $name; 310 | } 311 | 312 | /** 313 | * Get plural 314 | * 315 | * Returns the friendly plural name. 316 | * 317 | * ucwords capitalize words 318 | * strtolower makes string lowercase before capitalizing 319 | * str_replace replace all instances of _ to space 320 | * 321 | * @param string $name The slug name you want to pluralize. 322 | * @return string the friendly pluralized name. 323 | */ 324 | function get_plural( $name = null ) { 325 | 326 | // If no name is passed the post_type_name is used. 327 | if ( ! isset( $name ) ) { 328 | 329 | $name = $this->post_type_name; 330 | } 331 | 332 | // Return the plural name. Add 's' to the end. 333 | return $this->get_human_friendly( $name ) . 's'; 334 | } 335 | 336 | /** 337 | * Get singular 338 | * 339 | * Returns the friendly singular name. 340 | * 341 | * ucwords capitalize words 342 | * strtolower makes string lowercase before capitalizing 343 | * str_replace replace all instances of _ to space 344 | * 345 | * @param string $name The slug name you want to unpluralize. 346 | * @return string The friendly singular name. 347 | */ 348 | function get_singular( $name = null ) { 349 | 350 | // If no name is passed the post_type_name is used. 351 | if ( ! isset( $name ) ) { 352 | 353 | $name = $this->post_type_name; 354 | 355 | } 356 | 357 | // Return the string. 358 | return $this->get_human_friendly( $name ); 359 | } 360 | 361 | /** 362 | * Get human friendly 363 | * 364 | * Returns the human friendly name. 365 | * 366 | * ucwords capitalize words 367 | * strtolower makes string lowercase before capitalizing 368 | * str_replace replace all instances of hyphens and underscores to spaces 369 | * 370 | * @param string $name The name you want to make friendly. 371 | * @return string The human friendly name. 372 | */ 373 | function get_human_friendly( $name = null ) { 374 | 375 | // If no name is passed the post_type_name is used. 376 | if ( ! isset( $name ) ) { 377 | 378 | $name = $this->post_type_name; 379 | } 380 | 381 | // Return human friendly name. 382 | return ucwords( strtolower( str_replace( "-", " ", str_replace( "_", " ", $name ) ) ) ); 383 | } 384 | 385 | /** 386 | * Register Post Type 387 | * 388 | * @see http://codex.wordpress.org/Function_Reference/register_post_type 389 | */ 390 | function register_post_type() { 391 | 392 | // Friendly post type names. 393 | $plural = $this->plural; 394 | $singular = $this->singular; 395 | $slug = $this->slug; 396 | 397 | // Default labels. 398 | $labels = array( 399 | 'name' => sprintf( __( '%s', $this->textdomain ), $plural ), 400 | 'singular_name' => sprintf( __( '%s', $this->textdomain ), $singular ), 401 | 'menu_name' => sprintf( __( '%s', $this->textdomain ), $plural ), 402 | 'all_items' => sprintf( __( '%s', $this->textdomain ), $plural ), 403 | 'add_new' => __( 'Add New', $this->textdomain ), 404 | 'add_new_item' => sprintf( __( 'Add New %s', $this->textdomain ), $singular ), 405 | 'edit_item' => sprintf( __( 'Edit %s', $this->textdomain ), $singular ), 406 | 'new_item' => sprintf( __( 'New %s', $this->textdomain ), $singular ), 407 | 'view_item' => sprintf( __( 'View %s', $this->textdomain ), $singular ), 408 | 'search_items' => sprintf( __( 'Search %s', $this->textdomain ), $plural ), 409 | 'not_found' => sprintf( __( 'No %s found', $this->textdomain ), $plural ), 410 | 'not_found_in_trash' => sprintf( __( 'No %s found in Trash', $this->textdomain ), $plural ), 411 | 'parent_item_colon' => sprintf( __( 'Parent %s:', $this->textdomain ), $singular ) 412 | ); 413 | 414 | // Default options. 415 | $defaults = array( 416 | 'labels' => $labels, 417 | 'public' => true, 418 | 'rewrite' => array( 419 | 'slug' => $slug, 420 | ) 421 | ); 422 | 423 | // Merge user submitted options with defaults. 424 | $options = array_replace_recursive( $defaults, $this->options ); 425 | 426 | // Set the object options as full options passed. 427 | $this->options = $options; 428 | 429 | // Check that the post type doesn't already exist. 430 | if ( ! post_type_exists( $this->post_type_name ) ) { 431 | 432 | // Register the post type. 433 | register_post_type( $this->post_type_name, $options ); 434 | } 435 | } 436 | 437 | /** 438 | * Register taxonomy 439 | * 440 | * @see http://codex.wordpress.org/Function_Reference/register_taxonomy 441 | * 442 | * @param string $taxonomy_name The slug for the taxonomy. 443 | * @param array $options Taxonomy options. 444 | */ 445 | function register_taxonomy($taxonomy_names, $options = array()) { 446 | 447 | // Post type defaults to $this post type if unspecified. 448 | $post_type = $this->post_type_name; 449 | 450 | // An array of the names required excluding taxonomy_name. 451 | $names = array( 452 | 'singular', 453 | 'plural', 454 | 'slug' 455 | ); 456 | 457 | // if an array of names are passed 458 | if ( is_array( $taxonomy_names ) ) { 459 | 460 | // Set the taxonomy name 461 | $taxonomy_name = $taxonomy_names['taxonomy_name']; 462 | 463 | // Cycle through possible names. 464 | foreach ( $names as $name ) { 465 | 466 | // If the user has set the name. 467 | if ( isset( $taxonomy_names[ $name ] ) ) { 468 | 469 | // Use user submitted name. 470 | $$name = $taxonomy_names[ $name ]; 471 | 472 | // Else generate the name. 473 | } else { 474 | 475 | // Define the function to be used. 476 | $method = 'get_' . $name; 477 | 478 | // Generate the name 479 | $$name = $this->$method( $taxonomy_name ); 480 | 481 | } 482 | } 483 | 484 | // Else if only the taxonomy_name has been supplied. 485 | } else { 486 | 487 | // Create user friendly names. 488 | $taxonomy_name = $taxonomy_names; 489 | $singular = $this->get_singular( $taxonomy_name ); 490 | $plural = $this->get_plural( $taxonomy_name ); 491 | $slug = $this->get_slug( $taxonomy_name ); 492 | 493 | } 494 | 495 | // Default labels. 496 | $labels = array( 497 | 'name' => sprintf( __( '%s', $this->textdomain ), $plural ), 498 | 'singular_name' => sprintf( __( '%s', $this->textdomain ), $singular ), 499 | 'menu_name' => sprintf( __( '%s', $this->textdomain ), $plural ), 500 | 'all_items' => sprintf( __( 'All %s', $this->textdomain ), $plural ), 501 | 'edit_item' => sprintf( __( 'Edit %s', $this->textdomain ), $singular ), 502 | 'view_item' => sprintf( __( 'View %s', $this->textdomain ), $singular ), 503 | 'update_item' => sprintf( __( 'Update %s', $this->textdomain ), $singular ), 504 | 'add_new_item' => sprintf( __( 'Add New %s', $this->textdomain ), $singular ), 505 | 'new_item_name' => sprintf( __( 'New %s Name', $this->textdomain ), $singular ), 506 | 'parent_item' => sprintf( __( 'Parent %s', $this->textdomain ), $plural ), 507 | 'parent_item_colon' => sprintf( __( 'Parent %s:', $this->textdomain ), $plural ), 508 | 'search_items' => sprintf( __( 'Search %s', $this->textdomain ), $plural ), 509 | 'popular_items' => sprintf( __( 'Popular %s', $this->textdomain ), $plural ), 510 | 'separate_items_with_commas' => sprintf( __( 'Seperate %s with commas', $this->textdomain ), $plural ), 511 | 'add_or_remove_items' => sprintf( __( 'Add or remove %s', $this->textdomain ), $plural ), 512 | 'choose_from_most_used' => sprintf( __( 'Choose from most used %s', $this->textdomain ), $plural ), 513 | 'not_found' => sprintf( __( 'No %s found', $this->textdomain ), $plural ), 514 | ); 515 | 516 | // Default options. 517 | $defaults = array( 518 | 'labels' => $labels, 519 | 'hierarchical' => true, 520 | 'rewrite' => array( 521 | 'slug' => $slug 522 | ) 523 | ); 524 | 525 | // Merge default options with user submitted options. 526 | $options = array_replace_recursive( $defaults, $options ); 527 | 528 | // Add the taxonomy to the object array, this is used to add columns and filters to admin panel. 529 | $this->taxonomies[] = $taxonomy_name; 530 | 531 | // Create array used when registering taxonomies. 532 | $this->taxonomy_settings[ $taxonomy_name ] = $options; 533 | 534 | } 535 | 536 | 537 | 538 | /** 539 | * Register taxonomies 540 | * 541 | * Cycles through taxonomies added with the class and registers them. 542 | */ 543 | function register_taxonomies() { 544 | 545 | if ( is_array( $this->taxonomy_settings ) ) { 546 | 547 | // Foreach taxonomy registered with the post type. 548 | foreach ( $this->taxonomy_settings as $taxonomy_name => $options ) { 549 | 550 | // Register the taxonomy if it doesn't exist. 551 | if ( ! taxonomy_exists( $taxonomy_name ) ) { 552 | 553 | // Register the taxonomy with Wordpress 554 | register_taxonomy( $taxonomy_name, $this->post_type_name, $options ); 555 | 556 | } else { 557 | 558 | // If taxonomy exists, register it later with register_exisiting_taxonomies 559 | $this->exisiting_taxonomies[] = $taxonomy_name; 560 | } 561 | } 562 | } 563 | } 564 | 565 | /** 566 | * Register Exisiting Taxonomies 567 | * 568 | * Cycles through exisiting taxonomies and registers them after the post type has been registered 569 | */ 570 | function register_exisiting_taxonomies() { 571 | 572 | if( is_array( $this->exisiting_taxonomies ) ) { 573 | foreach( $this->exisiting_taxonomies as $taxonomy_name ) { 574 | register_taxonomy_for_object_type( $taxonomy_name, $this->post_type_name ); 575 | } 576 | } 577 | } 578 | 579 | /** 580 | * Add admin columns 581 | * 582 | * Adds columns to the admin edit screen. Function is used with add_action 583 | * 584 | * @param array $columns Columns to be added to the admin edit screen. 585 | * @return array 586 | */ 587 | function add_admin_columns( $columns ) { 588 | 589 | // If no user columns have been specified, add taxonomies 590 | if ( ! isset( $this->columns ) ) { 591 | 592 | $new_columns = array(); 593 | 594 | // determine which column to add custom taxonomies after 595 | if ( is_array( $this->taxonomies ) && in_array( 'post_tag', $this->taxonomies ) || $this->post_type_name === 'post' ) { 596 | $after = 'tags'; 597 | } elseif( is_array( $this->taxonomies ) && in_array( 'category', $this->taxonomies ) || $this->post_type_name === 'post' ) { 598 | $after = 'categories'; 599 | } elseif( post_type_supports( $this->post_type_name, 'author' ) ) { 600 | $after = 'author'; 601 | } else { 602 | $after = 'title'; 603 | } 604 | 605 | // foreach exisiting columns 606 | foreach( $columns as $key => $title ) { 607 | 608 | // add exisiting column to the new column array 609 | $new_columns[$key] = $title; 610 | 611 | // we want to add taxonomy columns after a specific column 612 | if( $key === $after ) { 613 | 614 | // If there are taxonomies registered to the post type. 615 | if ( is_array( $this->taxonomies ) ) { 616 | 617 | // Create a column for each taxonomy. 618 | foreach( $this->taxonomies as $tax ) { 619 | 620 | // WordPress adds Categories and Tags automatically, ignore these 621 | if( $tax !== 'category' && $tax !== 'post_tag' ) { 622 | // Get the taxonomy object for labels. 623 | $taxonomy_object = get_taxonomy( $tax ); 624 | 625 | // Column key is the slug, value is friendly name. 626 | $new_columns[ $tax ] = sprintf( __( '%s', $this->textdomain ), $taxonomy_object->labels->name ); 627 | } 628 | } 629 | } 630 | } 631 | } 632 | 633 | // overide with new columns 634 | $columns = $new_columns; 635 | 636 | } else { 637 | 638 | // Use user submitted columns, these are defined using the object columns() method. 639 | $columns = $this->columns; 640 | } 641 | 642 | return $columns; 643 | } 644 | 645 | /** 646 | * Populate admin columns 647 | * 648 | * Populate custom columns on the admin edit screen. 649 | * 650 | * @param string $column The name of the column. 651 | * @param integer $post_id The post ID. 652 | */ 653 | function populate_admin_columns( $column, $post_id ) { 654 | 655 | // Get wordpress $post object. 656 | global $post; 657 | 658 | // determine the column 659 | switch( $column ) { 660 | 661 | // If column is a taxonomy associated with the post type. 662 | case ( taxonomy_exists( $column ) ) : 663 | 664 | // Get the taxonomy for the post 665 | $terms = get_the_terms( $post_id, $column ); 666 | 667 | // If we have terms. 668 | if ( ! empty( $terms ) ) { 669 | 670 | $output = array(); 671 | 672 | // Loop through each term, linking to the 'edit posts' page for the specific term. 673 | foreach( $terms as $term ) { 674 | 675 | // Output is an array of terms associated with the post. 676 | $output[] = sprintf( 677 | 678 | // Define link. 679 | '%s', 680 | 681 | // Create filter url. 682 | esc_url( add_query_arg( array( 'post_type' => $post->post_type, $column => $term->slug ), 'edit.php' ) ), 683 | 684 | // Create friendly term name. 685 | esc_html( sanitize_term_field( 'name', $term->name, $term->term_id, $column, 'display' ) ) 686 | ); 687 | 688 | } 689 | 690 | // Join the terms, separating them with a comma. 691 | echo join( ', ', $output ); 692 | 693 | // If no terms found. 694 | } else { 695 | 696 | // Get the taxonomy object for labels 697 | $taxonomy_object = get_taxonomy( $column ); 698 | 699 | // Echo no terms. 700 | printf( __( 'No %s', $this->textdomain ), $taxonomy_object->labels->name ); 701 | } 702 | 703 | break; 704 | 705 | // If column is for the post ID. 706 | case 'post_id' : 707 | 708 | echo $post->ID; 709 | 710 | break; 711 | 712 | // if the column is prepended with 'meta_', this will automagically retrieve the meta values and display them. 713 | case ( preg_match( '/^meta_/', $column ) ? true : false ) : 714 | 715 | // meta_book_author (meta key = book_author) 716 | $x = substr( $column, 5 ); 717 | 718 | $meta = get_post_meta( $post->ID, $x ); 719 | 720 | echo join( ", ", $meta ); 721 | 722 | break; 723 | 724 | // If the column is post thumbnail. 725 | case 'icon' : 726 | 727 | // Create the edit link. 728 | $link = esc_url( add_query_arg( array( 'post' => $post->ID, 'action' => 'edit' ), 'post.php' ) ); 729 | 730 | // If it post has a featured image. 731 | if ( has_post_thumbnail() ) { 732 | 733 | // Display post featured image with edit link. 734 | echo ''; 735 | the_post_thumbnail( array(60, 60) ); 736 | echo ''; 737 | 738 | } else { 739 | 740 | // Display default media image with link. 741 | echo '' . $post->post_title . ''; 742 | 743 | } 744 | 745 | break; 746 | 747 | // Default case checks if the column has a user function, this is most commonly used for custom fields. 748 | default : 749 | 750 | // If there are user custom columns to populate. 751 | if ( isset( $this->custom_populate_columns ) && is_array( $this->custom_populate_columns ) ) { 752 | 753 | // If this column has a user submitted function to run. 754 | if ( isset( $this->custom_populate_columns[ $column ] ) && is_callable( $this->custom_populate_columns[ $column ] ) ) { 755 | 756 | // Run the function. 757 | call_user_func_array( $this->custom_populate_columns[ $column ], array( $column, $post ) ); 758 | 759 | } 760 | } 761 | 762 | break; 763 | } // end switch( $column ) 764 | } 765 | 766 | /** 767 | * Filters 768 | * 769 | * User function to define which taxonomy filters to display on the admin page. 770 | * 771 | * @param array $filters An array of taxonomy filters to display. 772 | */ 773 | function filters( $filters = array() ) { 774 | 775 | $this->filters = $filters; 776 | } 777 | 778 | /** 779 | * Add taxtonomy filters 780 | * 781 | * Creates select fields for filtering posts by taxonomies on admin edit screen. 782 | */ 783 | function add_taxonomy_filters() { 784 | 785 | global $typenow; 786 | global $wp_query; 787 | 788 | // Must set this to the post type you want the filter(s) displayed on. 789 | if ( $typenow == $this->post_type_name ) { 790 | 791 | // if custom filters are defined use those 792 | if ( is_array( $this->filters ) ) { 793 | 794 | $filters = $this->filters; 795 | 796 | // else default to use all taxonomies associated with the post 797 | } else { 798 | 799 | $filters = $this->taxonomies; 800 | } 801 | 802 | if ( ! empty( $filters ) ) { 803 | 804 | // Foreach of the taxonomies we want to create filters for... 805 | foreach ( $filters as $tax_slug ) { 806 | 807 | // ...object for taxonomy, doesn't contain the terms. 808 | $tax = get_taxonomy( $tax_slug ); 809 | 810 | // Get taxonomy terms and order by name. 811 | $args = array( 812 | 'orderby' => 'name', 813 | 'hide_empty' => false 814 | ); 815 | 816 | // Get taxonomy terms. 817 | $terms = get_terms( $tax_slug, $args ); 818 | 819 | // If we have terms. 820 | if ( $terms ) { 821 | 822 | // Set up select box. 823 | printf( '   ' ); 844 | } 845 | } 846 | } 847 | } 848 | } 849 | 850 | /** 851 | * Columns 852 | * 853 | * Choose columns to be displayed on the admin edit screen. 854 | * 855 | * @param array $columns An array of columns to be displayed. 856 | */ 857 | function columns( $columns ) { 858 | 859 | // If columns is set. 860 | if( isset( $columns ) ) { 861 | 862 | // Assign user submitted columns to object. 863 | $this->columns = $columns; 864 | 865 | } 866 | } 867 | 868 | /** 869 | * Populate columns 870 | * 871 | * Define what and how to populate a speicific admin column. 872 | * 873 | * @param string $column_name The name of the column to populate. 874 | * @param mixed $callback An anonyous function or callable array to call when populating the column. 875 | */ 876 | function populate_column( $column_name, $callback ) { 877 | 878 | $this->custom_populate_columns[ $column_name ] = $callback; 879 | 880 | } 881 | 882 | /** 883 | * Sortable 884 | * 885 | * Define what columns are sortable in the admin edit screen. 886 | * 887 | * @param array $columns An array of columns that are sortable. 888 | */ 889 | function sortable( $columns = array() ) { 890 | 891 | // Assign user defined sortable columns to object variable. 892 | $this->sortable = $columns; 893 | 894 | // Run filter to make columns sortable. 895 | $this->add_filter( 'manage_edit-' . $this->post_type_name . '_sortable_columns', array( &$this, 'make_columns_sortable' ) ); 896 | 897 | // Run action that sorts columns on request. 898 | $this->add_action( 'load-edit.php', array( &$this, 'load_edit' ) ); 899 | } 900 | 901 | /** 902 | * Make columns sortable 903 | * 904 | * Internal function that adds user defined sortable columns to WordPress default columns. 905 | * 906 | * @param array $columns Columns to be sortable. 907 | * 908 | */ 909 | function make_columns_sortable( $columns ) { 910 | 911 | // For each sortable column. 912 | foreach ( $this->sortable as $column => $values ) { 913 | 914 | // Make an array to merge into wordpress sortable columns. 915 | $sortable_columns[ $column ] = $values[0]; 916 | } 917 | 918 | // Merge sortable columns array into wordpress sortable columns. 919 | $columns = array_merge( $sortable_columns, $columns ); 920 | 921 | return $columns; 922 | } 923 | 924 | /** 925 | * Load edit 926 | * 927 | * Sort columns only on the edit.php page when requested. 928 | * 929 | * @see http://codex.wordpress.org/Plugin_API/Filter_Reference/request 930 | */ 931 | function load_edit() { 932 | 933 | // Run filter to sort columns when requested 934 | $this->add_filter( 'request', array( &$this, 'sort_columns' ) ); 935 | 936 | } 937 | 938 | /** 939 | * Sort columns 940 | * 941 | * Internal function that sorts columns on request. 942 | * 943 | * @see load_edit() 944 | * 945 | * @param array $vars The query vars submitted by user. 946 | * @return array A sorted array. 947 | */ 948 | function sort_columns( $vars ) { 949 | 950 | // Cycle through all sortable columns submitted by the user 951 | foreach ( $this->sortable as $column => $values ) { 952 | 953 | // Retrieve the meta key from the user submitted array of sortable columns 954 | $meta_key = $values[0]; 955 | 956 | // If the meta_key is a taxonomy 957 | if( taxonomy_exists( $meta_key ) ) { 958 | 959 | // Sort by taxonomy. 960 | $key = "taxonomy"; 961 | 962 | } else { 963 | 964 | // else by meta key. 965 | $key = "meta_key"; 966 | } 967 | 968 | // If the optional parameter is set and is set to true 969 | if ( isset( $values[1] ) && true === $values[1] ) { 970 | 971 | // Vaules needed to be ordered by integer value 972 | $orderby = 'meta_value_num'; 973 | 974 | } else { 975 | 976 | // Values are to be order by string value 977 | $orderby = 'meta_value'; 978 | } 979 | 980 | // Check if we're viewing this post type 981 | if ( isset( $vars['post_type'] ) && $this->post_type_name == $vars['post_type'] ) { 982 | 983 | // find the meta key we want to order posts by 984 | if ( isset( $vars['orderby'] ) && $meta_key == $vars['orderby'] ) { 985 | 986 | // Merge the query vars with our custom variables 987 | $vars = array_merge( 988 | $vars, 989 | array( 990 | 'meta_key' => $meta_key, 991 | 'orderby' => $orderby 992 | ) 993 | ); 994 | } 995 | } 996 | } 997 | return $vars; 998 | } 999 | 1000 | /** 1001 | * Set menu icon 1002 | * 1003 | * Use this function to set the menu icon in the admin dashboard. Since WordPress v3.8 1004 | * dashicons are used. For more information see @link http://melchoyce.github.io/dashicons/ 1005 | * 1006 | * @param string $icon dashicon name 1007 | */ 1008 | function menu_icon( $icon = "dashicons-admin-page" ) { 1009 | 1010 | if ( is_string( $icon ) && stripos( $icon, "dashicons" ) !== false ) { 1011 | 1012 | $this->options["menu_icon"] = $icon; 1013 | 1014 | } else { 1015 | 1016 | // Set a default menu icon 1017 | $this->options["menu_icon"] = "dashicons-admin-page"; 1018 | } 1019 | } 1020 | 1021 | /** 1022 | * Set textdomain 1023 | * 1024 | * @param string $textdomain Textdomain used for translation. 1025 | */ 1026 | function set_textdomain( $textdomain ) { 1027 | $this->textdomain = $textdomain; 1028 | } 1029 | 1030 | /** 1031 | * Updated messages 1032 | * 1033 | * Internal function that modifies the post type names in updated messages 1034 | * 1035 | * @param array $messages an array of post updated messages 1036 | */ 1037 | function updated_messages( $messages ) { 1038 | 1039 | $post = get_post(); 1040 | $singular = $this->singular; 1041 | 1042 | $messages[$this->post_type_name] = array( 1043 | 0 => '', 1044 | 1 => sprintf( __( '%s updated.', $this->textdomain ), $singular ), 1045 | 2 => __( 'Custom field updated.', $this->textdomain ), 1046 | 3 => __( 'Custom field deleted.', $this->textdomain ), 1047 | 4 => sprintf( __( '%s updated.', $this->textdomain ), $singular ), 1048 | 5 => isset( $_GET['revision'] ) ? sprintf( __( '%2$s restored to revision from %1$s', $this->textdomain ), wp_post_revision_title( (int) $_GET['revision'], false ), $singular ) : false, 1049 | 6 => sprintf( __( '%s updated.', $this->textdomain ), $singular ), 1050 | 7 => sprintf( __( '%s saved.', $this->textdomain ), $singular ), 1051 | 8 => sprintf( __( '%s submitted.', $this->textdomain ), $singular ), 1052 | 9 => sprintf( 1053 | __( '%2$s scheduled for: %1$s.', $this->textdomain ), 1054 | date_i18n( __( 'M j, Y @ G:i', $this->textdomain ), strtotime( $post->post_date ) ), 1055 | $singular 1056 | ), 1057 | 10 => sprintf( __( '%s draft updated.', $this->textdomain ), $singular ), 1058 | ); 1059 | 1060 | return $messages; 1061 | } 1062 | 1063 | /** 1064 | * Bulk updated messages 1065 | * 1066 | * Internal function that modifies the post type names in bulk updated messages 1067 | * 1068 | * @param array $messages an array of bulk updated messages 1069 | */ 1070 | function bulk_updated_messages( $bulk_messages, $bulk_counts ) { 1071 | 1072 | $singular = $this->singular; 1073 | $plural = $this->plural; 1074 | 1075 | $bulk_messages[ $this->post_type_name ] = array( 1076 | 'updated' => _n( '%s '.$singular.' updated.', '%s '.$plural.' updated.', $bulk_counts['updated'] ), 1077 | 'locked' => _n( '%s '.$singular.' not updated, somebody is editing it.', '%s '.$plural.' not updated, somebody is editing them.', $bulk_counts['locked'] ), 1078 | 'deleted' => _n( '%s '.$singular.' permanently deleted.', '%s '.$plural.' permanently deleted.', $bulk_counts['deleted'] ), 1079 | 'trashed' => _n( '%s '.$singular.' moved to the Trash.', '%s '.$plural.' moved to the Trash.', $bulk_counts['trashed'] ), 1080 | 'untrashed' => _n( '%s '.$singular.' restored from the Trash.', '%s '.$plural.' restored from the Trash.', $bulk_counts['untrashed'] ), 1081 | ); 1082 | 1083 | return $bulk_messages; 1084 | } 1085 | 1086 | /** 1087 | * Flush 1088 | * 1089 | * Flush rewrite rules programatically 1090 | */ 1091 | function flush() { 1092 | flush_rewrite_rules(); 1093 | } 1094 | } 1095 | --------------------------------------------------------------------------------