Добавление метабокса "Атрибуты страницы" и шаблонов страниц в редактор записей
(Примечание модератора: Первоначально заголовок был "Как добавить выбор 'Атрибуты страницы' и/или 'Атрибуты страницы > Шаблон' в редактор ЗАПИСЕЙ")
В WordPress в настоящее время назначение "шаблона" доступно только для страниц (т.е. post_type=='page'
). Я хочу расширить эту функциональность и для записей (т.е. post_type=='post'
).
Как я могу добавить метабокс "Атрибуты страницы" и, в частности, переключатель шаблонов в редактор записей?
Предполагаю, что этот код нужно разместить в файле functions.php
моей темы.
ОБНОВЛЕНИЕ: Мне удалось добавить выпадающий список с жестко заданными шаблонами в редактор записей, просто добавив HTML-код select в мой кастомный метабокс. Вот код, который я использую:
add_meta_box('categorydiv2', __('Настройки записи'), 'post_categories_meta_box_modified', 'post', 'side', 'high');
А вот функция, которая выводит опции и select для выбора шаблона:
//добавляет кастомный бокс с категориями
function post_categories_meta_box_modified() {
global $post;
if( get_post_meta($post->ID, '_noindex', true) ) $noindexChecked = " checked='checked'";
if( get_post_meta($post->ID, '_nofollow', true) ) $nofollowChecked = " checked='checked'";
?>
<div id="categories-all" class="ui-tabs-panel">
<ul id="categorychecklist" class="list:category categorychecklist form-no-clear">
<li id='noIndex' class="popular-category"><label class="selectit"><input value="noIndex" type="checkbox" name="chk_noIndex" id="chk_noIndex"<?php echo $noindexChecked ?> /> noindex</label></li>
<li id='noFollow' class="popular-category"><label class="selectit"><input value="noFollow" type="checkbox" name="chk_noFollow" id="chk_noFollow"<?php echo $nofollowChecked ?> /> nofollow</label></li>
</ul>
<p><strong>Шаблон</strong></p>
<label class="screen-reader-text" for="page_template">Шаблон записи</label><select name="page_template" id="page_template">
<option value='default'>Стандартный шаблон</option>
<option value='template-wide.php' >Без сайдбара</option>
<option value='template-salespage.php' >Страница продаж</option>
</select>
</div>
<?php
}
И наконец, код для сохранения выбранных значений:
function save_post_categories_meta($post_id) {
if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE ) return $post_id;
$noIndex = $_POST['chk_noIndex'];
$noFollow = $_POST['chk_noFollow'];
update_post_meta( $post_id, '_noindex', $noIndex );
update_post_meta( $post_id, '_nofollow', $noFollow );
return $post_id;
}
Теперь осталось (1) сохранить выбранный шаблон в метаданных записи и (2) модифицировать index.php и single.php для использования выбранного шаблона.

