Статьи

Рассечение текстового виджета WordPress

В этом руководстве мы подробно рассмотрим текстовый виджет WordPress, чтобы раскрыть процесс создания виджета, который можно добавить в несколько местоположений виджетов.

Если вы знаете основы программирования на PHP, создание виджета WordPress совсем не сложно. Я рассмотрел основы в предыдущем уроке «Анатомия плагина WordPress» — это хорошая идея, чтобы проверить его, прежде чем читать дальше, если вы раньше не создавали виджет.

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

Если вы добавляете виджет на одну из боковых панелей, кнопка «Добавить» виджета исчезает, и виджет не может быть добавлен в какой-либо другой слот до его удаления из первого.


Обратите внимание, что виджет «Архивы» больше не может быть добавлен.

В некоторых случаях это не проблема, поскольку природа виджета заключается в том, что он отображается только один раз. Например, вполне нормально размещать архив блога только в одном месте макета. Но есть много случаев, когда более чем один экземпляр может быть очень полезным. Одним из таких примеров является виджет Текст, который поставляется в комплекте с WordPress.

Виджет «Текст» — это простой виджет, который позволяет указывать заголовок и текст, также поддерживает HTML, и отображает его содержимое на боковой панели блога. Из-за этой простоты виджет может использоваться для самых разных целей. Например, на следующем изображении — снимок экрана с моего личного блога — вы можете увидеть, как я использую текстовый виджет на боковой панели, чтобы сначала показать текст приветствия, а затем снова, чтобы показать некоторые объявления внизу боковой панели. В документации WordPress такие виджеты называются «мультивиджетами».

Поскольку WordPress является проектом с открытым исходным кодом, мы можем копаться в исходном коде и использовать его в качестве справочного материала для наших собственных проектов. Для этого урока это означает, что мы можем более внимательно посмотреть на виджет «Текст» и увидеть, как он реализован, используя его в качестве примера для превращения наших собственных виджетов в мультивиджеты.

Первым шагом является поиск по базе кодов WordPress и поиск текстового виджета. Это интересный шаг, и я рекомендую вам посмотреть некоторые другие файлы здесь и там, чтобы получить общее представление о том, что происходит внутри платформы блогов.

Но если вам просто не терпится приступить к действиям, вот где вы найдете код виджета:


Код виджета находится в wordpress/wp-includes/widgets.php

Откройте файл и прокрутите его до самого конца, около строки 1958 (в версии 2.7.1), и там, скрытый в середине исходного кода, вы найдете пример виджета, закомментированный и помеченный как «Шаблон». для нескольких виджетов (допускает несколько экземпляров, таких как текстовый виджет). «

Сначала пример кода может выглядеть довольно устрашающе, поэтому мы рассмотрим его построчно, объясняя идеи, вокруг которых строится шаблон. Фрагменты кода в этом руководстве были скопированы из примера шаблона, но я сделал несколько небольших изменений здесь и там, чтобы сделать код проще для отслеживания (в основном, разбивая длинные строки на более короткие). Помимо этих небольших настроек, исходный код точно такой же, как вы найдете, открыв widgets.php из кодовой базы WordPress.

Когда пользователь нажимает кнопку «Сохранить изменения» в меню виджетов, WordPress собирает параметры формы из каждого виджета, указанного на боковых панелях, и отправляет их каждому из зарегистрированных виджетов. Эта функция делает возможным создание мультивиджетов: поскольку функция контроллера виджета получает уведомление о сохранении экземпляра виджета, все параметры передаются ему, и она может обновлять каждый экземпляр этого типа виджета за один раз, добавляя новые и удаление неиспользуемых по мере необходимости.

