32

我有许多为 WordPress 编写的插件,现在我想将它们改编为 MU。为了“升级”我的插件以支持多站点安装,我必须遵循/避免/适应
哪些注意事项/最佳实践/工作流程/功能/陷阱?

例如但不限于:

  • 排队脚本/注册
  • 包含文件(php、图像)
  • 自定义文件上传路径
  • $wpdb
  • 激活、卸载、停用
  • 管理特定页面的处理

在 Codex 中,有时会在单个功能描述中提到 Multisite,但我没有找到任何涉及此主题的一站式页面。

4

1 回答 1

71

至于入队和包含,一切正常。插件路径和 URL 相同。

我从来没有处理过与 Multisite 中的上传路径相关的任何事情,我猜通常 WP 会处理这个问题。


$wpdb

有一个常用的片段可以遍历所有博客:

global $wpdb;
$blogs = $wpdb->get_results("
    SELECT blog_id
    FROM {$wpdb->blogs}
    WHERE site_id = '{$wpdb->siteid}'
    AND spam = '0'
    AND deleted = '0'
    AND archived = '0'
");
$original_blog_id = get_current_blog_id();   
foreach ( $blogs as $blog_id ) 
{
    switch_to_blog( $blog_id->blog_id );
    // do something in the blog, like:
    // update_option()
}   
switch_to_blog( $original_blog_id );

您可能会找到restore_current_blog()使用 代替 的示例switch_to_blog( $original_blog_id )。但这switch就是更可靠的原因:restore_current_blog() 与 switch_to_blog()


$blog_id

根据博客ID执行一些函数或钩子:

global $blog_id;
if( $blog_id != 3 )
    add_image_size( 'category-thumb', 300, 9999 ); //300 pixels wide (and unlimited height)

或许:

if( 
    'child.multisite.com' === $_SERVER['SERVER_NAME'] 
    || 
    'domain-mapped-child.com' === $_SERVER['SERVER_NAME']
    )
{
    // do_something();
}

安装 - 仅限网络激活

使用插件标题Network: true (参见:示例插件只会在页面中显示插件/wp-admin/network/plugins.php。有了这个标头,我们可以使用以下内容来阻止某些操作,如果插件是仅限网络的。

function my_plugin_block_something()
{
    $plugin = plugin_basename( __FILE__ );
    if( !is_network_only_plugin( $plugin ) )
        wp_die(
            'Sorry, this action is meant for Network only', 
            'Network only',  
            array( 
                'response' => 500, 
                'back_link' => true 
            )
        );    
}

卸载

对于 (De)Activation,它取决于每个插件。但是,对于卸载,这是我在文件中使用的代码uninstall.php

<?php
/**
 * Uninstall plugin - Single and Multisite
 * Source: https://wordpress.stackexchange.com/q/80350/12615
 */

// Make sure that we are uninstalling
if ( !defined( 'WP_UNINSTALL_PLUGIN' ) ) 
    exit();

// Leave no trail
$option_name = 'HardCodedOptionName';

if ( !is_multisite() ) 
{
    delete_option( $option_name );
} 
else 
{
    global $wpdb;
    $blog_ids = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
    $original_blog_id = get_current_blog_id();

    foreach ( $blog_ids as $blog_id ) 
    {
        switch_to_blog( $blog_id );
        delete_option( $option_name );    
    }
    switch_to_blog( $original_blog_id );
}

管理页面

1)添加管理页面

要添加管理菜单,我们检查是否is_multisite()并相应地修改钩子:

$hook = is_multisite() ? 'network_' : '';
add_action( "{$hook}admin_menu", 'unique_prefix_function_callback' );

2) 检查多站点仪表板并修改管理 URL:

// Check for MS dashboard
if( is_network_admin() )
    $url = network_admin_url( 'plugins.php' );
else
    $url = admin_url( 'plugins.php' );

3) 仅在主站点中显示界面元素的解决方法

如果不创建网络管理菜单(操作挂钩network_admin_menu),则可以仅在主站点中显示插件的某些部分。

我开始在我最大的插件中包含一些多站点功能,并执行以下操作以将插件选项的一部分限制到主站点。意思是,如果插件在子站点中激活,该选项将不会显示

$this->multisite = is_multisite() 
        ? ( is_super_admin() && is_main_site() ) // must meet this 2 conditions to be "our multisite"
        : false;

再看一遍,也许可以简单的说:is_multisite() && is_super_admin() && is_main_site()。请注意,最后两个返回true单个站点。

