From 58243a8bc0f9964f83f720661e738413bc91da42 Mon Sep 17 00:00:00 2001 From: Joe Grainger Date: Tue, 16 Aug 2016 13:45:44 +0100 Subject: [PATCH] initial commit --- .gitattributes | 1 + .gitignore | 4 + LICENSE | 21 ++ README.md | 270 +++++++++++++++++ composer.json | 20 ++ examples/books.php | 39 +++ src/Columns.php | 139 +++++++++ src/PostType.php | 709 +++++++++++++++++++++++++++++++++++++++++++++ src/Taxonomy.php | 151 ++++++++++ 9 files changed, 1354 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 composer.json create mode 100644 examples/books.php create mode 100644 src/Columns.php create mode 100644 src/PostType.php create mode 100644 src/Taxonomy.php diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..e9d9d40 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +/examples export-ignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5826402 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/vendor +composer.phar +composer.lock +.DS_Store diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..0da0591 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 jjgrainger + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..5ef25d6 --- /dev/null +++ b/README.md @@ -0,0 +1,270 @@ +# PostTypes v1.0 + +Simple WordPress custom post types. + +## Installation + +#### Install with composer + +Run the following in your terminal to install PostTypes with [Composer](https://getcomposer.org/). + +``` +$ composer require jjgrainger/posttypes +``` + +As PostTypes uses [PSR-4](http://www.php-fig.org/psr/psr-4/) autoloading you will need to use Composers autoloader. Below is a basic example of getting started with the class, though your setup maybe different depending on how you are using composer. + +```php +require __DIR__ . '/vendor/autoload.php'; + +use PostTypes\PostType; + +$books = new PostType('book'); +``` + +See Composers [basic usage](https://getcomposer.org/doc/01-basic-usage.md#autoloading) guide for details on working Composer and autoloading. + +## Post Types + +#### Create a new Post Type + +A new post type can be created by simply passing the post types name to the class constructor. + +```php +$books = new PostType('book'); +``` + +#### Defining names + +The post type labels and slugs are automatically generated from the post type name, however, if you need to define these manually pass an array of names to the post types constructor. + +```php +$names = [ + 'name' => 'book', + 'singular' => 'Book', + 'plural' => 'Books', + 'slug' => 'books' +]; + +$books = new PostType($names); +``` + +It can accept the following names: + +* `name` is the post type name (*required*, singular, lowercase, underscores) +* `singular` is the singular label for the post type (Book, Person) +* `plural` is the plural label for the post type (Books, People) +* `slug` is the post type slug used in the permalinks (plural, lowercase, hyphens) + +The only required name is the post types `name`. + +#### Adding options + +You can define the options for the post type by passing an array as the second argument in the class constructor. + +```php +$options = [ + 'has_archive' => false +]; + +$books = new PostType('book', $options); +``` + +All available options are on the [WordPress Codex](https://codex.wordpress.org/Function_Reference/register_post_type) + +#### Exisiting Post Types + +To work with exisiting post types simple pass the post type name into the object. Be careful using global variables (i.e `$post`) which can lead to unwanted results. + +```php +$blog = new PostType('post'); +``` + +## Add Taxonomies + +Adding taxonomies to a post type is easily achieved by using the `taxonomy()` method. + +#### Create new taxonomy + +To create a new taxonomy simply pass the taxonomy name to the `taxonomy()` method. Labels and the taxonomy slug are generated from the taxonomy name. + +```php +$books->taxonomy('genre'); +``` + +#### Defining names + +You can define names by passing an array as the first argument. Only the `name` is required. + +* `name` is the post type name +* `singular` is the singular label for the post type +* `plural` is the plural label for the post type +* `slug` is the post type slug used in the permalinks + +```php +$names = [ + 'name' => 'genre', + 'singular' => 'Genre', + 'plural' => 'Genres', + 'slug' => 'genres' +]; + +$books->taxonomy($names); +``` + +#### Adding options + +You can further customise taxonomies by passing an array of options as the second argument to the method. + +```php +$options = [ + 'heirarchical' => false +]; + +$books->taxonomy('genre', $options); +``` + +All available options are on the [WordPress Codex](https://codex.wordpress.org/Function_Reference/register_taxonomy) + +#### Adding Exisiting Taxonomies + +You can add existing taxonomies by passing the taxonomy name to the `taxonomy()` method. This works with custom taxonomies too. You only need to pass the options/names for the taxonomy **once**, afterwards you only need to pass the taxonomy name. + +```php +$books->taxonomy('post_tag'); +``` + +## Admin Edit Screen + +#### Filters + +Set the taxonomy filters on the admin edit screen by passing an array to the `filters()` method + +```php +$books->filters(['genres', 'category']); +``` + +The order of the filters are set by the order of the items in the array. An empty array will remove all dropdown filters. + +#### Columns + +###### Adding Columns + +You can add columns to the admin edit screen by passing an array of slugs and labels to the `add()` method. + +```php +// add multiple columns and set their labels +$books->columns()->add([ + 'rating' => __('Rating'), + 'price' => __('Price') +]); +``` + +###### Hiding Columns + +You can hide columns by passing the column slug to the `hide()` method. You can hide multiple columns by passing an array of column slugs. + +```php +$books->columns()->hide('author'); + +$books->columns()->hide(['author', 'date']); +``` + +###### Set Columns + +You can force set all the columns to display on the admin page with the `set()` method by passing an array of the column slugs and labels. + +```php +$books->columns()->set([ + 'cb' => '', + 'title' => __("Title"), + 'genre' => __("Genres"), + 'rating' => __("Rating"), + 'date' => __("Date") +]); +``` + +###### Column Order + +After hiding and adding columns you may want to rearrange the column order. To do this use the `order()` method. You only have to pass through an array of the columns you want to reposition, not all of them. Their positions are based on a zero based index. + +```php +$books->columns()->order([ + 'rating' => 2, + 'genre' => 4 +]); +``` + +###### Populating Columns + +Columns that are automatically populated with correct slug + +* `post_id` - the post id +* `title` - the posts title with edit links +* `author` - the posts author +* `date` - the posts dates +* `{taxonomy_name}` - a list of the taxonomy terms attached to the post +* `thumbnail` - the post featured image +* `meta_{meta_key}` - the post meta for that key + +```php +$books->columns()->populate('rating', function($column, $post_id) { + echo get_post_meta($post_id, 'rating', true) . '/10'; +}); +``` + +###### Sorting Columns + +You can choose which custom columns are sortable with the `sortable()` method. This method accepts an array of column slugs and an array of sorting options. + +The first option is the `meta_key` to sort the colums by. + +The second option is whether to order the items numerically (`true`) or alphabetically (`false`) by default. + +```php +// will make both the price and rating columns sortable and ordered numerically +$books->columns()->sortable([ + 'price' => ['price', true], + 'rating' => ['rating', true] +]); +``` + +#### Menu Icons + +With WordPress 3.8 comes [Dashicons](https://developer.wordpress.org/resource/dashicons/) an icon font you can use with your custom post types. + +```php +$books->icon('dashicons-book-alt'); +``` + +### Flush Rewrite Rules + +You can programmatically recreate the sites rewrite rules with the `flush()` method. +This is an expensive operation and should be used with caution, see [codex](https://codex.wordpress.org/Function_Reference/flush_rewrite_rules) for more. + +```php +$books->flush(); +``` + +### Translation + +The class is setup for translation, but if you need to set your own textdomain to work with your theme or plugin use the `translation()` method: + +```php +$books->translation('your-textdomain'); +``` + +## Notes + +* The class has no methods for making custom fields for post types, use [Advanced Custom Fields](http://advancedcustomfields.com) +* The books example used in the README.md can be found in the [examples/books.php](examples/books.php) +* Licensed under the [MIT License](https://github.com/jjgrainger/wp-posttypes/blob/master/LICENSE) +* Maintained under the [Semantic Versioning Guide](http://semver.org) + +## Author + +**Joe Grainger** + +* [http://jjgrainger.co.uk](http://jjgrainger.co.uk) +* [http://twitter.com/jjgrainger](http://twitter.com/jjgrainger) + diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..a96e22c --- /dev/null +++ b/composer.json @@ -0,0 +1,20 @@ +{ + "name": "jjgrainger/posttypes", + "description": "Simple WordPress custom post types.", + "homepage": "https://github.com/jjgrainger/posttype", + "license": "MIT", + "authors": [ + { + "name": "Joe Grainger", + "email": "hello@jjgrainger.co.uk", + "homepage": "http://jjgrainger.co.uk" + } + ], + "minimum-stability": "dev", + "require": { + "php": ">=5.3.0" + }, + "autoload": { + "psr-4": { "PostTypes\\": "src/" } + } +} diff --git a/examples/books.php b/examples/books.php new file mode 100644 index 0000000..4996183 --- /dev/null +++ b/examples/books.php @@ -0,0 +1,39 @@ +taxonomy('genre'); + +// Hide the date and author columns +$books->columns()->hide(['date', 'author']); + +// add a price and rating column +$books->columns()->add([ + 'rating' => __('Rating'), + 'price' => __('Price') +]); + +// Populate the custom column +$books->columns()->populate('rating', function($column, $post_id) { + echo get_post_meta($post_id, 'rating') . '/10'; +}); + +// Populate the custom column +$books->columns()->populate('price', function($column, $post_id) { + echo '£' . get_post_meta($post_id, 'price'); +}); + +// Set sortable columns +$books->columns()->sortable([ + 'price' => ['price', true], + 'rating' => ['rating', true] +]); + +// Set the Books menu icon +$books->icon('dashicons-book-alt'); diff --git a/src/Columns.php b/src/Columns.php new file mode 100644 index 0000000..74d129d --- /dev/null +++ b/src/Columns.php @@ -0,0 +1,139 @@ +items = $columns; + } + + /** + * Add a new column + * @param string $column the slug of the column + * @param string $label the label for the column + */ + public function add($columns, $label = null) + { + + if (!is_array($columns)) { + $columns = [$columns => $label]; + } + + foreach ($columns as $column => $label) { + if (is_null($label)) { + $label = str_replace(['_', '-'], ' ', ucfirst($column)); + } + + $this->add[$column] = $label; + } + } + + /** + * Add a column to hide + * @param string $column the slug of the column to hdie + */ + public function hide($columns) + { + if (!is_array($columns)) { + $columns = [$columns]; + } + + foreach ($columns as $column) { + $this->hide[] = $column; + } + } + + /** + * Set a custom callback to populate a column + * @param string $column the column slug + * @param mixed $callback callback function + */ + public function populate($column, $callback) + { + $this->populate[$column] = $callback; + } + + /** + * Define the postion for a columns + * @param string $columns an array of columns + */ + public function order($columns) + { + foreach ($columns as $column => $position) { + $this->positions[$column] = $position; + } + } + + /** + * Set columns that are sortable + * @param string $column the slug of the column + * @param string $meta_value the meta_value to orderby + * @param boolean $is_num whether to order by string/number + */ + public function sortable($sortable) + { + foreach ($sortable as $column => $options) { + $this->sortable[$column] = $options; + } + } +} + diff --git a/src/PostType.php b/src/PostType.php new file mode 100644 index 0000000..a13c7f2 --- /dev/null +++ b/src/PostType.php @@ -0,0 +1,709 @@ +names($names); + + // set the options + $this->options($options); + + // create a columns object + $this->columns = new Columns(); + + // add actions and filters + $this->initialize(); + } + + /** + * Set the post type names. + * + * @param mixed $names a post type name as string or an array of names + */ + public function names($names) + { + + // if a string is passed + if (!is_array($names)) { + $names = ['name' => $names]; + } + + // set the postTypeName + $this->postTypeName = $names['name']; + + // an array of required names + $required = [ + // 'name', + 'singular', + 'plural', + 'slug', + ]; + + foreach ($required as $key) { + + // if the name has not been passed, generate it + if (!isset($names[$key])) { + + // if it is the singular/plural make the post type name human friendly + if ($key === 'singular' || $key === 'plural') { + $name = ucwords(strtolower(str_replace(['-', '_'], ' ', $this->postTypeName))); + + // if plural add an s + if ($key === 'plural') { + $name .= 's'; + } + + // if the slug, slugify the post type name + } elseif ($key === 'slug') { + $name = strtolower(str_replace([' ', '_'], '-', $this->postTypeName)); + } + + // otherwise use the name passed + } else { + $name = $names[$key]; + } + + // set the name + $this->$key = $name; + } + } + + /** + * Set the post type options. + * + * @param array $options an array of post type options + */ + public function options($options) + { + + // default labels. + $labels = [ + 'name' => sprintf(__('%s', $this->textdomain), $this->plural), + 'singular_name' => sprintf(__('%s', $this->textdomain), $this->singular), + 'menu_name' => sprintf(__('%s', $this->textdomain), $this->plural), + 'all_items' => sprintf(__('%s', $this->textdomain), $this->plural), + 'add_new' => __('Add New', $this->textdomain), + 'add_new_item' => sprintf(__('Add New %s', $this->textdomain), $this->singular), + 'edit_item' => sprintf(__('Edit %s', $this->textdomain), $this->singular), + 'new_item' => sprintf(__('New %s', $this->textdomain), $this->singular), + 'view_item' => sprintf(__('View %s', $this->textdomain), $this->singular), + 'search_items' => sprintf(__('Search %s', $this->textdomain), $this->plural), + 'not_found' => sprintf(__('No %s found', $this->textdomain), $this->plural), + 'not_found_in_trash' => sprintf(__('No %s found in Trash', $this->textdomain), $this->plural), + 'parent_item_colon' => sprintf(__('Parent %s:', $this->textdomain), $this->singular), + ]; + + // default options. + $defaults = [ + 'labels' => $labels, + 'public' => true, + 'rewrite' => [ + 'slug' => $this->slug, + ], + ]; + + // merge user submitted options with defaults. + $this->options = array_replace_recursive($defaults, $options); + } + + /** + * Register a taxonomy to the Post Type. + * + * @see http://codex.wordpress.org/Function_Reference/register_taxonomy + * + * @param mixed $names The names for the taxonomy. + * @param array $options Taxonomy options. + */ + public function taxonomy($names, $options = []) + { + // if only the name is passed, create an array + if (!is_array($names)) { + $names = ['name' => $names]; + } + + // add taxonomy to the list + $this->taxonomies[] = $names['name']; + + // if taxonomy exists, just add the name + if (taxonomy_exists($names['name'])) { + $this->existingTaxonomies[] = $names['name']; + + // else create a new taxonomy + } else { + $this->newTaxonomies[$names['name']] = new Taxonomy($names, $options); + $this->newTaxonomies[$names['name']]->textdomain($this->textdomain); + } + } + + /** + * Set which filters appear on the admin table page for the post type. + * + * @param array $filters An array of taxonomy filters to display. + */ + public function filters($filters) + { + $this->filters = $filters; + } + + /** + * the columns object for the post type. + * + * @return PostType\Columns; + */ + public function columns() + { + return $this->columns; + } + + /** + * Use this function to set the menu icon in the admin dashboard. Since WordPress v3.8 + * dashicons are used. For more information see @link http://melchoyce.github.io/dashicons/. + * + * @param string $icon dashicon name + */ + public function icon($icon) + { + $this->options['menu_icon'] = $icon; + } + + /** + * Flush rewrite rules programatically. + */ + public function flush() + { + flush_rewrite_rules(); + } + + /** + * set the textdomain for the post type. + * + * @param string $textdomain Textdomain used for translation. + */ + public function translation($textdomain) + { + $this->textdomain = $textdomain; + } + + /** + * bind methods to WordPress actions and filters. + */ + public function initialize() + { + // register taxonomies. + add_action('init', array(&$this, 'registerTaxonomies')); + + // register the post type. + add_action('init', array(&$this, 'registerPostType')); + + // register existing taxonomies. + add_action('init', array(&$this, 'registerExistingTaxonomies')); + + // add taxonomy to admin edit columns. + add_filter('manage_edit-'.$this->postTypeName.'_columns', array(&$this, 'modifyColumns')); + + // populate the taxonomy columns with the posts terms. + add_action('manage_'.$this->postTypeName.'_posts_custom_column', array(&$this, 'populateColumns'), 10, 2); + + // add filter select option to admin edit. + add_action('restrict_manage_posts', array(&$this, 'modifyFilters')); + + // run filter to make columns sortable. + add_filter('manage_edit-'.$this->postTypeName.'_sortable_columns', array(&$this, 'setSortableColumns')); + + // run action that sorts columns on request. + add_action('load-edit.php', array(&$this, 'loadEdit')); + + // rewrite post update messages + add_filter('post_updated_messages', array(&$this, 'modifyUpdatedMessages')); + add_filter('bulk_post_updated_messages', array(&$this, 'modifyBulkUpdateMessages'), 10, 2); + } + + /** + * Register the post type. + */ + public function registerPostType() + { + // check that the post type doesn't already exist. + if (!post_type_exists($this->postTypeName)) { + + // register the post type. + register_post_type($this->postTypeName, $this->options); + } + } + + /** + * Register the post type taxonomies. + */ + public function registerTaxonomies() + { + foreach ($this->newTaxonomies as $taxonomy_name => $tax) { + // register the taxonomy with Wordpress + register_taxonomy($taxonomy_name, $this->postTypeName, $tax->options); + } + } + + /** + * Register existing taxonomies to the post type. + */ + public function registerExistingTaxonomies() + { + foreach ($this->existingTaxonomies as $taxonomy_name) { + register_taxonomy_for_object_type($taxonomy_name, $this->postTypeName); + } + } + + /** + * Modify the post type admin filters. + * + * @param string $postType the current post type being viewed + */ + public function modifyFilters($postType) + { + global $wp_query; + + $filters = []; + + // must set this to the post type you want the filter(s) displayed on. + if ($postType == $this->postTypeName) { + + // if we have user supplied filters use them + if (!empty($this->filters)) { + $filters = $this->filters; + + // otherwise add taxonomies as fitlers + } elseif (!empty($this->taxonomies)) { + foreach ($this->taxonomies as $taxonomy) { + $filters[] = $taxonomy; + } + } + + // foreach of the taxonomies we want to create filters for + foreach ($filters as $taxonomy_name) { + + // object for taxonomy, doesn't contain the terms + $tax = get_taxonomy($taxonomy_name); + + // get taxonomy terms and order by name + $args = [ + 'orderby' => 'name', + 'hide_empty' => false, + ]; + + // get taxonomy terms + $terms = get_terms($taxonomy_name, $args); + + // if we have terms + if ($terms) { + // set up select box + printf('   '; + } + } + } + } + + /** + * Modify the post type columns with the columns object. + * + * @param array $columns an array of admin table columns + * @return array an array of admin tbale columns + */ + public function modifyColumns($columns) + { + // if the user has supplied a columns array use that + if (!empty($this->columns()->items)) { + return $this->columns()->items; + } + + // otherwise add the taxonomies to the columns + if (!empty($this->taxonomies)) { + // determine what column the taxomies follow + if ($this->postTypeName === 'post' || in_array('post_tag', $this->taxonomies)) { + $after = 'tags'; + } elseif (in_array('categories', $this->taxonomies)) { + $after = 'categories'; + } elseif (post_type_supports($this->postTypeName, 'author')) { + $after = 'author'; + } else { + $after = 'title'; + } + + // create a new columns array + $newColumns = []; + + foreach ($columns as $key => $label) { + $newColumns[$key] = $label; + + if ($key === $after) { + foreach ($this->taxonomies as $taxonomy) { + if ($taxonomy !== 'category' && $taxonomy !== 'post_tag') { + // get the taxonomy object for labels + $taxonomy_object = get_taxonomy($taxonomy); + + // column key is the slug, value is friendly name + $newColumns[$taxonomy] = sprintf(__('%s', $this->textdomain), $taxonomy_object->labels->name); + } + } + } + } + + // set columns to new ones with taxonomies + $columns = $newColumns; + } + + // if user has made added custom columns + foreach ($this->columns()->add as $key => $label) { + + // if user has assigned a custom position + if (isset($this->columns()->positions[$key])) { + $position = $this->columns()->positions[$key]; + + $start = array_slice($columns, 0, $position, true); + $end = array_slice($columns, $position, count($columns) - 1, true); + + $columns = $start + [$key => $label] + $end; + } else { + $columns[$key] = $label; + } + } + + // any columns the user has hidden + foreach ($this->columns()->hide as $key) { + unset($columns[$key]); + } + + // overide with new columns + return $columns; + } + + /** + * populate the columns for the admin table. + * + * @param string $column the column name + * @param int $post_id the post id + */ + public function populateColumns($column, $post_id) + { + // get wordpress $post object + global $post; + + // use custom populate + if (isset($this->columns()->populate[$column])) { + call_user_func_array($this->columns()->populate[$column], [$column, $post_id]); + + return; + } + + switch ($column) { + // if column is a taxonomy associated with the post type + case taxonomy_exists($column): + // get the taxonomy for the post + $terms = get_the_terms($post_id, $column); + + // if we have terms + if (!empty($terms)) { + $output = []; + + // loop through each term, linking to the 'edit posts' page for the specific term + foreach ($terms as $term) { + + // output is an array of terms associated with the post + $output[] = sprintf( + '%s', // Define link format + esc_url(add_query_arg(['post_type' => $post->post_type, $column => $term->slug], 'edit.php')), // Create filter url + esc_html(sanitize_term_field('name', $term->name, $term->term_id, $column, 'display')) // Create friendly term name + ); + } + + // join the terms, separating them with a comma + echo implode(', ', $output); + + // if no terms found. + } else { + // get the taxonomy object for labels + $taxonomy_object = get_taxonomy($column); + + // echo no terms. + printf(__('No %s', $this->textdomain), $taxonomy_object->labels->name); + } + break; + // if column is for the post ID + case 'post_id': + echo $post->ID; + break; + // if the column is prepended with 'meta_', this will automagically retrieve the meta values and display them + case preg_match('/^meta_/', $column) ? true : false: + // meta_book_author (meta key = book_author) + $x = substr($column, 5); + + $meta = get_post_meta($post->ID, $x); + + echo implode(', ', $meta); + break; + // if the column is post thumbnail + case 'icon': + // create the edit link + $link = esc_url(add_query_arg(['post' => $post->ID, 'action' => 'edit'], 'post.php')); + + // if it post has a featured image + if (has_post_thumbnail()) { + // display post featured image with edit link + echo ''; + the_post_thumbnail(array(60, 60)); + echo ''; + } else { + // display default media image with link + echo ''.$post->post_title.''; + } + break; + } + } + + /** + * Set the columns that are sortable. + * + * @param array $columns the sortable columns + * @return array an array of sortable columns + */ + public function setSortableColumns($columns) + { + if (!empty($this->columns()->sortable)) { + // for each sortable column + foreach ($this->columns()->sortable as $column => $values) { + // make an array to merge into wordpress sortable columns + $sortable_columns[$column] = $values[0]; + } + // merge sortable columns array into wordpress sortable columns + $columns = array_merge($sortable_columns, $columns); + } + + return $columns; + } + + /** + * add filter to sort sortable columns. + */ + public function loadEdit() + { + // Run filter to sort columns when requested + add_filter('request', [&$this, 'sortColumns']); + } + + /** + * Sort data via the requested column. + * + * @param array $vars the request query vars + * @return array the request query vars + */ + public function sortColumns($vars) + { + // cycle through all sortable columns submitted by the user + foreach ($this->columns()->sortable as $column => $values) { + + // retrieve the meta key from the user submitted array of sortable columns + $meta_key = $values[0]; + + // if the optional parameter is set and is set to true + if (isset($values[1]) && true === $values[1]) { + // vaules needed to be ordered by integer value + $orderby = 'meta_value_num'; + } else { + // values are to be order by string value + $orderby = 'meta_value'; + } + + // check if we're viewing this post type + if (isset($vars['post_type']) && $this->postTypeName == $vars['post_type']) { + // find the meta key we want to order posts by + if (isset($vars['orderby']) && $meta_key === $vars['orderby']) { + $add = []; + + if (!taxonomy_exists($meta_key)) { + $add = [ + 'meta_key' => $meta_key, + 'orderby' => $orderby, + ]; + } + + // merge the query vars with our custom variables + $vars = array_merge( + $vars, + $add + ); + } + } + } + + return $vars; + } + + /** + * Internal function that modifies the post type names in updated messages. + * + * @param array $messages an array of post updated messages + * + * @return array modified bulk updated messages + */ + public function modifyUpdatedMessages($messages) + { + $post = get_post(); + $singular = $this->singular; + + $messages[$this->postTypeName] = [ + 0 => '', + 1 => sprintf(__('%s updated.', $this->textdomain), $singular), + 2 => __('Custom field updated.', $this->textdomain), + 3 => __('Custom field deleted.', $this->textdomain), + 4 => sprintf(__('%s updated.', $this->textdomain), $singular), + 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, + 6 => sprintf(__('%s updated.', $this->textdomain), $singular), + 7 => sprintf(__('%s saved.', $this->textdomain), $singular), + 8 => sprintf(__('%s submitted.', $this->textdomain), $singular), + 9 => sprintf( + __('%2$s scheduled for: %1$s.', $this->textdomain), + date_i18n(__('M j, Y @ G:i', $this->textdomain), strtotime($post->post_date)), + $singular + ), + 10 => sprintf(__('%s draft updated.', $this->textdomain), $singular), + ]; + + return $messages; + } + + /** + * Internal function that modifies the post type names in bulk updated messages. + * + * @param array $messages an array of bulk updated messages + * + * @return array modified bulk updated messages + */ + public function modifyBulkUpdateMessages($bulk_messages, $bulk_counts) + { + $singular = $this->singular; + $plural = $this->plural; + + $bulk_messages[$this->postTypeName] = [ + 'updated' => _n('%s '.$singular.' updated.', '%s '.$plural.' updated.', $bulk_counts['updated']), + 'locked' => _n('%s '.$singular.' not updated, somebody is editing it.', '%s '.$plural.' not updated, somebody is editing them.', $bulk_counts['locked']), + 'deleted' => _n('%s '.$singular.' permanently deleted.', '%s '.$plural.' permanently deleted.', $bulk_counts['deleted']), + 'trashed' => _n('%s '.$singular.' moved to the Trash.', '%s '.$plural.' moved to the Trash.', $bulk_counts['trashed']), + 'untrashed' => _n('%s '.$singular.' restored from the Trash.', '%s '.$plural.' restored from the Trash.', $bulk_counts['untrashed']), + ]; + + return $bulk_messages; + } +} diff --git a/src/Taxonomy.php b/src/Taxonomy.php new file mode 100644 index 0000000..cf0bfbc --- /dev/null +++ b/src/Taxonomy.php @@ -0,0 +1,151 @@ +setNames($names); + + // set the options for the taxonomy + $this->setOptions($options); + } + + /** + * Set the required names for the taxonomy + * @param mixed $names an array/string of taxonomy names + */ + public function setNames($names) + { + if (!is_array($names)) { + $names = ['name' => $names]; + } + + $required = [ + // 'taxonomyName', + 'singular', + 'plural', + 'slug', + ]; + + foreach ($required as $key) { + + // if the name has not been passed, generate it + if (!isset($names[$key])) { + + // if it is the singular/plural make the post type name human friendly + if ($key === 'singular' || $key === 'plural') { + $name = ucwords(strtolower(str_replace('-', ' ', str_replace('_', ' ', $names['name'])))); + + // if plural add an s + if ($key === 'plural') { + $name .= 's'; + } + + // if the slug, slugify the post type name + } elseif ($key === 'slug') { + $name = strtolower(str_replace([' ', '_'], '-', $names['name'])); + } + + // otherwise use the name passed + } else { + $name = $names[$key]; + } + + // set the name + $this->$key = $name; + } + } + + /** + * Set the options for the taxonomy + * @param array $options an array of options for the taxonomy + */ + public function setOptions($options) + { + // default labels + $labels = [ + 'name' => sprintf(__('%s', $this->textdomain), $this->plural), + 'singular_name' => sprintf(__('%s', $this->textdomain), $this->singular), + 'menu_name' => sprintf(__('%s', $this->textdomain), $this->plural), + 'all_items' => sprintf(__('All %s', $this->textdomain), $this->plural), + 'edit_item' => sprintf(__('Edit %s', $this->textdomain), $this->singular), + 'view_item' => sprintf(__('View %s', $this->textdomain), $this->singular), + 'update_item' => sprintf(__('Update %s', $this->textdomain), $this->singular), + 'add_new_item' => sprintf(__('Add New %s', $this->textdomain), $this->singular), + 'new_item_name' => sprintf(__('New %s Name', $this->textdomain), $this->singular), + 'parent_item' => sprintf(__('Parent %s', $this->textdomain), $this->plural), + 'parent_item_colon' => sprintf(__('Parent %s:', $this->textdomain), $this->plural), + 'search_items' => sprintf(__('Search %s', $this->textdomain), $this->plural), + 'popular_items' => sprintf(__('Popular %s', $this->textdomain), $this->plural), + 'separate_items_with_commas' => sprintf(__('Seperate %s with commas', $this->textdomain), $this->plural), + 'add_or_remove_items' => sprintf(__('Add or remove %s', $this->textdomain), $this->plural), + 'choose_from_most_used' => sprintf(__('Choose from most used %s', $this->textdomain), $this->plural), + 'not_found' => sprintf(__('No %s found', $this->textdomain), $this->plural), + ]; + + // default options + $defaults = [ + 'labels' => $labels, + 'hierarchical' => true, + 'rewrite' => [ + 'slug' => $this->slug, + ], + ]; + + // merge default options with user submitted options + $this->options = array_replace_recursive($defaults, $options); + } + + /** + * Set the textdomain for translation + * @param string $textdomain the textdomain + */ + public function textdomain($textdomain) + { + $this->textdomain = $textdomain; + } +}