WordPress 媒体文件分类管理

use taxonomy with media file

媒体文件是 WordPress 内置的一种内容类型,叫做 “Attachment”,其它的还有如 Post、Page 等,目前 Post、Page 提供了分类管理的机制,就是目录和标签(category、tag),但媒体文件这种内容类型并没有提供相应的分类管理方法,本篇文章就介绍实现媒体文件分类管理的机制和方法。

在之前的两篇文章中,我们介绍过用户自定义的内容类型分类,媒体文件这种内容类型在 WordPress 中是 Attachment,对其做内容分类管理可以使用系统内置的目录方法(term 可以复用),也可以创建自定义的分类方法(taxonomy)。

一、注册分类方法并关联内容类型

分类方法可以使用系统内置的目录(category),也可以使用自定义的,注册的时候关联 Attachment 内容类型。

public function taxonomy_init() {
   // 注册分类方法,可以自定义
   $taxonomy = 'mediacat';
   $args = array(
       'hierarchical'          => true,
       'show_admin_column'     => true,
       'public'                => false,
       'show_ui'               => true,
       'update_count_callback' => '_update_generic_term_count'
   );
   register_taxonomy( $taxonomy, array( 'attachment' ), $args );
}

分类方法创建和内容类型关联一般放到系统的 ‘init’ 这个 action 中:

add_action( ‘init’, ‘taxonomy_init’ );

_update_generic_term_count 这个回调函数是当分类中的内容数量改变时被调用。
_update_post_term_count(),用于关联到 Post 这种内容类型时使用。

二、媒体管理界面上显示自定义分类项

新建的分类需要作为过滤条件显示在媒体管理界面上,要分成两种方式来处理,即列表方式和网格方式。

1、列表方式

列表方式使用系统 action 钩链 “restrict_manage_posts”,主要是获取分类及相关信息并创建界面元素。

add_action( ‘restrict_manage_posts’, ‘media_category_filter’ );

public function media_category_filter() {
   global $wp_query, $current_screen;
   if ( 'upload' == $current_screen->base ) {
       // 当前界面在媒体管理界面
       $taxonomy = 'mediacat';
       $selected = isset( $wp_query->query[$taxonomy] ) ? $wp_query->query[$taxonomy] : 0;
       $dropdown_options = array(
           'taxonomy'        => $taxonomy,
           'name'            => $taxonomy,
           'class'           => 'media-filter', //css
           'show_option_all' => __( 'All categories', 'wp-media-library-categories' ),
           'hide_empty'      => false,
           'hierarchical'    => true,
           'orderby'         => 'name',
           'selected'        => $selected,
           'show_count'      => true,
           'echo'            => true,
           'walker'          => new media_walker_category_filter(),
           'value_field'     => 'slug'
       );
       wp_dropdown_categories( $dropdown_options ); // 获取分类名称及其关联的媒体文件数量
   }
}

media_walker_category_filter 这个类可以对处理分类下拉列表时对每个条目做预处理,比如处理每个分类项关联的媒体文件数量。父类是 Walker_CategoryDropdown(系统内置),具体的数据是通过 wp_dropdown_categories 系统调用得到的。

class media_walker_category_filter extends Walker_CategoryDropdown {
    function start_el( &$output, $term, $depth = 0, $args = array(), $id = 0 ) {
        $pad = str_repeat( ' ', $depth * 3 ); //父子项缩进处理
        $term_name = apply_filters( 'list_cats', $term->name, $term );

        if( ! isset( $args['value'] ) ) {
            $args['value'] = $term->slug;
        }
        //分类项是否被选中
        $value = $args['value'];
        if ( 0 == $args['selected'] && isset( $_GET['mediacat'] ) && '' != $_GET['mediacat'] ) {
            $args['selected'] = $_GET['mediacat'];
        }
        //生成选项标签
        $output .= '<option class="level-' . $depth . '" value="' . $value . '"';
        if ( (string) $value === (string) $args['selected'] ) {
            $output .= ' selected="selected"';
        }
        $output .= '>';
        $output .= $pad . $term_name;
        //处理该项分类中的媒体文件数量
        if ( $args['show_count'] )
            $output .= '  (' . $term->count . ')';

        $output .= "</option>\n";
    }
}
2、网格方式

