好吧,我试试看。我在这一过程中遇到了一些限制:
WP\\u List\\u表的子类中没有太多的过滤器,至少没有我们需要它们的地方。
由于缺少过滤器,我们无法真正将插件类型的准确列表保持在顶部。
我们还必须使用一些可怕的JavaScript黑客来显示插件的活动状态。
我将管理区号包装在一个类中,因此我的函数名没有前缀。您可以看到所有这些代码here. 请投稿!
Central API
这只是一个简单的函数,它设置了一个全局变量,该变量将在关联数组中包含我们的插件目录。这个
$key
将在内部用于获取插件等。
$dir
是完整路径或与
wp-content
目录
$label
将在管理区域显示(例如,可翻译字符串)。
<?php
function register_plugin_directory( $key, $dir, $label )
{
global $wp_plugin_directories;
if( empty( $wp_plugin_directories ) ) $wp_plugin_directories = array();
if( ! file_exists( $dir ) && file_exists( trailingslashit( WP_CONTENT_DIR ) . $dir ) )
{
$dir = trailingslashit( WP_CONTENT_DIR ) . $dir;
}
$wp_plugin_directories[$key] = array(
\'label\' => $label,
\'dir\' => $dir
);
}
然后,当然,我们需要加载插件。钩入
plugins_loaded
很晚了,检查活动插件,加载每个插件。
Admin Area
让我们在类中设置我们的功能。
<?php
class CD_APD_Admin
{
/**
* The container for all of our custom plugins
*/
protected $plugins = array();
/**
* What custom actions are we allowed to handle here?
*/
protected $actions = array();
/**
* The original count of the plugins
*/
protected $all_count = 0;
/**
* constructor
*
* @since 0.1
*/
function __construct()
{
add_action( \'load-plugins.php\', array( &$this, \'init\' ) );
add_action( \'plugins_loaded\', array( &$this, \'setup_actions\' ), 1 );
}
} // end class
我们要加入
plugins_loaded
非常早,设置我们将要使用的允许的“操作”。这些将处理插件的激活和停用,因为内置函数无法处理自定义目录。
function setup_actions()
{
$tmp = array(
\'custom_activate\',
\'custom_deactivate\'
);
$this->actions = apply_filters( \'custom_plugin_actions\', $tmp );
}
然后是连接到
load-plugins.php
. 这可以做各种有趣的事情。
function init()
{
global $wp_plugin_directories;
$screen = get_current_screen();
$this->get_plugins();
$this->handle_actions();
add_filter( \'views_\' . $screen->id, array( &$this, \'views\' ) );
// check to see if we\'re using one of our custom directories
if( $this->get_plugin_status() )
{
add_filter( \'views_\' . $screen->id, array( &$this, \'views_again\' ) );
add_filter( \'all_plugins\', array( &$this, \'filter_plugins\' ) );
// TODO: support bulk actions
add_filter( \'bulk_actions-\' . $screen->id, \'__return_empty_array\' );
add_filter( \'plugin_action_links\', array( &$this, \'action_links\' ), 10, 2 );
add_action( \'admin_enqueue_scripts\', array( &$this, \'scripts\' ) );
}
}
让我们一次只做一件事。这个
get_plugins
方法,是另一个函数的包装器。它填充属性
plugins
使用数据。
function get_plugins()
{
global $wp_plugin_directories;
foreach( array_keys( $wp_plugin_directories ) as $key )
{
$this->plugins[$key] = cd_apd_get_plugins( $key );
}
}
cd_apd_get_plugins
是内置的撕裂
get_plugins
不带硬编码的功能
WP_CONTENT_DIR
和
plugins
商业基本上:从
$wp_plugin_directories
全局,打开它,找到所有插件文件。将它们存储在缓存中以备将来使用。
<?php
function cd_apd_get_plugins( $dir_key )
{
global $wp_plugin_directories;
// invalid dir key? bail
if( ! isset( $wp_plugin_directories[$dir_key] ) )
{
return array();
}
else
{
$plugin_root = $wp_plugin_directories[$dir_key][\'dir\'];
}
if ( ! $cache_plugins = wp_cache_get( \'plugins\', \'plugins\') )
$cache_plugins = array();
if ( isset( $cache_plugins[$dir_key] ) )
return $cache_plugins[$dir_key];
$wp_plugins = array();
$plugins_dir = @ opendir( $plugin_root );
$plugin_files = array();
if ( $plugins_dir ) {
while ( ( $file = readdir( $plugins_dir ) ) !== false ) {
if ( substr($file, 0, 1) == \'.\' )
continue;
if ( is_dir( $plugin_root.\'/\'.$file ) ) {
$plugins_subdir = @ opendir( $plugin_root.\'/\'.$file );
if ( $plugins_subdir ) {
while (($subfile = readdir( $plugins_subdir ) ) !== false ) {
if ( substr($subfile, 0, 1) == \'.\' )
continue;
if ( substr($subfile, -4) == \'.php\' )
$plugin_files[] = "$file/$subfile";
}
closedir( $plugins_subdir );
}
} else {
if ( substr($file, -4) == \'.php\' )
$plugin_files[] = $file;
}
}
closedir( $plugins_dir );
}
if ( empty($plugin_files) )
return $wp_plugins;
foreach ( $plugin_files as $plugin_file ) {
if ( !is_readable( "$plugin_root/$plugin_file" ) )
continue;
$plugin_data = get_plugin_data( "$plugin_root/$plugin_file", false, false ); //Do not apply markup/translate as it\'ll be cached.
if ( empty ( $plugin_data[\'Name\'] ) )
continue;
$wp_plugins[trim( $plugin_file )] = $plugin_data;
}
uasort( $wp_plugins, \'_sort_uname_callback\' );
$cache_plugins[$dir_key] = $wp_plugins;
wp_cache_set(\'plugins\', $cache_plugins, \'plugins\');
return $wp_plugins;
}
接下来是实际激活和停用插件的麻烦事。为此,我们使用
handle_actions
方法这又是从核心顶部公然撕下的
wp-admin/plugins.php
文件
function handle_actions()
{
$action = isset( $_REQUEST[\'action\'] ) ? $_REQUEST[\'action\'] : \'\';
// not allowed to handle this action? bail.
if( ! in_array( $action, $this->actions ) ) return;
// Get the plugin we\'re going to activate
$plugin = isset( $_REQUEST[\'plugin\'] ) ? $_REQUEST[\'plugin\'] : false;
if( ! $plugin ) return;
$context = $this->get_plugin_status();
switch( $action )
{
case \'custom_activate\':
if( ! current_user_can(\'activate_plugins\') )
wp_die( __(\'You do not have sufficient permissions to manage plugins for this site.\') );
check_admin_referer( \'custom_activate-\' . $plugin );
$result = cd_apd_activate_plugin( $plugin, $context );
if ( is_wp_error( $result ) )
{
if ( \'unexpected_output\' == $result->get_error_code() )
{
$redirect = add_query_arg( \'plugin_status\', $context, self_admin_url( \'plugins.php\' ) );
wp_redirect( add_query_arg( \'_error_nonce\', wp_create_nonce( \'plugin-activation-error_\' . $plugin ), $redirect ) ) ;
exit();
}
else
{
wp_die( $result );
}
}
wp_redirect( add_query_arg( array( \'plugin_status\' => $context, \'activate\' => \'true\' ), self_admin_url( \'plugins.php\' ) ) );
exit();
break;
case \'custom_deactivate\':
if ( ! current_user_can( \'activate_plugins\' ) )
wp_die( __(\'You do not have sufficient permissions to deactivate plugins for this site.\') );
check_admin_referer(\'custom_deactivate-\' . $plugin);
cd_apd_deactivate_plugins( $plugin, $context );
if ( headers_sent() )
echo "<meta http-equiv=\'refresh\' content=\'" . esc_attr( "0;url=plugins.php?deactivate=true&plugin_status=$status&paged=$page&s=$s" ) . "\' />";
else
wp_redirect( self_admin_url("plugins.php?deactivate=true&plugin_status=$context") );
exit();
break;
default:
do_action( \'custom_plugin_dir_\' . $action );
break;
}
}
这里又有几个自定义函数。
cd_apd_activate_plugin
(摘自
activate_plugin
) 和
cd_apd_deactivate_plugins
(摘自
deactivate_plugins
). 两者都与各自的“父”函数相同,没有硬编码目录。
function cd_apd_activate_plugin( $plugin, $context, $silent = false )
{
$plugin = trim( $plugin );
$redirect = add_query_arg( \'plugin_status\', $context, admin_url( \'plugins.php\' ) );
$redirect = apply_filters( \'custom_plugin_redirect\', $redirect );
$current = get_option( \'active_plugins_\' . $context, array() );
$valid = cd_apd_validate_plugin( $plugin, $context );
if ( is_wp_error( $valid ) )
return $valid;
if ( !in_array($plugin, $current) ) {
if ( !empty($redirect) )
wp_redirect(add_query_arg(\'_error_nonce\', wp_create_nonce(\'plugin-activation-error_\' . $plugin), $redirect)); // we\'ll override this later if the plugin can be included without fatal error
ob_start();
include_once( $valid );
if ( ! $silent ) {
do_action( \'custom_activate_plugin\', $plugin, $context );
do_action( \'custom_activate_\' . $plugin, $context );
}
$current[] = $plugin;
sort( $current );
update_option( \'active_plugins_\' . $context, $current );
if ( ! $silent ) {
do_action( \'custom_activated_plugin\', $plugin, $context );
}
if ( ob_get_length() > 0 ) {
$output = ob_get_clean();
return new WP_Error(\'unexpected_output\', __(\'The plugin generated unexpected output.\'), $output);
}
ob_end_clean();
}
return true;
}
和停用功能
function cd_apd_deactivate_plugins( $plugins, $context, $silent = false ) {
$current = get_option( \'active_plugins_\' . $context, array() );
foreach ( (array) $plugins as $plugin )
{
$plugin = trim( $plugin );
if ( ! in_array( $plugin, $current ) ) continue;
if ( ! $silent )
do_action( \'custom_deactivate_plugin\', $plugin, $context );
$key = array_search( $plugin, $current );
if ( false !== $key ) {
array_splice( $current, $key, 1 );
}
if ( ! $silent ) {
do_action( \'custom_deactivate_\' . $plugin, $context );
do_action( \'custom_deactivated_plugin\', $plugin, $context );
}
}
update_option( \'active_plugins_\' . $context, $current );
}
还有
cd_apd_validate_plugin
函数,当然是对
validate_plugin
没有硬编码的垃圾。
<?php
function cd_apd_validate_plugin( $plugin, $context )
{
$rv = true;
if ( validate_file( $plugin ) )
{
$rv = new WP_Error(\'plugin_invalid\', __(\'Invalid plugin path.\'));
}
global $wp_plugin_directories;
if( ! isset( $wp_plugin_directories[$context] ) )
{
$rv = new WP_Error( \'invalid_context\', __( \'The context for this plugin does not exist\' ) );
}
$dir = $wp_plugin_directories[$context][\'dir\'];
if( ! file_exists( $dir . \'/\' . $plugin) )
{
$rv = new WP_Error( \'plugin_not_found\', __( \'Plugin file does not exist.\' ) );
}
$installed_plugins = cd_apd_get_plugins( $context );
if ( ! isset($installed_plugins[$plugin]) )
{
$rv = new WP_Error( \'no_plugin_header\', __(\'The plugin does not have a valid header.\') );
}
$rv = $dir . \'/\' . $plugin;
return $rv;
}
好吧,别挡道了。我们可以开始讨论
list table display步骤1:将我们的视图添加到表顶部的列表中。这是通过过滤完成的views_{$screen->id}
在我们的init
作用
add_filter( \'views_\' . $screen->id, array( &$this, \'views\' ) );
然后,实际的钩子函数只是在
$wp_plugin_directories
. 如果其中一个新注册的目录有插件,我们将在屏幕上显示它。
function views( $views )
{
global $wp_plugin_directories;
// bail if we don\'t have any extra dirs
if( empty( $wp_plugin_directories ) ) return $views;
// Add our directories to the action links
foreach( $wp_plugin_directories as $key => $info )
{
if( ! count( $this->plugins[$key] ) ) continue;
$class = $this->get_plugin_status() == $key ? \' class="current" \' : \'\';
$views[$key] = sprintf(
\'<a href="%s"\' . $class . \'>%s <span class="count">(%d)</span></a>\',
add_query_arg( \'plugin_status\', $key, \'plugins.php\' ),
esc_html( $info[\'label\'] ),
count( $this->plugins[$key] )
);
}
return $views;
}
如果我们碰巧看到一个自定义插件目录页面,我们需要做的第一件事就是再次过滤视图。我们需要摆脱
inactive
数一数,因为它不会准确。这是因为在我们需要的地方没有过滤器。再次上钩。。。
if( $this->get_plugin_status() )
{
add_filter( \'views_\' . $screen->id, array( &$this, \'views_again\' ) );
}
和快速取消设置。。。
function views_again( $views )
{
if( isset( $views[\'inactive\'] ) ) unset( $views[\'inactive\'] );
return $views;
}
接下来,让我们去掉列表中的插件,用我们的自定义插件替换它们。钩入
all_plugins
.
if( $this->get_plugin_status() )
{
add_filter( \'views_\' . $screen->id, array( &$this, \'views_again\' ) );
add_filter( \'all_plugins\', array( &$this, \'filter_plugins\' ) );
}
因为我们已经设置了插件和数据(请参见
setup_plugins
,则
filter_plugins
方法just(1)保存所有插件的计数以备将来使用,并且(2)替换列表中的插件。
function filter_plugins( $plugins )
{
if( $key = $this->get_plugin_status() )
{
$this->all_count = count( $plugins );
$plugins = $this->plugins[$key];
}
return $plugins;
}
现在我们将取消批量操作。我想这些很容易得到支持吧?
if( $this->get_plugin_status() )
{
add_filter( \'views_\' . $screen->id, array( &$this, \'views_again\' ) );
add_filter( \'all_plugins\', array( &$this, \'filter_plugins\' ) );
// TODO: support bulk actions
add_filter( \'bulk_actions-\' . $screen->id, \'__return_empty_array\' );
}
默认的插件操作链接对我们不起作用。所以,我们需要建立自己的(使用自定义操作等)。在
init
作用
if( $this->get_plugin_status() )
{
add_filter( \'views_\' . $screen->id, array( &$this, \'views_again\' ) );
add_filter( \'all_plugins\', array( &$this, \'filter_plugins\' ) );
// TODO: support bulk actions
add_filter( \'bulk_actions-\' . $screen->id, \'__return_empty_array\' );
add_filter( \'plugin_action_links\', array( &$this, \'action_links\' ), 10, 2 );
}
这里唯一需要更改的是(1)我们正在更改操作,(2)保持插件状态,以及(3)稍微更改nonce名称。
function action_links( $links, $plugin_file )
{
$context = $this->get_plugin_status();
// let\'s just start over
$links = array();
$links[\'activate\'] = sprintf(
\'<a href="%s" title="Activate this plugin">%s</a>\',
wp_nonce_url( \'plugins.php?action=custom_activate&plugin=\' . $plugin_file . \'&plugin_status=\' . esc_attr( $context ), \'custom_activate-\' . $plugin_file ),
__( \'Activate\' )
);
$active = get_option( \'active_plugins_\' . $context, array() );
if( in_array( $plugin_file, $active ) )
{
$links[\'deactivate\'] = sprintf(
\'<a href="%s" title="Deactivate this plugin" class="cd-apd-deactivate">%s</a>\',
wp_nonce_url( \'plugins.php?action=custom_deactivate&plugin=\' . $plugin_file . \'&plugin_status=\' . esc_attr( $context ), \'custom_deactivate-\' . $plugin_file ),
__( \'Deactivate\' )
);
}
return $links;
}
最后,我们只需要将一些JavaScript排队以结束它。在
init
再次运行(这次全部一起运行)。
if( $this->get_plugin_status() )
{
add_filter( \'views_\' . $screen->id, array( &$this, \'views_again\' ) );
add_filter( \'all_plugins\', array( &$this, \'filter_plugins\' ) );
// TODO: support bulk actions
add_filter( \'bulk_actions-\' . $screen->id, \'__return_empty_array\' );
add_filter( \'plugin_action_links\', array( &$this, \'action_links\' ), 10, 2 );
add_action( \'admin_enqueue_scripts\', array( &$this, \'scripts\' ) );
}
在将我们的JS排队时,我们还将使用
wp_localize_script
获取“所有插件”总数的值。
function scripts()
{
wp_enqueue_script(
\'cd-apd-js\',
CD_APD_URL . \'js/apd.js\',
array( \'jquery\' ),
null
);
wp_localize_script(
\'cd-apd-js\',
\'cd_apd\',
array(
\'count\' => esc_js( $this->all_count )
)
);
}
当然,JS只是一些很好的技巧,可以让列表表的活动/非活动插件正确显示。我们还将把所有插件的正确计数粘贴回
All
链接
jQuery(document).ready(function(){
jQuery(\'li.all a\').removeClass(\'current\').find(\'span.count\').html(\'(\' + cd_apd.count + \')\');
jQuery(\'.wp-list-table.plugins tr\').each(function(){
var is_active = jQuery(this).find(\'a.cd-apd-deactivate\');
if(is_active.length) {
jQuery(this).removeClass(\'inactive\').addClass(\'active\');
jQuery(this).find(\'div.plugin-version-author-uri\').removeClass(\'inactive\').addClass(\'active\');
}
});
});
Wrap Up
额外插件目录的实际加载非常乏味。让列表正确显示是更困难的部分。我仍然不完全满意结果,但也许有人可以
improve the code