├── README.md ├── js └── taxonomy-term-image.js └── taxonomy-term-image.php /README.md: -------------------------------------------------------------------------------- 1 | # Taxonomy Term Image 2 | 3 | An example plugin for adding an image upload field to taxonomy term edit pages, and an example of the new taxonomy term meta data added in WordPress 4.4. This example IS NOT compatible with any version of WordPress lower than 4.4, it is meant to be used as an example only. 4 | 5 | ### How to use within a theme or plugin: 6 | 7 | **Setup file:** 8 | 9 | 1. Delete plugin meta data at the top of taxonomy-term-image.php 10 | 1. include_once taxonomy-term-image.php 11 | 12 | ### Hooks 13 | 14 | **filter `taxonomy-term-image-taxonomy`**: 15 | 16 | Change the taxonomy targeted by the plugin. By default, the `category` taxonomy is the only taxonomy targeted. You can change this to tags if you'd like following the example below: 17 | 18 | ```php 19 | function the_term_image_taxonomy( $taxonomy ) { 20 | // use for tags instead of categories 21 | return 'post_tag'; 22 | } 23 | add_filter( 'taxonomy-term-image-taxonomy', 'the_term_image_taxonomy' ); 24 | ``` 25 | 26 | Alternatively, the plugin can target more than one taxonomy by providing it an array of taxonomy slugs: 27 | 28 | ```php 29 | function the_term_image_taxonomy( $taxonomy ) { 30 | // use for tags and categories 31 | return array( 'post_tag', 'category' ); 32 | } 33 | add_filter( 'taxonomy-term-image-taxonomy', 'the_term_image_taxonomy' ); 34 | ``` 35 | 36 | **filter `taxonomy-term-image-labels`**: 37 | 38 | Change the field and button text. 39 | 40 | ```php 41 | function the_taxonomy_term_image_labels( $labels ) { 42 | $labels['fieldTitle'] = __( 'My Super Rad Plugin', 'yourdomain' ); 43 | $labels['fieldDescription'] = __( 'This plugin is cool, and does neat stuff.', 'yourdomain' ); 44 | 45 | return $labels; 46 | } 47 | add_filter( 'taxonomy-term-image-labels', 'the_taxonomy_term_image_labels' ); 48 | ``` 49 | 50 | **filter `taxonomy-term-image-meta-key`**: 51 | 52 | Change the meta key used to save the image ID in the term meta data 53 | 54 | ```php 55 | function the_taxonomy_term_image_meta_key( $option_name ) { 56 | // store in term meta where term meta key is = 'my_term_meta_key' 57 | return 'my_term_meta_key'; 58 | } 59 | add_filter( 'taxonomy-term-image-meta-key', 'the_taxonomy_term_image_meta_key' ); 60 | ``` 61 | 62 | **filter `taxonomy-term-image-js-dir-url`**: 63 | 64 | Change where the js file is located. (no trailing slash) 65 | 66 | ```php 67 | function my_taxonomy_term_image_js_dir_url( $option_name ) { 68 | // change the js directory to a subdirectory of this hook 69 | return plugin_dir_url( __FILE__ ) . '/js'; 70 | } 71 | add_filter( 'taxonomy-term-image-js-dir-url', 'my_taxonomy_term_image_js_dir_url' ); 72 | ``` 73 | 74 | **show image on archive template** 75 | 76 | Term Image IDs are automatically attached to terms that are passed through the `get_term` and `get_terms` filters as the `->term_image` property. 77 | 78 | ```php 79 | $term = get_term( 123, 'category' ); 80 | 81 | if ( $term->term_image ) { 82 | echo wp_get_attachment_image( $term->term_image, 'full' ); 83 | } 84 | ``` 85 | 86 | In order to retrieve the term image on an archive page: 87 | 88 | ```php 89 | $term = get_queried_object(); 90 | 91 | if ( $term->term_image ) { 92 | echo wp_get_attachment_image( $term->term_image, 'full' ); 93 | } 94 | ``` 95 | 96 | ### References: 97 | 98 | **Articles:** 99 | 100 | * [Using Media Uploader in plugins](http://mikejolley.com/2012/12/using-the-new-wordpress-3-5-media-uploader-in-plugins/) 101 | * [Introduction to WordPress term meta](http://themehybrid.com/weblog/introduction-to-wordpress-term-meta) 102 | 103 | **Code References:** 104 | 105 | * Plugin Hooks 106 | * action [admin_init](http://codex.wordpress.org/Plugin_API/Action_Reference/admin_init) 107 | * action [admin_enqueue_scripts](http://codex.wordpress.org/Plugin_API/Action_Reference/admin_enqueue_scripts) 108 | * function [wp_register_script](https://developer.wordpress.org/reference/functions/wp_register_script/) 109 | * function [wp_localize_script](https://developer.wordpress.org/reference/functions/wp_localize_script/) 110 | * function [wp_enqueue_script](https://developer.wordpress.org/reference/functions/wp_enqueue_script/) 111 | * Term Meta Data 112 | * function [register_meta](https://developer.wordpress.org/reference/functions/register_meta/) 113 | * function [get_term_meta](https://make.wordpress.org/core/2015/10/23/4-4-taxonomy-roundup/) 114 | * function [update_term_meta](https://make.wordpress.org/core/2015/10/23/4-4-taxonomy-roundup/) 115 | * function [delete_term_meta](https://make.wordpress.org/core/2015/10/23/4-4-taxonomy-roundup/) 116 | * Taxonomy Hooks 117 | * action [create_{$taxonomy}](https://developer.wordpress.org/reference/hooks/create_taxonomy/) 118 | * action [edit_{$taxonomy}](https://developer.wordpress.org/reference/hooks/edit_taxonomy/) 119 | * action [{$taxonomy}_add_form_fields](https://developer.wordpress.org/reference/hooks/taxonomy_add_form_fields/) 120 | * action [{$taxonomy}_edit_form_fields](https://developer.wordpress.org/reference/hooks/taxonomy_edit_form_fields/) 121 | * filter [get_term](https://developer.wordpress.org/reference/hooks/get_term/) 122 | * filter [get_terms](https://developer.wordpress.org/reference/hooks/get_terms/) 123 | * filter [get_object_terms](https://developer.wordpress.org/reference/hooks/get_object_terms/) 124 | 125 | -------------------------------------------------------------------------------- /js/taxonomy-term-image.js: -------------------------------------------------------------------------------- 1 | (function($){ 2 | var taxonomyTermImageText = TaxonomyTermImageText || {}; 3 | 4 | var taxonomyTermImage = { 5 | 6 | // modal frame 7 | file_frame: null, 8 | 9 | // modal settings 10 | settings: { 11 | title: taxonomyTermImageText.modalTitle || 'Select or upload a video', 12 | button: { 13 | text: taxonomyTermImageText.modalButton || 'Attach' 14 | }, 15 | multiple: false, // Set to true to allow multiple files to be selected 16 | library: { 17 | type: 'image' 18 | } 19 | }, 20 | 21 | /** 22 | * Initialize script 23 | */ 24 | init: function(){ 25 | var _this = this; 26 | 27 | // delegate our click handler so that the image itself is clickable. because that's cool 28 | $('body').on('click', '.taxonomy-term-image-attach', function ( event ) { 29 | event.preventDefault(); 30 | 31 | _this.openModal(); 32 | }); 33 | 34 | // remove button 35 | $('.taxonomy-term-image-remove').click( _this.removeImage ); 36 | }, 37 | 38 | /** 39 | * Remove the current image by emptying the container and field 40 | */ 41 | removeImage: function(){ 42 | $('#taxonomy-term-image-container').html(''); 43 | $('#taxonomy-term-image-id').val(''); 44 | }, 45 | 46 | /** 47 | * Open the media modal window 48 | * - http://mikejolley.com/2012/12/using-the-new-wordpress-3-5-media-uploader-in-plugins/ 49 | * - https://gist.github.com/pippinsplugins/29bebb740e09e395dc06 50 | */ 51 | openModal: function(){ 52 | var _this = this; 53 | 54 | // If the media frame already exists, reopen it. 55 | if ( _this.file_frame ) { 56 | _this.file_frame.open(); 57 | return; 58 | } 59 | 60 | // Create the media frame. 61 | _this.file_frame = wp.media.frames.file_frame = wp.media( _this.settings ); 62 | 63 | // When an image is selected, run a callback. 64 | _this.file_frame.on( 'select', function() { 65 | 66 | _this.file_frame.state() 67 | .get('selection') 68 | 69 | // handle each attachment 70 | .map( _this.updateImage); 71 | }); 72 | 73 | // Finally, open the modal 74 | _this.file_frame.open(); 75 | }, 76 | 77 | /** 78 | * Handle a single selected image attachment 79 | * 80 | * @param attachment 81 | */ 82 | updateImage: function( attachment ){ 83 | // the selected image 84 | var image = attachment.toJSON(); 85 | 86 | // retrieve image url. 87 | // no bitmap image like svg does not have "sizes" attribute. 88 | var url ; 89 | var sizes = attachment.get('sizes'); 90 | if( typeof sizes !== 'undefined' ) 91 | url = ( typeof sizes.thumbnail === 'undefined' ) ? sizes.full.url : sizes.thumbnail.url ; 92 | else 93 | url = attachment.get('url'); 94 | 95 | //image.id 96 | $('#taxonomy-term-image-id').val( image.id ); 97 | 98 | //sizes.thumbnail.url 99 | $('#taxonomy-term-image-container').html(""); 100 | } 101 | }; 102 | 103 | $(document).ready(function(){ 104 | taxonomyTermImage.init(); 105 | }); 106 | 107 | })(jQuery); 108 | -------------------------------------------------------------------------------- /taxonomy-term-image.php: -------------------------------------------------------------------------------- 1 | labels = array( 63 | 'fieldTitle' => __( 'Taxonomy Term Image' ), 64 | 'fieldDescription' => __( 'Select which image should represent this term.' ), 65 | 'imageButton' => __( 'Select Image' ), 66 | 'removeButton' => __( 'Remove' ), 67 | 'modalTitle' => __( 'Select or upload an image for this term' ), 68 | 'modalButton' => __( 'Attach' ), 69 | 'adminColumnTitle' => __( 'Image' ), 70 | ); 71 | 72 | // allow overriding of the html text 73 | $this->labels = apply_filters( 'taxonomy-term-image-labels', $this->labels ); 74 | 75 | // allow overriding of the target taxonomies 76 | $this->taxonomies = apply_filters( 'taxonomy-term-image-taxonomy', $this->taxonomies ); 77 | 78 | if ( ! is_array( $this->taxonomies ) ) { 79 | $this->taxonomies = array( $this->taxonomies ); 80 | } 81 | 82 | // @deprecated: allow overriding of option_name 83 | // default option name keyed to the taxonomy 84 | $this->option_name = $this->taxonomies[0] . '_term_images'; 85 | $this->option_name = apply_filters( 'taxonomy-term-image-option-name', $this->option_name ); 86 | 87 | // allow overriding of term_meta 88 | $this->term_meta_key = apply_filters( 'taxonomy-term-image-meta-key', $this->term_meta_key ); 89 | 90 | // get our js location for enqueing scripts 91 | $this->js_dir_url = apply_filters( 'taxonomy-term-image-js-dir-url', plugin_dir_url( __FILE__ ) . '/js' ); 92 | 93 | // check for updates 94 | $this->upgrade(); 95 | 96 | // hook into WordPress 97 | $this->hook_up(); 98 | } 99 | 100 | /** 101 | * Check for plugin updates 102 | */ 103 | private function upgrade(){ 104 | $previous_version = get_option( 'taxonomy-term-image-version', '0.0.0' ); 105 | 106 | // if the previous version is less than the current version, 107 | // we need to upgrade 108 | if ( version_compare( $previous_version, $this->version, '<' ) ){ 109 | 110 | $old_option = get_option( $this->option_name, array() ); 111 | 112 | // if we have data in the old (1.x) option, 113 | // move it to term meta and delete the old option 114 | if ( ! empty( $old_option ) ) { 115 | foreach( $old_option as $term_id => $image_id ) { 116 | update_term_meta( $term_id, $this->term_meta_key, $image_id ); 117 | } 118 | 119 | delete_option( $this->option_name ); 120 | } 121 | 122 | // modify the stored version data for future checks 123 | update_option( 'taxonomy-term-image-version', $this->version ); 124 | } 125 | } 126 | 127 | /** 128 | * Hook into WordPress 129 | */ 130 | private function hook_up(){ 131 | // register our term meta and sanitize as an integer 132 | register_meta( 'term', $this->term_meta_key, 'absint' ); 133 | 134 | // add our data when term is retrieved 135 | add_filter( 'get_term', array( $this, 'get_term' ) ); 136 | add_filter( 'get_terms', array( $this, 'get_terms' ) ); 137 | add_filter( 'get_object_terms', array( $this, 'get_terms' ) ); 138 | 139 | // we only need to add most hooks on the admin side 140 | if ( is_admin() ) { 141 | add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) ); 142 | 143 | foreach ( $this->taxonomies as $taxonomy ) { 144 | // add our image field to the taxonomy term forms 145 | add_action( $taxonomy . '_add_form_fields', array( $this, 'taxonomy_add_form' ) ); 146 | add_action( $taxonomy . '_edit_form_fields', array( $this, 'taxonomy_edit_form' ) ); 147 | 148 | // hook into term administration actions 149 | add_action( 'create_' . $taxonomy, array( $this, 'taxonomy_term_form_save' ) ); 150 | add_action( 'edit_' . $taxonomy, array( $this, 'taxonomy_term_form_save' ) ); 151 | 152 | // custom admin taxonomy term list columns 153 | add_filter( 'manage_edit-' . $taxonomy . '_columns', array( $this, 'taxonomy_term_column_image' ) ); 154 | add_filter( 'manage_' . $taxonomy . '_custom_column', array( $this, 'taxonomy_term_column_image_content' ), 10, 3 ); 155 | } 156 | } 157 | } 158 | 159 | /** 160 | * Add a new column to enabled taxonomy admin pages that show the chosen 161 | * image. 162 | * 163 | * @param $columns 164 | * 165 | * @return mixed 166 | */ 167 | function taxonomy_term_column_image( $columns ){ 168 | $columns['term_image'] = $this->labels['adminColumnTitle']; 169 | 170 | return $columns; 171 | } 172 | 173 | /** 174 | * Show the selected term image within the newly created admin column 175 | * 176 | * @param $content 177 | * @param $column_name 178 | * @param $term_id 179 | * 180 | * @return mixed 181 | */ 182 | function taxonomy_term_column_image_content( $content, $column_name, $term_id ){ 183 | if ( 'term_image' == $column_name ){ 184 | $term = get_term( $term_id ); 185 | 186 | if ( $term->term_image ) { 187 | $content = wp_get_attachment_image( $term->term_image, 'thumbnail', false, array( 'style' => 'max-width:100%; height:auto;' ) ); 188 | } 189 | } 190 | return $content; 191 | } 192 | 193 | 194 | /** 195 | * Add the image data to any relevant get_term() call. Double duty as a 196 | * helper function for this->get_terms(). 197 | * 198 | * @param $_term 199 | * @return object 200 | */ 201 | function get_term( $_term ) { 202 | // only modify term when dealing with our taxonomies 203 | if ( is_object( $_term ) && in_array( $_term->taxonomy, $this->taxonomies ) ) { 204 | 205 | // default to null if not found 206 | $image_id = get_term_meta( $_term->term_id, $this->term_meta_key, true ); 207 | $_term->term_image = !empty( $image_id ) ? $image_id : null; 208 | } 209 | return $_term; 210 | } 211 | 212 | /** 213 | * Add term_image data to objects when get_terms() or wp_get_object_terms() 214 | * is called. 215 | * 216 | * @param $terms 217 | * @return array 218 | */ 219 | function get_terms( $terms ) { 220 | foreach( $terms as $i => $term ){ 221 | if ( is_object( $term ) && !empty( $term->taxonomy ) ) { 222 | $terms[ $i ] = $this->get_term( $term ); 223 | } 224 | } 225 | return $terms; 226 | } 227 | 228 | /** 229 | * WordPress action "admin_enqueue_scripts" 230 | */ 231 | function admin_enqueue_scripts(){ 232 | // get the screen object to decide if we want to inject our scripts 233 | $screen = get_current_screen(); 234 | 235 | // check if we are on any edit-{taxonomy} screen 236 | foreach( $this->taxonomies as $taxonomy ) { 237 | if ( $screen->id == 'edit-' . $taxonomy ){ 238 | // WP core stuff we need 239 | wp_enqueue_media(); 240 | wp_enqueue_style( 'thickbox' ); 241 | 242 | // register our custom script 243 | wp_register_script( 'taxonomy-term-image-js', $this->js_dir_url . '/taxonomy-term-image.js', array( 'jquery', 'thickbox', 'media-upload' ), $this->version, true ); 244 | 245 | // Localize the modal window text so it can be translated 246 | wp_localize_script( 'taxonomy-term-image-js', 'TaxonomyTermImageText', $this->labels ); 247 | 248 | // enqueue the registered and localized script 249 | wp_enqueue_script( 'taxonomy-term-image-js' ); 250 | break; 251 | } 252 | } 253 | } 254 | 255 | /** 256 | * The HTML form for our taxonomy image field 257 | * 258 | * @param int $image_ID the image ID 259 | * @return string the html output for the image form 260 | */ 261 | function taxonomy_term_image_field( $image_ID = null ) { 262 | $image_src = ( $image_ID ) ? wp_get_attachment_image_src( $image_ID, 'thumbnail' ) : array(); 263 | 264 | wp_nonce_field( 'taxonomy-term-image-form-save', 'taxonomy-term-image-save-form-nonce' ); 265 | ?> 266 | 267 | 268 | 269 |