Мы рассмотрим детали процесса сохранения в ближайшее время, но сначала хорошее место, чтобы начать смотреть на то, как хранятся данные виджета для мультивиджета, — это посмотреть на ту часть кода, где используются данные. , Вот код для метода, используемого для показа экземпляра примера виджета:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
<?php
function widget_many( $args, $widget_args = 1 ) {
    extract( $args, EXTR_SKIP );
    if ( is_numeric($widget_args) )
        $widget_args = array( ‘number’ => $widget_args );
    $widget_args = wp_parse_args( $widget_args, array( ‘number’ => -1 ) );
    extract( $widget_args, EXTR_SKIP );
 
    // Data should be stored as array
    $options = get_option(‘widget_many’);
    if ( !isset($options[$number]) )
        return;
 
    echo $before_widget;
 
    // Do stuff for this widget, drawing data from $options[$number]
 
    echo $after_widget;
}
?>
  1. Первый параметр, передаваемый в функцию рендеринга виджета, $args , содержит общие настройки виджета, такие как то, что должно быть напечатано до и после виджета, и способ форматирования заголовка виджета. Код в строке 3 используется для разделения этой информации на локальные переменные, из которых в примере используются $before_widget и $after_widget .

  2. Второй параметр, $widget_args , более интересен для нас: он содержит идентификатор экземпляра виджета, который будет отображаться. Строки с 4 по 7 предназначены для того, чтобы убедиться, что параметр имеет правильный формат, и что в конце, после вызова extract в строке 7, мы можем найти индекс виджета в локальной переменной $number .

  3. Так же, как настройки виджета для простого одноразового виджета, настройки для экземпляра виджета хранятся в виде массива, содержащего пары ключ-значение для каждого параметра. Но так как теперь нам нужно хранить несколько экземпляров, вместо того, чтобы сохранять каждый массив с собственным вызовом update_option , мы помещаем их все в один массив, используя идентификатор экземпляра ( $number ) в качестве индекса, а затем сохраняем все это с идентификатором тип виджета.

  4. В приведенном выше примере у нас есть виджет с именем » widget_many «, и, скажем, мы добавили три его экземпляра в боковую панель нашего блога. При рендеринге одного из них мы сначала получаем массив, содержащий все экземпляры widget_many , вызывая get_option('widget_many'); (строка 10), а затем найдите данные для текущего экземпляра из этого массива (строка 16).

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?php
function wp_widget_text($args, $widget_args = 1) {
    extract( $args, EXTR_SKIP );
    if ( is_numeric($widget_args) )
        $widget_args = array( ‘number’ => $widget_args );
    $widget_args = wp_parse_args( $widget_args, array( ‘number’ => -1 ) );
    extract( $widget_args, EXTR_SKIP );
 
    $options = get_option(‘widget_text’);
    if ( !isset($options[$number]) )
        return;
 
    $title = apply_filters(‘widget_title’, $options[$number][‘title’]);
    $text = apply_filters( ‘widget_text’, $options[$number][‘text’] );
?>
        <?php echo $before_widget;
            <?php
                if ( !empty( $title ) ) {
                    echo $before_title .
                }
            ?>
            <div class=»textwidget»><?php echo $text;
        <?php echo $after_widget;
<?php
}
?>

В строках 13 и 14 вы можете видеть, как два параметра, необходимые для текстового виджета, читаются из опций, используя $number качестве идентификатора экземпляра. И в следующих десяти строках параметры отображаются на экране.

Анализируя функцию рендеринга виджетов, мы теперь раскрыли основы сохранения данных для мультивиджетов:

  1. Сохраните данные конкретного экземпляра виджета в массив с именами настроек (например, «заголовок») в качестве ключей
  2. Сохраните все экземпляры виджета в массив, используя идентификаторы экземпляров в качестве ключей
  3. Сохраните все это в базе данных, используя встроенную в WordPress систему get_option / update_option .

Теперь мы посмотрим поближе и увидим хитрости и предостережения.

Используя тот же подход, что и раньше, теперь мы рассмотрим функцию, используемую для отображения и обработки форм настроек, и пройдемся по ней построчно. Эта функция, widget_many_control , является функцией, которая сохраняет настройки и отображает форму настроек для примера виджета «widget_many». Он вызывается один раз для каждого экземпляра widget_many , всегда передавая идентификатор экземпляра в $widget_args .

Поскольку функция является частью обработки отправки формы, массив $_POST будет содержать все параметры, которые были отправлены с использованием формы редактирования виджета.

