Title: Documentation for Advanced Post Manager Author: Matt Wiebe Date: Aug 16, 2011 CSS: docs.css # Advanced Post Manager # ## About ## This is a tool for developers who want to turbo-charge their custom post type listings with metadata, taxonomies, and more. An intuitive interface for adding (and saving) complex filtersets is provided, along with a drag-and-drop interface for choosing and ordering columns to be displayed. Metaboxes are also automatically generated for all your metadata-entry needs. ## Installation ## Put the plugin in your plugins directory in the normal manner. Activate it. Nothing will immediately happen, because this is a tool for developers, and you're going to have to write some code. But not much. ## How to Use ## You're going to want to write your own plugin, or possibly put code in your theme's functions.php file, whichever makes sense for your needs. What you won't want to do is modify the core Advanced Post Manager files in any way. ## Filter Terminology ## You might get confused about the term "filters" in this document. I talk about two different types: 1. The WordPress's plugin system type that get used via `add_filter()` and `apply_filters()`. APM uses these filters extensively to do its work. 2. The APM filter type, which says "filter this list of posts according to a query." This document mostly talks about the 2nd type, but discusses the first type as well. The context should hopefully make it clear. ### Initialization ### Run your code attached to the WordPress `init` hook, like so: add_action('init', 'setup_cpt_filters'); function setup_cpt_filters() { // globalize it so that we can call methods on the returned object global $my_cpt_filters; // We'll show you what goes in this later $filter_array = array(); $my_cpt_filters = tribe_setup_apm('my_post_type', $filter_array ); } Now, you're asking yourself, *what's in that `$filter_array`*? I thought you'd never ask. This array is where you tell APM what you want to make available for filters. This is important. So important it gets its own heading. ### Filter Array ### Once you've mastered everything here, `scratch.php` should serve as a handy quick reference. #### Meta Filters #### Filtering on metadata can be extremely powerful, and much of the power of APM lies here. *Note that meta fields that contain multiple entries per post will behave erratically.* $filter_array = array( // The key is pretty essential. It's used in many places. Choose a unique key, preferably prefixed 'my_meta_filter_key' => array( // required 'name' => 'Name or Title for display purposes', // Tells us this is a metadata filter 'meta' => 'meta_key' ) ); That would be enough to add a filter for just the `meta_key` meta field. But we can do more: $filter_array = array( 'my_meta_filter_key' => array( 'name' => 'Name or Title for display purposes', 'meta' => 'meta_key', // The options field restricts the filter to a specific dropdown of values to query 'options' => array( 'meta_value' => 'Display Title', 'another_meta_value' => 'Another Title' ) ) ); Pretty cool. There's all kinds of metadata in the world though. Maybe my metadata is number-ish. I want my ordering done right: $filter_array = array( 'my_meta_filter_key' => array( 'name' => 'Name or Title for display purposes', 'meta' => 'meta_key', 'options' => array( 'meta_value' => 'Display Title', 'another_meta_value' => 'Another Title' ), // NUMERIC is translated to SIGNED in MySQL-speak. 'cast' => 'NUMERIC' ) ); The above will ensure that 2 comes before 10 when you use ordering or using < or > filters. Allowed values include `BINARY, CHAR, DATE, DATETIME, DECIMAL, SIGNED, TIME, UNSIGNED, NUMERIC`. Consult your nearest database [manual](http://dev.mysql.com/doc/refman/5.1/en/cast-functions.html) and/or nerd for what those mean. That takes care of meta filters, probably the most common thing you'd use this for. Let's also take a look at taxonomy filters. #### Taxonomy Filters #### This could be totally unnecessary, since `tribe_setup_apm` automatically adds associated taxonomies with the `show_ui` flag set to `true`. If, for some reason, you have taxonomies that you're showing the UI for but don't want a filter for, simply do the following on initialization: add_action('init', 'setup_cpt_filters'); function setup_cpt_filters() { // globalize it so that we can call methods on the returned object global $my_cpt_filters; $my_cpt_filters = tribe_setup_apm('my_post_type', $filter_array ); // Disable automatic taxonomy registration $my_cpt_filters->add_taxonomies = false; } And that's all there is to it. Now, either you've disabled automatic taxonomies, or you have taxonomies without the UI showing that you want to add. Let's dive in, assuming we're using `$filter_array` as the second argument on `tribe_setup_apm`: $filter_array = array( // maybe a bunch of meta filters here 'taxonomy_key' => array( // seriously, do you need that documented? 'name' => 'Taxonomy Name', // The taxonomy. First arg of register_taxonomy() 'taxonomy' => 'my_taxonomy', ) ); If you're thinking *that's too easy*, you're right. But it is that easy. The UI will expose the ability to query multiple taxonomy entries at once, making the admin UI much more powerful. (The multiple taxonomies follow the OR pattern, meaning that you'll view "posts" in any of the multiple taxonomy terms selected.) #### Custom Filters #### You're a smart, good-looking developer. You're saying, *yes, but the filtering I want to do doesn't fit into your predefined meta and taxonomy filters.* But of course, we anticipated your needs and provided hooks and filters out the proverbial, um, something. Registration is simple: $filter_array = array( // maybe a bunch of meta and/or taxonomy filters here 'custom_query_key' = array( 'name' => 'My Custom Query', // Your custom_type 'custom_type' => 'my_custom_type', ) ); Now, this won't do much except show a label that does nothing in some places. But you're smart and good-looking, you're ready to code. Let's dive in. We're going to build a post status filter. We're going to encapsulate our funcitonality within its own class. This makes namespacing simpler. There are other ways to do this, and if you're smart and/or good-looking enough to do this differently, use your preferred methodology and feel superior to me. We're going to piece this custom_type plugin together. Here's how we register it: $filter_array = array( 'tribe_post_status' => array( 'name' => 'Status', 'custom_type' => 'post_status' ) ); And here's how we start off our custom_type class: class Tribe_Status_Type { protected $key = 'tribe_post_status'; protected $type = 'post_status'; public function __construct() { // I'm aliasing this here so I have to type less for actions to follow. // Lazy programming = good programming. Usually. $type = $this->type; } } new Tribe_Status_Type; // no need to assign this to a variable since it has no methods involved. Just instatiating, haters. This does nothing at all yet. Let's remedy that. Let's sort out the column display function first, as that will be easy and lure us into the idea that this is simple. Remembers, this stuff is inside the class above. We'll show the whole thing when we're done. // this belongs inside the __construct() function // remember that $type is aliased to $this->type which is the custom_type add_filter( 'tribe_custom_column'.$type, array($this, 'column_value'), 10, 3 ); // every filter instance receives a blank $value. // $column_id aliases the filter key // $post_id is the "post" ID public function column_value($value, $column_id, $post_id) { // what's our post status? $status = get_post_status($post_id); // get the WP post status object. We might get a nicer label. $status_object = get_post_status_object($status); // return the nicer label, or just the raw status if it's not there return ( isset($status_object->label) ) ? $status_object->label : $status; } Now, our columns work, huzzah! Our filter dropdown sadly consists of nothing at all though. Let's fix that. // inside __construct() . If you don't want all 4 variables provided, change '4' to the appropriate number. add_filter( 'tribe_custom_row'.$type, array($this, 'form_row'), 10, 4 ); /** the callback. * @var $return string is blank until we provide something. We need to return something for displaying. * @var $key string The filter key. We may or may not use it. * @var $value array The active array. Contains various data regarding the active state of our filter. We will provide this data later. * @var $filter array The filter array defined earlier. You might have provided additional data in the filter registration that you want now. */ public function form_row($return, $key, $value, $filter) { // Getting the publically available post statuses, or stati, if you will. $stati = get_post_stati(array('show_in_admin_status_list'=>true), 'objects'); $args = array(); // Set up the $args array for our dropdown foreach ( $stati as $k => $object ) { $args[$k] = $object->label; } /** * tribe_select_field() is your friend * * @var $key string The name attribute of the dropdown. Default for anything with options already provided ), 'date_field' => array( 'name' => 'Birthday', 'meta' => '_birthday', 'type' => 'date' // Gives a datepicker. Also automatically sets 'cast' to 'DATE' ) ); You get the idea. The available types are: * `text` Just a text field. Default. * `textarea` A larger text field * `wysiwyg` A visual editor field * `checkbox` A checkbox. Ensure that the `desc` field is used so that there's a label to click on. * `select` A dropdown. Requires an `options` array to populate. Provide `multiple` => true to create a multi-select field. * `checkbox_list` Multiple checkboxes. Requires an `options` array to populate. A good alternative to a multi-select. * `radio` Radio buttons. Requires an `options` array to populate. * `file` Upload a file(s). Saves a reference to the attachment ID. * `image` Upload an image(s). Saves a reference to the attachment ID. * `color` A HEX colorpicker. * `html` Lets you just display some HTML. Useful for when you need to add directions, etc. * `post2post` Produces a UI for associating with another WP post. Requires a `post_type` argument for what should appear in the dropdown. * `time` Provides a timepicker. Timepicker UIs all suck in different ways. You probably shouldn't use this. These types will likely be extensible at some future point. A text field with a good `desc` goes a long way.