Creating a Lightweight Media Tags Plugin with a Custom Taxonomy

We’ve been using Media Tags ( http://wordpress.org/plugins/media-tags/ ) for a while, particularly for the dashboard media management features rather than any of its front end display features. It isn’t really actively maintained anywhere, and since WP 3.5, is just a massive amount of overhead for something that should be able to be accomplished with little more than a custom taxonomy.

In trying to create a working, minimal Media Tags plugin, I’ve used this Question ( How to use taxonomies on attachments with the new Media Library? ) as my primary guide, but it suffers from at least two problems that I’m having issues resolving.

Read More

1) When viewing the tags in the dashboard, I always get a count of 0 displayed, even when there are clearly items tagged with said tag (and clicking on the 0 even takes you to them). I tried updating the update_count_callback value to ‘_update_generic_term_count’ per the other Question I’ve linked to, but it doesn’t give me anything other than 0s.

EDIT: My counts were off due to, not to this code, but to pre-existing entries in the database from code used earlier.

2) When viewing the Media Tags in the backend, you’re always presenting with a ‘View’ link when hovering on an item. That view link always tries to go to a non-dahsboard page that always brings back no posts. Ideally, the View link would just not be present – but it it needs to show up, I’d like it to at least work.

Here’s the code I’m currently trying, but running into the two problems above:

    // Register Custom Taxonomy
function cets_media_tags_init()  {
    $labels = array(
        'name'                       => _x( 'Media Tags', 'Taxonomy General Name', 'text_domain' ),
        'singular_name'              => _x( 'Media Tag', 'Taxonomy Singular Name', 'text_domain' ),
        'menu_name'                  => __( 'Media Tags', 'text_domain' ),
        'all_items'                  => __( 'All Media Tags', 'text_domain' ),
        'parent_item'                => __( 'Parent Media Tag', 'text_domain' ),
        'parent_item_colon'          => __( 'Parent Media Tag:', 'text_domain' ),
        'new_item_name'              => __( 'New Media Tag', 'text_domain' ),
        'add_new_item'               => __( 'Add New Media Tag', 'text_domain' ),
        'edit_item'                  => __( 'Edit Media Tag', 'text_domain' ),
        'update_item'                => __( 'Update Media Tag', 'text_domain' ),
        'separate_items_with_commas' => __( 'Separate Media Tags with commas', 'text_domain' ),
        'search_items'               => __( 'Search Media Tags', 'text_domain' ),
        'add_or_remove_items'        => __( 'Add or remove Media Tags', 'text_domain' ),
        'choose_from_most_used'      => __( 'Choose from the most used Media Tags', 'text_domain' ),
    );

    $args = array(
        'labels'                     => $labels,
        'hierarchical'               => false,
        'public'                     => true,
        'show_ui'                    => true,
        'show_admin_column'          => true,
        'show_in_nav_menus'          => false,
        'show_tagcloud'              => false,
        'update_count_callback'      => '_update_generic_term_count',
        'query_var'                  => false,
        'rewrite'                    => false,
    );

    register_taxonomy( 'media-tags', 'attachment', $args );
}

// Hook into the 'init' action
add_action( 'init', 'cets_media_tags_init', 0 );

Related posts

1 comment

  1. Sadly, I haven’t found the flaw in your code as far as update counts. Copy and paste mine for comparison (stick it in a plugin you can easily deactivate). This works like a charm, with all counts updating correctly:

    class ZGAttachmentTags {
        const SLUG = 'attachment-tags';
    
        function __construct() {
            add_action( 'init', array( $this, 'register_custom_taxonomy' ) );
        }
    
    
        function register_custom_taxonomy() {
            register_taxonomy(
                self::SLUG,
                array( 'attachment' ),
                array(
                    'label' => __( 'Media Tags' ),
                    'labels' => array(
                      'name'=> __( 'Media Tags' ),
                      'singular_name'=> __( 'Media Tags' )
                    ),
                    'hierarchical' => false,
                    'update_count_callback' => '_update_generic_term_count'
                )
            );
        }
    }
    
    $zg_attachment_tags_plugin = new ZGAttachmentTags();
    

    Clearly I’m using a pared-down version of register_taxonomy, laving a lot of the defaults alone. Perhaps it’s one of your other parameters that’s causing the issue. Or I’m misunderstanding the question.

    Regarding the “View” link – by default, taxonomy archive queries only include posts whose status are public (usually ‘publish’ and ‘private’ – if your user is logged in). Attachments don’t get published – they received the ‘inherit’ status. The solution then is to modify either the query or the request to include posts with the status of ‘inherit’.

    The simplest way to do this is to use the parse_query filter, which is run after the query vars have all been parsed into the main query object, and just before the get_posts() method is run that ultimately generates the SQL request. You do that like this:

    add_filter( 'parse_query', 'parse_query__attachment_status' );
    
    function parse_query__attachment_status( $query_obj ){
        // Make sure we don't contaminate any other queries...
        if ( $query_obj->query['attachment-tags'] ){
            $statuses = get_post_stati( array( 'public' => true ) ) ;
            $statuses['inherit'] = 'inherit'; // push 'inherit' status onto the list
            $query_obj->set( 'post_status', array_keys( $statuses ) );
        }
    
        return $query_obj;
    }
    

    Note that for simplicity, I included this example as if it were a standalone piece of code in your functions.php for example. But you could (and should) roll it into your class and therefore use the class constant for the taxonomy slug (self::SLUG). Also don’t forget to modify your add_filter() call to use the array form.

    The only problem with this approach (it’s not really a problem I guess) is that WordPress automatically does some funkiness when it detects that you’re querying an attachment. It will add a JOIN to wp_posts to query the parent post, and adds a clause to the WHERE clause that checks the parent’s post_status, as well as the child’s status.

    This means that you might get some duplication if, in fact, some of the media items have been included in posts. One way to avoid this is to hard code the status as ‘inherit’ only (rather than using the show-off get_post_stati() approach that I used, primarily because I admire the use of the latin plural in a function name.).

    The other thing you could do, if you’re brave at heart, is eschew the parse_query filter in favour of the posts_clauses filter, and rewrite the WHERE and JOIN sql yourself. This would take a bit of creative search-and-replace, and to me seemed like too much trouble for this answer, though it is undoubtedly the most correct way to go, and the most future-proof.

Comments are closed.