Добавить несколько плагинов

Задача

Вы можете зарегистрировать дополнительные каталоги Темы, используя register_theme_directory () для вашей установки WP. К сожалению, ядро ​​не обеспечивает такую ​​же функциональность для плагинов. У нас уже есть MU-плагин, Drop-Ins, плагины и темы. Но нам нужно больше для лучшей организации файлов.

Вот список задач для достижения:

  • Добавить дополнительный каталог плагинов
  • Для каждого каталога плагинов необходима новая «вкладка», как показано здесь [1]
  • Дополнительный каталог будет иметь те же функции, что и каталог плагина по умолчанию

Что там для вас?

Самый лучший и самый полный ответ будет отмечен наградой.


[1] Дополнительная вкладка для новой папки /каталога плагина

37 голосов | спросил kaiser 23 FebruaryEurope/MoscowbThu, 23 Feb 2012 16:58:40 +0400000000pmThu, 23 Feb 2012 16:58:40 +040012 2012, 16:58:40

3 ответа


25

Хорошо, я возьму на это удар. Некоторые ограничения, которые я встречал на этом пути:

  1. В подклассах WP_List_Table не так много фильтров, меньше всего там, где они нам нужны.

  2. Из-за отсутствия фильтров мы не можем поддерживать точное список типов плагинов вверху.

  3. Мы также должны использовать некоторые потрясающие (читай: грязные) JavaScript-хаки для отображать плагины как активные.

Я завернул код моей административной зоны внутри класса, поэтому мои имена функций не префиксны. Вы можете увидеть весь этот код здесь . Пожалуйста, внесите свой вклад!

Центральный API

Просто простая функция, которая устанавливает глобальную переменную, которая будет содержать наши каталоги плагинов в ассоциативном массиве. $ key будет использоваться внутри, чтобы извлекать плагины и т. Д. $ dir - это полный путь или что-то относительно wp-content code>. $ label будет отображаться в нашей области администрирования (например, переводимая строка).

  & л;? PHP
function register_plugin_directory ($ key, $ dir, $ label)
{
    глобальные $ wp_plugin_directories;
    if (empty ($ wp_plugin_directories)) $ wp_plugin_directories = array ();

    if (! file_exists ($ dir) & amp; & amp; file_exists (trailingslashit (WP_CONTENT_DIR). $ dir))
    {
        $ dir = trailingslashit (WP_CONTENT_DIR). $ Реж;
    }

    $ wp_plugin_directories [$ key] = массив (
        'label' => $ Этикетка,
        'dir' => $ реж
    );
}
 

Тогда, конечно, нам нужно загрузить плагины. Подключитесь к plugins_loaded позже и пройдите через активные плагины, загрузив каждый.

Область администратора

Давайте создадим нашу функциональность внутри класса.

  & л;? PHP