Не хочу быть вестником плохих новостей, но WordPress жестко привязывает функциональность шаблонов страниц к типу записи "page", по крайней мере в версии 3.0 (это может измениться в будущих версиях, но пока нет конкретной инициативы, о которой я знаю, чтобы это изменить. Так что это один из тех редких случаев, когда я не могу найти обходное решение без изменения ядра.)
Решение, которое я придумал, заключается в копировании соответствующего кода из ядра WordPress и его модификации под наши нужды. Вот шаги (номера строк приведены для версии 3.0.1):
Скопируйте функцию
page_attributes_meta_box()
со строки 535 файла/wp-admin/includes/meta-boxes.php
и модифицируйте её.Напишите хук
add_meta_boxes
для добавления метабокса, созданного в шаге 1.Скопируйте функцию
get_page_templates()
со строки 166 файла/wp-admin/includes/theme.php
и модифицируйте её.Скопируйте функцию
page_template_dropdown()
со строки 2550 файла/wp-admin/includes/template.php
и модифицируйте её.Добавьте шаблон записи в вашу тему.
Напишите хук
save_post
для сохранения имени файла шаблона записи при сохранении.Напишите хук
single_template
для загрузки шаблона записи для соответствующих записей.
Теперь приступим!
1. Копирование функции page_attributes_meta_box()
В качестве первого шага вам нужно скопировать функцию page_attributes_meta_box()
со строки 535 файла /wp-admin/includes/meta-boxes.php
, и я решил переименовать её в post_template_meta_box()
. Поскольку вам нужны только шаблоны страниц, я опустил код для указания родительской записи и порядка, что значительно упрощает код. Также я решил использовать постмету вместо повторного использования свойства page_template
, чтобы избежать потенциальных несовместимостей. Вот код:
function post_template_meta_box($post) {
if ( 'post' == $post->post_type && 0 != count( get_post_templates() ) ) {
$template = get_post_meta($post->ID,'_post_template',true);
?>
<label class="screen-reader-text" for="post_template"><?php _e('Шаблон записи') ?></label><select name="post_template" id="post_template">
<option value='default'><?php _e('Шаблон по умолчанию'); ?></option>
<?php post_template_dropdown($template); ?>
</select>
<?php
} ?>
<?php
}
2. Написание хука add_meta_boxes
Следующий шаг — добавление метабокса с помощью хука add_meta_boxes
:
add_action('add_meta_boxes','add_post_template_metabox');
function add_post_template_metabox() {
add_meta_box('postparentdiv', __('Шаблон записи'), 'post_template_meta_box', 'post', 'side', 'core');
}
3. Копирование функции get_page_templates()
Я предположил, что имеет смысл различать шаблоны страниц и шаблоны записей, поэтому потребовалась функция get_post_templates()
, основанная на get_page_templates()
со строки 166 файла /wp-admin/includes/theme.php
. Вместо маркера Template Name:
, который используется в шаблонах страниц, эта функция использует маркер Post Template:
, как показано ниже.
Я также исключил проверку файла functions.php
(не уверен, как get_page_templates()
вообще работала правильно без этого, но ладно!). Осталось только заменить упоминания слова page
на post
для удобства поддержки в будущем:
function get_post_templates() {
$themes = get_themes();
$theme = get_current_theme();
$templates = $themes[$theme]['Template Files'];
$post_templates = array();
if ( is_array( $templates ) ) {
$base = array( trailingslashit(get_template_directory()), trailingslashit(get_stylesheet_directory()) );
foreach ( $templates as $template ) {
$basename = str_replace($base, '', $template);
if ($basename != 'functions.php') {
// не разрешаем файлы шаблонов в подкаталогах
if ( false !== strpos($basename, '/') )
continue;
$template_data = implode( '', file( $template ));
$name = '';
if ( preg_match( '|Post Template:(.*)$|mi', $template_data, $name ) )
$name = _cleanup_header_comment($name[1]);
if ( !empty( $name ) ) {
$post_templates[trim( $name )] = $basename;
}
}
}
}
return $post_templates;
}
4. Копирование функции page_template_dropdown()
Аналогично скопируйте функцию page_template_dropdown()
со строки 2550 файла /wp-admin/includes/template.php
, чтобы создать post_template_dropdown()
, и просто измените её, чтобы она вызывала get_post_templates()
:
function post_template_dropdown( $default = '' ) {
$templates = get_post_templates();
ksort( $templates );
foreach (array_keys( $templates ) as $template )
: if ( $default == $templates[$template] )
$selected = " selected='selected'";
else
$selected = '';
echo "\n\t<option value='".$templates[$template]."' $selected>$template</option>";
endforeach;
}
5. Добавление шаблона записи
Следующий шаг — добавление шаблона записи для тестирования. Используя маркер Post Template:
, упомянутый в шаге 3, скопируйте single.php
из вашей темы в single-test.php
и добавьте следующий заголовок комментария (обязательно измените что-то в single-test.php
, чтобы можно было отличить его от single.php
):
/**
* Post Template: Мой тестовый шаблон
*/
После выполнения шагов с 1 по 5 вы увидите метабокс "Шаблоны записей" на странице редактирования записи:
(источник: mikeschinkel.com)
6. Написание хука save_post
Теперь, когда редактор готов, вам нужно сохранить имя файла шаблона в постмету при нажатии кнопки "Опубликовать". Вот код для этого:
add_action('save_post','save_post_template',10,2);
function save_post_template($post_id,$post) {
if ($post->post_type=='post' && !empty($_POST['post_template']))
update_post_meta($post->ID,'_post_template',$_POST['post_template']);
}
7. Написание хука single_template
И наконец, вам нужно заставить WordPress использовать ваши новые шаблоны записей. Это делается с помощью хука single_template
, который возвращает имя нужного шаблона для записей, у которых он назначен:
add_filter('single_template','get_post_template_for_template_loader');
function get_post_template_for_template_loader($template) {
global $wp_query;
$post = $wp_query->get_queried_object();
if ($post) {
$post_template = get_post_meta($post->ID,'_post_template',true);
if (!empty($post_template) && $post_template!='default')
$template = get_stylesheet_directory() . "/{$post_template}";
}
return $template;
}
Вот и всё!
ПРИМЕЧАНИЕ: Я не учитывал Пользовательские типы записей, только post_type=='post'
. На мой взгляд, работа с пользовательскими типами записей потребовала бы дифференциации между разными типами записей, и хотя это не слишком сложно, я не стал этого делать здесь.