labels['fieldDescription']; ?>

270 | 271 |

272 | 273 | 274 | 275 |

276 | 284 |
285 | 286 | taxonomy_term_image_field(); ?> 287 |
288 | term_image ) ){ 299 | $term = $this->get_term( $term, $term->taxonomy ); 300 | } 301 | ?> 302 | 303 | 304 | 305 | taxonomy_term_image_field( $term->term_image ); ?> 306 | 307 | 308 | taxonomies ) 329 | ) 330 | { 331 | // get the term_meta and assign it the old_image 332 | $old_image = get_term_meta( $term_id, $this->term_meta_key, true ); 333 | 334 | // sanitize the data and save it as the new_image 335 | $new_image = absint( $_POST['taxonomy_term_image'] ); 336 | 337 | // if an image was removed, delete the meta data 338 | if ( $old_image && '' === $new_image ) { 339 | delete_term_meta( $term_id, $this->term_meta_key ); 340 | } 341 | // if the new image is not the same as the old update the term_meta 342 | else if ( $old_image !== $new_image ) { 343 | update_term_meta( $term_id, $this->term_meta_key, $new_image ); 344 | } 345 | } 346 | } 347 | } 348 | 349 | endif; 350 | 351 | /** 352 | * Initialize the plugin by calling for its instance on WordPress action 'init' 353 | */ 354 | function taxonomy_term_image_init() { 355 | Taxonomy_Term_Image::instance(); 356 | } 357 | add_action( 'init', 'taxonomy_term_image_init' ); 358 | 359 | --------------------------------------------------------------------------------