网格方式使用系统 action 钩链 “admin_enqueue_scripts” 创建界面元素。

add_action( ‘admin_enqueue_scripts’, ‘enqueue_media_action’ );

public function enqueue_media_action() {
  global $pagenow;
  if(wp_script_is( 'media-editor' ) && 'upload.php' == $pagenow) {
        $taxonomy = 'mediacat';
	$dropdown_options = array(
	'taxonomy'        => $taxonomy,
	'class'           => 'media-filter',
	'hide_empty'      => false,
	'hierarchical'    => true,
	'orderby'         => 'name',
	'show_count'      => true,
	'walker'          => new media_walker_category_gridfilter(),
	'value'           => 'id',
	'echo'            => false
	);
  } else return;
  //获取分类项数据
  $media_terms = wp_dropdown_categories( $dropdown_options );
  $media_terms = preg_replace( array( "/<select([^>]*)>/", "/<\/select>/" ), "", $media_terms );
  // grid-media-views.js 代码部分在下面说明
  wp_enqueue_script( 'grid-media-views', plugins_url( 'js/grid-media-views' . '.js', __FILE__ ), array( 'media-views' ), null, true );
  //为 grid-media-views.js 准备数据
  wp_localize_script( 'grid-media-views', 'media_grid', array(
	media_taxonomies => '{"' . $taxonomy . '":{"list_title":"' . __( 'All categories', 'wp-media-library-categories' ) . '","term_list":[' . substr( $media_terms, 2 ) . ']}};'
	));
}

分类条目预处理 Walker:

class media_walker_category_gridfilter extends Walker_CategoryDropdown {
    function start_el( &$output, $term, $depth = 0, $args = array(), $id = 0 ) {
        $pad = str_repeat( ' ', $depth * 3 );
        $term_name = apply_filters( 'list_cats', $term->name, $term );
        $output .= ',{"term_id":"' . $term->term_id . '",';
        $output .= '"term_name":"' . $pad . esc_attr( $term_name );
        if ( $args['show_count'] ) {
            $output .= '  ('. $term->count .')';
        }
        $output .= '"}';
    }
}

分类条目界面及选择后响应处理代码(grid-media-views.js):

需要说明一下,网格方式媒体文件管理部分采用了基于 backbone.js 的 WordPress 媒体库 wp.media

(function($){
	var media = wp.media;
	media.view.AttachmentFilters.Taxonomy = media.view.AttachmentFilters.extend({	//对系统内置的分类过滤器做扩展
	tagName:   'select',	//界面元素是 select
	createFilters: function() {	//过滤函数,创建分类项条目
		var filters = {};
		var that = this;
		//创建每个分类条目的过滤条件(显示名称和值)
		_.each( that.options.termList || {}, function( term, key ) {
			var term_id = term['term_id'];
			var term_name = $("<div/>").html(term['term_name']).text();
			filters[ term_id ] = {
				text: term_name,
				priority: key+2
			};
			filters[term_id]['props'] = {};
			filters[term_id]['props'][that.options.taxonomy] = term_id;
			});
		//分类项“所有”的显示名称和值
		filters.all = {
			text: that.options.termListTitle,
			priority: 1
		};
		filters['all']['props'] = {};
		filters['all']['props'][that.options.taxonomy] = null;
		this.filters = filters;
	}
});
	var curAttachmentsBrowser = media.view.AttachmentsBrowser;
	media.view.AttachmentsBrowser = media.view.AttachmentsBrowser.extend({	//对内置的管理界面做扩展
		//媒体管理界面Toolbar中创建分类项,使用上面自定义过滤器
		createToolbar: function() {
			var filters = this.options.filters;	curAttachmentsBrowser.prototype.createToolbar.apply(this,arguments);
			var that = this,
			i = 1;
			//分类项数据来自上面 enqueue_media_action 函数
			$.each(media_grid.media_taxonomies, 
			function(taxonomy, values)
			{
				if ( values.term_list && filters )
				{	//工具条中显示分类项选择
					that.toolbar.set( taxonomy+'-filter', new media.view.AttachmentFilters.Taxonomy({
			//扩展界面和系统model、controller做绑定
			controller: that.controller,
			model: that.collection.props,
			priority: -80 + 10*i++,
			taxonomy: taxonomy,
			termList: values.term_list,  //分类条目列表
			termListTitle: values.list_title, //“所有”选项显示名
			className: 'taxonomy-filter attachment-'+taxonomy+'-filter'
					}).render() );
				}
			});
		}
	});
})( jQuery );
三、媒体文件“附件详情”编辑界面提供分类项选择