класс CD_APD_Admin
{

    /**
     * Контейнер для всех наших пользовательских плагинов
     * /
    protected $ plugins = array ();

    /**
     * Какие пользовательские действия нам разрешено обрабатывать здесь?
     * /
    protected $ actions = array ();

    /**
     * Исходное количество плагинов
     * /
    protected $ all_count = 0;

    /**
     конструктор *
     *
     * @ с 0,1
     * /
    функция __construct ()
    {
        add_action ('load-plugins.php', array (& amp; $ this, 'init'));
        add_action ('plugins_loaded', array (& amp; $ this, 'setup_actions'), 1);

    }

} //конец класса
 

Мы собираемся подключиться к plugins_loaded на самом деле рано и настроить допустимые «действия», которые мы будем использовать. Они будут обрабатывать активацию и деактивацию плагинов, поскольку встроенные функции не могут сделать это с помощью настраиваемых каталогов.

  function setup_actions ()
{
    $ tmp = array (
        'Custom_activate',
        'Custom_deactivate'
    );
    $ this-> действия = apply_filters ('custom_plugin_actions', $ tmp);
}
 

Тогда есть функция, подключенная к load-plugins.php . Это делает всевозможные забавные вещи.

  функция init ()
{
    глобальные $ wp_plugin_directories;

    $ screen = get_current_screen ();

    $ Этом- & GT; get_plugins ();

    $ Этом- & GT; handle_actions ();

    add_filter ('views_'. $ screen-> id, array (& amp; $ this, 'views'));

    //проверяем, используем ли мы один из наших настраиваемых каталогов
    if ($ this-> get_plugin_status ())
    {
        add_filter ('views_'. $ screen-> id, array (& amp; $ this, 'views_again'));
        add_filter ('all_plugins', array (& amp; $ this, 'filter_plugins'));
        //TODO: поддержка массовых действий
        add_filter ('bulk_actions-'. $ screen-> id, '__return_empty_array');
        add_filter ('plugin_action_links', array (& amp; $ this, 'action_links'), 10, 2);
        add_action ('admin_enqueue_scripts', array (& amp; $ this, 'scripts'));
    }
}
 

Давайте рассмотрим это одно за раз. метод get_plugins , является оболочкой вокруг другой функции. Он заполняет атрибут плагинами данными.

  function get_plugins ()
{
    глобальные $ wp_plugin_directories;
    foreach (array_keys ($ wp_plugin_directories) как $ key)
    {
       $ this-> плагины [$ key] = cd_apd_get_plugins ($ key);
    }
}
 

cd_apd_get_plugins - это копирование встроенной функции get_plugins без жесткого кода WP_CONTENT_DIR и плагинов . В принципе: получите каталог из $ wp_plugin_directories global, откройте его, найдите все файлы плагина. Храните их в кеше позже.

  & л;? PHP
функция cd_apd_get_plugins ($ dir_key)
{
    глобальные $ wp_plugin_directories;

    //недействительный ключ-ключ? порука
    if (! isset ($ wp_plugin_directories [$ dir_key]))
    {
        return array ();
    }
    еще
    {
        $ plugin_root = $ wp_plugin_directories [$ dir_key] ['dir'];
    }

    if (! $ cache_plugins = wp_cache_get ('plugins', 'plugins'))
        $ cache_plugins =Массив ();

    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) == '.')
                Продолжать;
            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) == '.')
                            Продолжать;
                        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 как $ plugin_file) {
        if (! is_readable ("$ plugin_root /$ plugin_file"))
            Продолжать;

        $ plugin_data = get_plugin_data ("$ plugin_root /$ plugin_file", false, false); //Не применяйте разметку /перевод, поскольку он будет кэшироваться.

        if (empty ($ plugin_data ['Name']))
            Продолжать;

        $ 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']: '';

    //не разрешено обрабатывать это действие? под залог.
    if (! in_array ($ action, $ this-> действия)) return;

    //Получить плагин, который мы собираемся активировать
    $ plugin = isset ($ _REQUEST ['plugin'])? $ _REQUEST ['plugin']: false;
    if (! $ plugin) return;

    $ context = $ this-> get_plugin_status ();

    переключатель ($ action)
    {
        case 'custom_activate':
            if (! current_user_can ('activate_plugins'))
                    wp_die (__ ('У вас недостаточно прав для управления плагинами для этого сайта.'));

            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));
                    Выход();
                }
                еще
                {
                    wp_die ($ result);
                }
            }

            wp_redirect (add_query_arg (array ('plugin_status' => $ context, 'activate' => 'true'), self_admin_url ('plugins.php')));
            Выход();
            ломать;
        case 'custom_deactivate':
            if (! current_user_can ('activate_plugins'))
                wp_die (__ ('У вас нет достаточных разрешений для деактивации плагинов для этого сайта.'));

            check_admin_referer (плагин `custom_deactivate- '. $);
            cd_apd_deactivate_plugins ($ plugin, $ context);
            if (headers_sent ())
                echo "<meta http-equiv = 'refresh' content = '". esc_attr ("0; url = plugins.php? deactivate = true & amp; plugin_status = $ status & amp; paged = $ page & amp; s = $ s"). "/& gt;";
            еще
                wp_redirect (self_admin_url ("plugins.php? deactivate = true & amp; plugin_status = $ context"));
            Выход();
            ломать;
        по умолчанию:
            do_action ('custom_plugin_dir_'. $ action);
            ломать;
    }

}
 

Пара дополнительных функций здесь снова. 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)); //мы переопределим это позже, если плагин может быть включен без фатальной ошибки
        ob_start ();
        include_once ($ valid);

        if (! $ silent) {
            do_action ('custom_activate_plugin', $ plugin, $ context);
            do_action ('custom_activate_'. $ plugin, $ context);
        }

        $ current [] = $ плагин;
        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', __ ('Плагин генерирует неожиданный вывод.'), $ output);
        }
        ob_end_clean ();
    }

    return true;
}
 

И функция дезактивации

  function cd_apd_deactivate_plugins ($ plugins, $ context, $ silent = false) {
    $ current = get_option ('active_plugins_'. $ context, array ());

    foreach ((массив) $ плагинов как плагин $)
    {
        $ 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
функция cd_apd_validate_plugin ($ plugin, $ context)
{
    $ rv = true;
    if (validate_file ($ plugin))
    {
        $ rv = new WP_Error ('plugin_invalid', __ ('Недопустимый путь к плагину.'));
    }

    глобальные $ wp_plugin_directories;
    if (! isset ($ wp_plugin_directories [$ context]))
    {
        $ rv = new WP_Error ('invalid_context', __ ('Контекст для этого плагина не существует'));
    }

    $ dir = $ wp_plugin_directories [$ context] ['dir'];
    if (! file_exists ($ dir. '/'. $ plugin))
    {
        $ rv = new WP_Error ('plugin_not_found', __ ('Файл плагина не существует.'));
    }

    $ installed_plugins = cd_apd_get_plugins ($ context);
    if (! isset ($ installed_plugins [$ plugin]))
    {
        $ rv = new WP_Error ('no_plugin_header', __ ('Плагин не имеет допустимого заголовка.'));
    }

    $ rv = $ dir. '/'. $ Плагин;
    return $ rv;
}
 

Хорошо, с этим с дороги. Мы действительно можем начать говорить о отображении таблицы списка

Шаг 1: добавьте наши представления в список вверху таблицы. Это делается путем фильтрации views _ {$ screen-> id} внутри нашей функции init .

  add_filter ('views_'. $ screen-> id, array (& amp; $ this, 'views'));
 

Затем фактическая функция зацепления просто прокручивается через $ wp_plugin_directories . Если в одном из недавно зарегистрированных каталогов есть плагины, мы включим его на дисплей.

  представления функций ($ views)
{
    глобальные $ wp_plugin_directories;

    //поручитесь, если у нас нет дополнительных ресурсов
    if (empty ($ wp_plugin_directories)) возвращает $ views;

    //Добавьте наши каталоги в ссылки действий
    foreach ($ wp_plugin_directories как $ key => $ info)
    {
        if (! count ($ this-> плагины [$ key])) continue;
        $ class = $ this-> get_plugin_status () == $ key? 'class = "current"': '';
        $ views [$ key] = sprintf (
            '<a href = "% s"'. $ class.>% s <span class = "count"> (% d) </span> </a & gt ;, '
            add_query_arg ('plugin_status', $ key, 'plugins.php'),
            esc_html ($ info ['label']),
            count ($ this-> плагины [$ key])
        );
    }
    return $ views;
}
 

Первое, что нам нужно сделать, если мы увидим страницу пользовательского каталога плагинов, снова фильтрует представления. Нам нужно избавиться от подсчета inactive , потому что это не будет точно. Следствием отсутствия фильтров там, где они нам нужны. Подцепите снова ...

  if ($ this-> get_plugin_status ())
{
    add_filter ('views_'. $ screen-> id, array (& amp; $ 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 (& amp; $ this, 'views_again'));
    add_filter ('all_plugins', array (& amp; $ this, 'filter_plugins'));
}
 

Поскольку мы уже настроили наши плагины и данные (см. setup_plugins выше), метод filter_plugins просто (1) сохраняет счетчик для всех плагинов для более поздней версии и (2) заменяет плагины в таблице списка.

  function filter_plugins ($ plugins)
{
    if ($ key = $ this-> get_plugin_status ())
    {
        $ this-> all_count = count ($ plugins);
        $ plugins = $ this-> плагины [$ key];
    }
    return $ plugins;
}
 

И теперь мы убьем массовые действия. Возможно, они могут быть поддержаны?

  if ($ this-> get_plugin_status ())
{
    add_filter ('views_'. $ screen-> id, array (& amp; $ this, 'views_again'));
    add_filter ('all_plugins', array (& amp; $ this, 'filter_plugins'));
    //TODO: поддержка массовых действий
    add_filter ('bulk_actions-'. $ screen-> id, '__return_empty_array');
}
 

Ссылки по умолчанию для плагинов не будут работать для нас. Поэтому вместо этого нам нужно настроить свои собственные (с помощью пользовательских действий и т. Д.). В init .

  if ($ this-> get_plugin_status ())
{
    add_filter ('views_'. $ screen-> id, array (& amp; $ this, 'views_again'));
    add_filter ('all_plugins', array (& amp; $ this, 'filter_plugins'));
    //TODO: поддержка массовых действий
    add_filter ('bulk_actions-'. $ screen-> id, '__return_empty_array');
    add_filter ('plugin_action_links', array (& amp; $ this, 'action_links'), 10, 2);
}
 

Единственное, что здесь изменилось, это: (1) мы меняем действия, (2) сохраняем статус плагина и (3) немного меняем имена nonce.

  function action_links ($ links, $ plugin_file)
{
    $ context = $ this-> get_plugin_status ();

    //давайте начнем
    $ links = array ();
    $ links ['activate'] = sprintf (
        '<a href = "% s" title = "Активировать этот плагин">% s </a>',
        wp_nonce_url ('plugins.php? action = custom_activate & amp; plugin ='. $ plugin_file. '& amp; amp; plugin_status ='. esc_attr ($ context), 'custom_activate-'. $ plugin_file)
        __ ('Активировать')
    );

    $ active = get_option ('active_plugins_'. $ context, array ());
    if (in_array ($ plugin_file, $ active))
    {
        $ links ['deactivate'] = sprintf (
            '<a href = "% s" title = "Деактивировать этот плагин" class = "cd-apd-deactivate">% s </a & gt ;,'
            wp_nonce_url ('plugins.php? action = custom_deactivate & amp; plugin ='. $ plugin_file. '& amp; amp; plugin_status ='. esc_attr ($ context), 'custom_deactivate-'. $ plugin_file)
            __ ('Деактивировать')
        );
    }
    return $ links;
}
 

И, наконец, нам просто нужно поставить в очередь некоторый JavaScript, чтобы от него отказаться. В функции init снова (все вместе это время).

  if ($ this-> get_plugin_status ())
{
    add_filter ('views_'. $ screen-> id, array (& amp; $ this, 'views_again'));
    add_filter ('all_plugins', array (& amp; $ this, 'filter_plugins'));
    //TODO: поддержка массовых действий
    add_filter ('bulk_actions-'. $ screen-> id, '__return_empty_array');
    add_filter ('plugin_action_links', array (& amp; $ this, 'action_links'), 10, 2);
    add_action ('admin_enqueue_scripts', array (& amp; $ this, 'scripts'));
}
 

В то время как enqueueing ourJS, мы также будем использовать wp_localize_script , чтобы получить значение общего количества всех плагинов.

  function scripts ()
{
    wp_enqueue_script (
        'CD-APD-JS',
        CD_APD_URL. 'JS /apd.js',
        array ('jquery'),
        ноль
    );
    wp_localize_script (
        'CD-APD-JS',
        'Cd_apd',
        массив (
            'count' => esc_js ($ this-> all_count)
        )
    );
}
 

И, конечно же, JS - это просто приятные хаки, чтобы отображать активные /неактивные плагины таблицы списков для правильной отображения. Мы также вернем правильный подсчет всех плагинов обратно в ссылку All .

  JQuery (документ) .ready (функция () {
    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 (это) .removeClass ( 'неактивные') addClass ( 'активный').
            JQuery (это) .find ( 'div.plugin-версия-автор-URI') removeClass ( 'неактивные') addClass ( 'активный')..;
        }
    });
});
 

Обернуть

Фактическая загрузка дополнительных каталогов плагина довольно неинтересна. Правильное отображение таблицы списковболее сложная часть. Я все еще не полностью доволен тем, как это получилось, но, возможно, кто-то может улучшить код

ответил chrisguitarguy 9 MaramFri, 09 Mar 2012 10:42:13 +04002012-03-09T10:42:13+04:0010 2012, 10:42:13
0

Я лично не заинтересован в изменении пользовательского интерфейса, но мне хотелось бы более организованного макета файловой системы по нескольким причинам.

С этой целью другим подходом было бы использовать символические ссылки.

  сор-контент
    | - Общение
        | - Охота и рыбалка ../plugins-custom/acme-widgets
        | - Охота и рыбалка ../plugins-custom/acme-custom-post-types
        | - Охота и рыбалка ../plugins-custom/acme-business-logic
        | - Авторизация | ../plugins-external/google-authenticator
        | - Отдых и развлечения ../plugins-external/rest-api
        | - Общение ../plugins-external/quick-navigation-interface
    | - Общение
        | - Охота и рыбалка
        | - Охота и рыбалка
        | - Охота и рыбалка
    | - Общение
        | - Общение
        | - Общение
        | - Общение
 

Вы можете настроить свои собственные плагины в plugins-custom , которые могут быть частью репозитория управления версиями вашего проекта.

Затем вы можете установить сторонние зависимости в plugins-external (через Composer, или подмодули Git или что угодно).

Тогда у вас может быть простой скрипт Bash или команда WP-CLI, которая сканирует дополнительные каталоги и создает символическую ссылку в плагинах для каждой вложенной папки, которую он находит.

плагины по-прежнему будут загромождены, но это не имеет значения, потому что вам нужно будет взаимодействовать с плагинами plugins-custom и -external код>.

Масштабирование до n дополнительных каталогов будет следовать тому же процессу, что и первые два.

ответил Ian Dunn 29 J000000Friday16 2016, 21:26:36
-2

Или вы также можете использовать COMPOSER с настраиваемым каталогом, установленным для указания на папку wp-content. Если это не прямой ответ на ваш вопрос, это новый способ мышления Wordpress, перейдите к композитору, прежде чем он съест вас.

ответил Franzscisco Mai 16 32016vEurope/Moscow11bEurope/MoscowWed, 16 Nov 2016 15:03:47 +0300 2016, 15:03:47

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132