1
2
3
4
5
6
7
8
9
<?php
function widget_many_control( $widget_args = 1 ) {
    global $wp_registered_widgets;
    static $updated = false;
 
    if ( is_numeric($widget_args) )
        $widget_args = array( ‘number’ => $widget_args );
    $widget_args = wp_parse_args( $widget_args, array( ‘number’ => -1 ) );
    extract( $widget_args, EXTR_SKIP );

В первых строках функции вы увидите что-то знакомое (строки 6-9). Это тот же фрагмент кода, который использовался в функции рендеринга, чтобы убедиться, что $number был инициализирован с числовым идентификатором текущего экземпляра виджета.

Но, как вы вскоре заметите, нам не понадобится этот индекс ни для чего другого, кроме как для визуализации формы редактирования виджета, поскольку мы всегда сохраняем каждый экземпляр типа виджета «widget_many» в одном цикле.

Далее мы извлечем текущие настройки виджета, создав их, если они не существуют:

1
2
3
$options = get_option(‘widget_many’);
if ( !is_array($options) )
    $options = array();

При реализации собственного виджета не забудьте изменить ключ widget_many на widget_many своего виджета. Кроме того, этот код может быть повторно использован как есть. В строках 2 и 3 код учитывает случай, когда мы добавляем первый экземпляр виджета этого типа, создавая пустой массив параметров для использования.

Если настройки уже были сохранены хотя бы для одного экземпляра, мы получаем их из get_option и можем обновить настройки, если форма была отправлена:

1
if ( !$updated && !empty($_POST[‘sidebar’]) ) {

Приведенная выше строка имеет две функции: она проверяет, были ли опубликованы данные (» !empty($_POST['sidebar']) «), и проверяет, что экземпляры этого типа виджета обрабатываются только один раз.

Система виджетов вызывает функцию обработки виджетов один раз для каждого экземпляра виджета, но поскольку нам нужно позаботиться о таких вещах, как добавление и удаление новых экземпляров виджета, мы не можем этого сделать, не зная всех существующих виджетов этого типа. Вот почему подход заключается в том, чтобы обновлять каждый экземпляр виджета этого типа при первом появлении, а затем устанавливать для глобальной переменной $updated значение true, чтобы следующий экземпляр не выполнял обновление снова.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// Tells us what sidebar to put the data in
$sidebar = (string) $_POST[‘sidebar’];
 
$sidebars_widgets = wp_get_sidebars_widgets();
if ( isset($sidebars_widgets[$sidebar]) )
    $this_sidebar =& $sidebars_widgets[$sidebar];
else
    $this_sidebar = array();
 
foreach ( $this_sidebar as $_widget_id ) {
    $widget = $wp_registered_widgets[$_widget_id];
    if ( ‘widget_many’ == $widget[‘callback’]
        && isset($widget[‘params’][0][‘number’]) ) {
            $widget_number = $widget[‘params’][0][‘number’];
 
            if ( !in_array( «many-$widget_number», $_POST[‘widget-id’] ) )
                unset($options[$widget_number]);
    }
}
 
// Compile data from $widget_many_instance
$widget_data = (array) $_POST[‘widget-many’];
foreach ( $widget_data as $widget_number => $widget_many_instance ) {
    if ( !isset($widget_many_instance[‘something’])
        && isset($options[$widget_number]) ) {
            // user clicked cancel
            continue;
    }
    $something = wp_specialchars( $widget_many_instance[‘something’] );
    $options[$widget_number] = array( ‘something’ => $something );
}
 
update_option(‘widget_many’, $options);
 
$updated = true;

Этот фрагмент кода сначала просматривает все виджеты, хранящиеся на боковой панели, которая обновляется (строки 2-8), а затем просматривает список, удаляя все, которые соответствуют идентификатору виджета этого типа виджета (строки 10-18).

$_POST("sidebar") (строка 2) содержит идентификатор боковой панели. Таким образом, если пользователь удалил виджет, он также будет удален из данных. И удаляя все, мы убеждаемся, что случайно не оставляем дубликаты, например, если идентификатор некоторого виджета изменился между обновлениями.

После удаления всего мы начинаем добавлять виджеты, которые были размещены пользователем. В опубликованной форме есть массив, содержащий все данные для каждого экземпляра типа виджета. Массив организован так:

1
2
3
4
[‘widget-many’] =>
       [0] => params for widget instance 0 (array),
       [1] => params for widget instance 1 (array),
       …

В строках 23-31 код перебирает передаваемые данные экземпляра виджета и создает настройки для каждого экземпляра (строки 29 и 30). Поскольку это просто пример кода, часть сохранения использует данные-заполнители, такие как «что-то». Итак, давайте снова посмотрим на текстовый виджет, чтобы увидеть, как это работает с реальными данными:

1
2
3
4
5
6
$title = strip_tags(stripslashes($widget_text[‘title’]));
if ( current_user_can(‘unfiltered_html’) )
    $text = stripslashes( $widget_text[‘text’] );
else
    $text = stripslashes(wp_filter_post_kses( $widget_text[‘text’] ));
$options[$widget_number] = compact( ‘title’, ‘text’ );

Текстовый виджет имеет два параметра: title и text . $widget_text в этом фрагменте кода используется так же, как и $widget_many_instance в приведенном ниже примере кода: она содержит данные, опубликованные для конкретного экземпляра виджета.

В примере с текстовыми виджетами вы также увидите некоторые интересные функции безопасности, которые вы, возможно, захотите изучить при разработке своих собственных виджетов. Однако для этого урока достаточно увидеть, что содержимое переменных $title и $text читается из размещенного массива, а затем сохраняется как массив для параметров виджета в строке 6.

Если вас интересует функция compact() , она противоположна extract() и берет локальные переменные, имена которых были переданы в качестве параметров, и превращает их в массив с именами в качестве ключей.

Наконец, новый массив $options сохраняется как данные для виджета, widget_many в примере кода или widget_text в текстовом виджете, используя update_option() .

Последнее, что осталось в функции настройки виджета, — это нарисовать форму для этого экземпляра виджета. Эта форма не отображается на экране как есть, но WordPress позже преобразует ее в реальную форму, используя некоторую магию JavaScript. Приведенный ниже код взят из кода шаблона, поэтому для представления данных виджета используется $something . В текстовом виджете он заменяется на $title и $text , а в вашем собственном виджете — все, что вам нужно сохранить.

Важно отметить, что хотя сохранение выполняется для каждого экземпляра за один раз, форма, представленная здесь, предназначена только для одного экземпляра. Здесь мы будем использовать идентификатор экземпляра виджета, прочитанный в начале функции.

1
2
3
4
5
6
if ( -1 == $number ) {
       $something = »;
       $number = ‘%i%’;
   } else {
       $something = attribute_escape($options[$number][‘something’]);
   }

В приведенных выше строках виджет сначала проверяет, был ли задан правильный номер виджета или нет. Если нет, а для $number было установлено значение по умолчанию -1, для всех данных должны быть установлены значения по умолчанию. В противном случае мы получим данные из массива $options и используем его для заполнения формы текущими настройками. Это очень удобно, так что пользователю не нужно всегда начинать редактирование виджета с чистого листа.

И затем сама форма создается так, что ее можно прочитать в массив при публикации в коде обработки виджета: входные данные формы именуются в соответствии с шаблоном widget-many[$number][something] где $number — это число этот экземпляр виджета и something является именем параметра, который будет сохранен. WordPress затем анализирует это в структуре массива, описанной ранее, когда пользователь отправляет форму.

Форма не имеет кнопки отправки, потому что все виджеты сохраняются с помощью одной и той же кнопки отправки, предоставляемой WordPress.

1
2
3
4
5
6
7
8
9
<p>
    <input class=»widefat»
        id=»widget-many-something-<?php echo $number; ?>»
        name=»widget-many[<?php echo $number; ?>][something]»
        type=»text» value=»<?php echo $data; ?>» />
    <input type=»hidden»
        id=»widget-many-submit-<?php echo $number; ?>»
        name=»widget-many[<?php echo $number; ?>][submit]» value=»1″ />
</p>

Чтобы WordPress отображал виджет в списке виджетов, вам нужно зарегистрировать его, сообщив WordPress, какие функции использовать для рендеринга виджета и формы его настроек. То, как это делается для мультивиджетов, ничем не отличается от обычных виджетов. Единственное отличие состоит в том, как определяется идентификатор виджета: в $control_ops (строка 7) мы говорим WordPress включить несколько экземпляров и присоединить все экземпляры виджета с идентификатором, начинающимся с "many" к этому типу виджета.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
function widget_many_register() {
    if ( !$options = get_option(‘widget_many’) )
        $options = array();
 
    $widget_ops = array(‘classname’ => ‘widget_many’,
                ‘description’ => __(‘Widget which allows multiple instances’));
    $control_ops = array(‘width’ => 400, ‘height’ => 350, ‘id_base’ => ‘many’);
    $name = __(‘Many’);
 
    $registered = false;
    foreach ( array_keys($options) as $o ) {
        // Old widgets can have null values for some reason
        if ( !isset($options[$o][‘something’]) )
            continue;
 
        // $id should look like {$id_base}-{$o}
        $id = «many-$o»;
        $registered = true;
        wp_register_sidebar_widget( $id, $name, ‘widget_many’,
                        $widget_ops, array( ‘number’ => $o ) );
        wp_register_widget_control( $id, $name, ‘widget_many_control’,
                        $control_ops, array( ‘number’ => $o ) );
    }
 
    // If there are none, we register the widget’s
    // existance with a generic template
    if ( !$registered ) {
        wp_register_sidebar_widget( ‘many-1’, $name, ‘widget_many’,
                $widget_ops, array( ‘number’ => -1 ) );
        wp_register_widget_control( ‘many-1’, $name, ‘widget_many_control’,
                $control_ops, array( ‘number’ => -1 ) );
    }
}
 
// This is important
add_action( ‘widgets_init’, ‘widget_many_register’ );

В приведенной выше функции мы сначала регистрируем все существующие виджеты этого типа, пропуская те, у которых нет действительных данных (строки 11-23), а затем, если еще нет доступного экземпляра, регистрируем экземпляр по умолчанию, чтобы убедиться, что виджет зарегистрирован и доступен на боковой панели (строки 27-32).

В качестве последнего, решающего шага, в строке 34, мы подключаем функцию инициализации виджета к событию widgets_init чтобы WordPress вызывал его, когда пришло время инициализировать все виджеты. Без этой функции мы не увидели бы никакой функциональности виджета, который мы только что проанализировали, поскольку ни одна часть кода никогда не была бы вызвана.

В приведенных выше фрагментах я намеренно пропустил часть кода, чтобы сделать его более читабельным, поэтому вы не сможете просто скопировать фрагменты и соединить их вместе. Вместо этого вы должны пойти и скопировать тот же код из вашей установки WordPress.

После этого вам просто нужно добавить свой собственный функционал, и вот он у вас: собственный мульти-виджет.