媒体文件编辑界面提供创建新的分类项及子项条目,也能选择归属条目(操作界面系统自动生成),而附件详情编辑界面只能选择归属的分类项。

分类条目选择界面是在系统钩链 “attachment_fields_to_edit” 中完成。

add_filter( ‘attachment_fields_to_edit’, ‘media_attachment_fields_to_edit’, 10, 2 );

public function media_attachment_fields_to_edit( $form_fields, $post ) {

	foreach ( get_attachment_taxonomies( $post->ID ) as $taxonomy ) {
		$terms = get_object_term_cache( $post->ID, $taxonomy );
		$t = (array)get_taxonomy( $taxonomy );
		if ( ! $t['public'] || ! $t['show_ui'] ) {
			continue;
		}
		if ( empty($t['label']) ) {
			$t['label'] = $taxonomy;
		}
		if ( empty($t['args']) ) {
			$t['args'] = array();
		}
		if ( false === $terms ) {
			$terms = wp_get_object_terms($post->ID, $taxonomy, $t['args']);
		}
		$values = array();
		foreach ( $terms as $term ) {
			$values[] = $term->slug;
		}
		$t['value'] = join(', ', $values);
		$t['show_in_edit'] = false;
		if ( $t['hierarchical'] ) {
			ob_start();
                //获取分类项条目,生成操作界面并输出
                    wp_terms_checklist( $post->ID, array( 'taxonomy' => $taxonomy, 'checked_ontop' => false, 'walker' => new media_walker_taxonomy_checklist() ) );
                    if ( ob_get_contents() != false ) {
                        $html = '<ul class="term-list">' . ob_get_contents() . '</ul>';
                    } else {
                        $html = '<ul class="term-list"><li>No ' . $t['label'] . '</li></ul>';
                    }
			ob_end_clean();
			$t['input'] = 'html';
			$t['html'] = $html;
		}
		$form_fields[$taxonomy] = $t;
	}
	return $form_fields;
}

分类条目预处理 Walker:

class media_walker_taxonomy_checklist extends Walker {
    var $tree_type = 'category';
    var $db_fields = array(
        'parent' => 'parent',
        'id'     => 'term_id'
    );
    function start_lvl( &$output, $depth = 0, $args = array() ) {
        $indent  = str_repeat( "\t", $depth );
        $output .= "$indent<ul class='children'>\n";
    }
    function end_lvl( &$output, $depth = 0, $args = array() ) {
        $indent  = str_repeat( "\t", $depth );
        $output .= "$indent</ul>\n";
    }
    function start_el( &$output, $term, $depth = 0, $args = array(), $id = 0 ) {
        extract( $args );
        // 自定义分类方法
        $taxonomy = 'mediacat';
        $name  = 'tax_input[' . $taxonomy . ']';
        $class = in_array( $term->term_id, $args['popular_cats'] ) ? ' class="popular-category"' : '';
        //输出分类项条目
        $output .= "\n<li id='{$taxonomy}-{$term->term_id}'$class>" .
                '<label class="selectit"><input value="' . $term->slug . '" type="checkbox" name="' . $name . '[' . $term->slug . ']" id="in-' . $taxonomy . '-' . $term->term_id . '"' .
                checked( in_array( $term->term_id, $args['selected_cats'] ), true, false ) .
                disabled( empty( $args['disabled'] ), false, false ) . ' /> ' .
                esc_html( apply_filters( 'the_category', $term->name ) ) . '</label>';
    }
    function end_el( &$output, $term, $depth = 0, $args = array() ) {
        $output .= "</li>\n";
    }
}

通过上述代码就可以实现一个简单的媒体文件分类管理的功能,了解清楚原理后还可以进一步拓展更多的管理方法,完成更多复杂的操作。

上述代码参考并引用自这个网址,感谢分享。

发表评论

邮箱地址不会被公开。 必填项已用*标注