进而:

if( $this->multisite )
    echo "Something only for the main site, i.e.: Super Admin!";

4) 有用的钩子和函数的集合。

挂钩: network_admin_menu, wpmu_new_blog, signup_blogform, wpmu_blogs_columns, manage_sites_custom_column, manage_blogs_custom_column, wp_dashboard_setup, network_admin_notices, site_option_active_sitewide_plugins,{$hook}admin_menu

功能: is_multisite, , , , , ,is_super_admin is_main_siteget_blogs_of_userupdate_blog_optionis_network_adminnetwork_admin_urlis_network_only_plugin

PS:我宁愿链接到 WordPress Answers 而不是 Codex,因为会有更多的工作代码示例。


示例插件

我刚刚推出了一个多站点插件Network Deactivated but Active Elsewhere,并在下面制作了一个非工作的恢复注释版本(有关完成的完整工作版本,请参见 GitHub)。完成的插件纯粹是功能性的,没有设置界面。

请注意,插件标头具有Network: true. 它可以防止插件显示在子站点中。

<?php
/**
 * Plugin Name: Network Deactivated but Active Elsewhere
 * Network: true
 */ 

/**
 * Start the plugin only if in Admin side and if site is Multisite
 */
if( is_admin() && is_multisite() )
{
    add_action(
        'plugins_loaded',
        array ( B5F_Blog_Active_Plugins_Multisite::get_instance(), 'plugin_setup' )
    );
}    

/**
 * Based on Plugin Class Demo - https://gist.github.com/toscho/3804204 
 */
class B5F_Blog_Active_Plugins_Multisite
{
    protected static $instance = NULL;
    public $blogs = array();
    public $plugin_url = '';
    public $plugin_path = '';

    public static function get_instance()
    {
        NULL === self::$instance and self::$instance = new self;
        return self::$instance;
    }

    /**
     * Plugin URL and Path work as normal
     */
    public function plugin_setup()
    {
        $this->plugin_url    = plugins_url( '/', __FILE__ );
        $this->plugin_path   = plugin_dir_path( __FILE__ );
        add_action( 
            'load-plugins.php', 
            array( $this, 'load_blogs' ) 
        );
    }

    public function __construct() {}

    public function load_blogs()
    { 
        /**
         * Using "is_network" property from $current_screen global variable.
         * Run only in /wp-admin/network/plugins.php
         */
        global $current_screen;
        if( !$current_screen->is_network )
            return;

        /**
         * A couple of Multisite-only filter hooks and a regular one.
         */
        add_action( 
                'network_admin_plugin_action_links', 
                array( $this, 'list_plugins' ), 
                10, 4 
        );
        add_filter( 
                'views_plugins-network', // 'views_{$current_screen->id}'
                array( $this, 'inactive_views' ), 
                10, 1 
        );
        add_action(
                'admin_print_scripts',
                array( $this, 'enqueue')
        );

        /**
         * This query is quite frequent to retrieve all blog IDs.
         */
        global $wpdb;
        $this->blogs = $wpdb->get_results(
                " SELECT blog_id, domain 
                FROM {$wpdb->blogs}
                WHERE site_id = '{$wpdb->siteid}'
                AND spam = '0'
                AND deleted = '0'
                AND archived = '0' "
        );  
    }

    /**
     * Enqueue script and style normally.
     */
    public function enqueue()
    {
        wp_enqueue_script( 
                'ndbae-js', 
                $this->plugin_url . '/ndbae.js', 
                array(), 
                false, 
                true 
        );
        wp_enqueue_style( 
                'ndbae-css', 
                $this->plugin_url . '/ndbae.css'
        );
    }

    /**
     * Check if plugin is active in any blog
     * Using Multisite function get_blog_option
     */
    private function get_network_plugins_active( $plug )
    {
        $active_in_blogs = array();
        foreach( $this->blogs as $blog )
        {
            $the_plugs = get_blog_option( $blog['blog_id'], 'active_plugins' );
            foreach( $the_plugs as $value )
            {
                if( $value == $plug )
                    $active_in_blogs[] = $blog['domain'];
            }
        }
        return $active_in_blogs;
    }
}

其他资源 - 电子书

与插件开发没有直接关系,但对多站点管理至关重要。
电子书的作者不少于 Multisite 的两大巨头:Mika Epstein(又名 Ipstenu)和 Andrea Rennick。

于 2013-04-11T03:42:19.313 回答