Được rồi, tôi sẽ đâm sau này. Một số hạn chế tôi gặp phải trên đường đi:
Không có nhiều bộ lọc trong các lớp con của WP_List_Table, ít nhất là không có nơi nào chúng ta cần chúng ở đó.
Do thiếu bộ lọc, chúng tôi thực sự không thể duy trì một danh sách chính xác các loại plugin ở đầu.
Chúng tôi cũng phải sử dụng một số hack JavaScript tuyệt vời (đọc: bẩn) để hiển thị các plugin khi hoạt động.
Tôi đã bọc mã vùng quản trị của mình bên trong một lớp, vì vậy tên hàm của tôi không có tiền tố. Bạn có thể xem tất cả các mã này ở đây . Hãy đóng góp!
API trung tâm
Chỉ cần một chức năng đơn giản thiết lập một biến toàn cục sẽ chứa các thư mục plugin của chúng tôi trong một mảng kết hợp. Đây $key
sẽ là một cái gì đó được sử dụng nội bộ để tìm nạp các plugin, v.v. $dir
hoặc là một đường dẫn đầy đủ hoặc một cái gì đó liên quan đến wp-content
thư mục. $label
sẽ được hiển thị cho khu vực quản trị của chúng tôi (ví dụ: chuỗi có thể dịch).
<?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
);
}
Sau đó, tất nhiên, chúng ta cần tải các plugin. Đi vào plugins_loaded
muộn và đi qua các plugin đang hoạt động, tải từng plugin.
Khu vực quản trị
Hãy thiết lập chức năng của chúng tôi trong một lớp.
<?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
Chúng tôi sẽ kết nối plugins_loaded
sớm và thiết lập các "hành động" được phép mà chúng tôi sẽ sử dụng. Chúng sẽ xử lý kích hoạt và hủy kích hoạt plugin vì các hàm tích hợp không thể làm điều đó với các thư mục tùy chỉnh.
function setup_actions()
{
$tmp = array(
'custom_activate',
'custom_deactivate'
);
$this->actions = apply_filters( 'custom_plugin_actions', $tmp );
}
Sau đó, có chức năng móc vào load-plugins.php
. Điều này làm tất cả các loại công cụ thú vị.
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' ) );
}
}
Chúng ta hãy đi qua một điều tại một thời điểm. các get_plugins
phương pháp, là một wrapper quanh chức năng khác. Nó điền thuộc tính plugins
với dữ liệu.
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
là sự lột xác của hàm dựng sẵn get_plugins
mà không cần mã hóa WP_CONTENT_DIR
và plugins
kinh doanh. Về cơ bản: lấy thư mục từ $wp_plugin_directories
toàn cầu, mở nó, tìm tất cả các tệp plugin. Lưu trữ chúng trong bộ nhớ cache để sau.
<?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;
}
Tiếp theo là công việc phiền phức khi thực sự kích hoạt và hủy kích hoạt các plugin. Để làm điều này, chúng tôi sử dụng handle_actions
phương pháp. Đây là, một lần nữa, bị gạt một cách trắng trợn từ đầu wp-admin/plugins.php
tập tin cốt lõi .
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;
}
}
Một vài chức năng tùy chỉnh ở đây một lần nữa. cd_apd_activate_plugin
(tách ra từ activate_plugin
) và cd_apd_deactivate_plugins
(tách ra từ deactivate_plugins
). Cả hai đều giống như các hàm "cha" tương ứng của chúng mà không có các thư mục được mã hóa cứng.
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;
}
Và chức năng hủy kích hoạt
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 );
}
Ngoài ra còn có cd_apd_validate_plugin
chức năng, tất nhiên, là một sự lột xác validate_plugin
mà không có rác được mã hóa cứng.
<?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;
}
Được rồi, với điều đó ra khỏi đường đi. Chúng ta thực sự có thể bắt đầu nói về việc hiển thị bảng danh sách
Bước 1: thêm lượt xem của chúng tôi vào danh sách ở đầu bảng. Điều này được thực hiện bằng cách lọc views_{$screen->id}
bên trong init
chức năng của chúng tôi .
add_filter( 'views_' . $screen->id, array( &$this, 'views' ) );
Sau đó, hàm hooked thực tế chỉ cần vòng qua $wp_plugin_directories
. Nếu một trong những thư mục mới được đăng ký có plugin, chúng tôi sẽ đưa nó vào màn hình.
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;
}
Điều đầu tiên chúng ta cần làm nếu tình cờ xem trang thư mục plugin tùy chỉnh là lọc lại lượt xem. Chúng ta cần loại bỏ inactive
số đếm vì nó sẽ không chính xác. Hậu quả của việc không có bộ lọc mà chúng ta cần chúng là. Kết nối lại ...
if( $this->get_plugin_status() )
{
add_filter( 'views_' . $screen->id, array( &$this, 'views_again' ) );
}
Và nhanh chóng bỏ qua ...
function views_again( $views )
{
if( isset( $views['inactive'] ) ) unset( $views['inactive'] );
return $views;
}
Tiếp theo, hãy loại bỏ các plugin mà bạn có thể thấy trong bảng danh sách và thay thế chúng bằng các plugin tùy chỉnh của chúng tôi. Móc vào all_plugins
.
if( $this->get_plugin_status() )
{
add_filter( 'views_' . $screen->id, array( &$this, 'views_again' ) );
add_filter( 'all_plugins', array( &$this, 'filter_plugins' ) );
}
Vì chúng tôi đã thiết lập các plugin và dữ liệu của mình (xem setup_plugins
bên trên), filter_plugins
phương thức chỉ (1) lưu số đếm trên tất cả các plugin cho sau này và (2) thay thế các plugin trong bảng danh sách.
function filter_plugins( $plugins )
{
if( $key = $this->get_plugin_status() )
{
$this->all_count = count( $plugins );
$plugins = $this->plugins[$key];
}
return $plugins;
}
Và bây giờ chúng ta sẽ giết các hành động số lượng lớn. Chúng có thể dễ dàng được hỗ trợ, tôi cho rằng?
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' );
}
Các liên kết hành động plugin mặc định sẽ không hoạt động đối với chúng tôi. Vì vậy, thay vào đó, chúng ta cần thiết lập riêng của mình (với các hành động tùy chỉnh, v.v.). Trong init
chức năng.
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 );
}
Điều duy nhất được thay đổi ở đây là (1) chúng tôi đang thay đổi hành động, (2) giữ trạng thái plugin và (3) thay đổi tên nonce một chút.
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;
}
Và cuối cùng, chúng ta chỉ cần ghi lại một số JavaScript để hoàn thành nó. Trong init
chức năng một lần nữa (tất cả cùng nhau lần này).
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' ) );
}
Trong khi chinh phụcJJ của chúng tôi, chúng tôi cũng sẽ sử dụng wp_localize_script
để có được giá trị của tổng số "tất cả các plugin".
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 )
)
);
}
Và tất nhiên, JS chỉ là một số hack đẹp để bảng danh sách các plugin hoạt động / không hoạt động hiển thị đúng. Chúng tôi cũng sẽ gắn số đếm chính xác của tất cả các plugin vào All
liên kết.
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');
}
});
});
Gói lại
Việc tải thực tế của các thư mục plugin bổ sung là khá khó chịu. Lấy bảng danh sách để hiển thị chính xác là phần khó khăn hơn. Tôi vẫn chưa hoàn toàn hài lòng với cách nó bật ra, nhưng có lẽ ai đó có thể cải thiện mã