Отлично! Я лег спать с почти готовым кодом в редакторе, используя тот же подход копирования стандартных функций WordPress (он был готов, но я бы не стал его публиковать, так как не тестировал). :)

@sorich87 - Знаешь старую поговорку: "Кто рано встает, тому Бог подает!" Шучу, конечно. На самом деле есть только один разумный способ заставить это работать, поэтому неудивительно, что ваш код оказался таким же!

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

@sorich87 - Спасибо, что работал над этим. Я действительно ценю твои усилия.

@MikeSchinkel: :) это было почти то же самое. @Scott B: пожалуйста!

@Scott B: Без проблем, рад, что смог помочь. Я ищу достаточно общие вопросы, которые потенциально могут помочь многим людям, и стараюсь отвечать на них не только для того, кто задал вопрос, но и для тех, кто может прийти после.

WordPress позволяет добавлять метаданные к категориям с помощью плагина:
Для этого необходимо установить одно из множества расширений, добавляющих метаданные к категориям (имитируя функционал, который страницы имеют из коробки). Плагин Simple Term Meta отлично справляется с этой задачей.
Примечание: Для расширения функционала категорий требуется WordPress версии 3.x или выше.
После установки плагина вы можете использовать следующие функции:
- add_term_meta
- update_term_meta
- get_term_meta
Используйте файл Functions.php для добавления необходимых методов. Например:
add_action('category_add_form_fields', 'category_metabox_add', 10, 1);
function category_metabox_add($tag) { ?>
<div class="form-field">
<label for="image-url"><?php _e('URL изображения') ?></label>
<input name="image-url" id="image-url" type="text" value="" size="40" aria-required="true" />
<p class="description"><?php _e('Это изображение будет использоваться как миниатюра на странице категории.'); ?></p>
</div>
<?php }
add_action('created_category', 'save_category_metadata', 10, 1);
function save_category_metadata($term_id)
{
if (isset($_POST['image-url']))
update_term_meta( $term_id, 'image-url', $_POST['image-url']);
}
Вызов новых полей в темах осуществляется просто:
<?php echo get_term_meta(get_query_var('cat'), 'image-url', true); ?>
Подробнее и больше примеров: http://www.wphub.com/adding-metadata-taxonomy-terms/
