Добавление метабокса "Атрибуты страницы" и шаблонов страниц в редактор записей

10 окт. 2010 г., 20:40:15
Просмотры: 34K
Голосов: 14

(Примечание модератора: Первоначально заголовок был "Как добавить выбор 'Атрибуты страницы' и/или 'Атрибуты страницы > Шаблон' в редактор ЗАПИСЕЙ")

В 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 для использования выбранного шаблона.

2
Комментарии

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

MikeSchinkel MikeSchinkel
11 окт. 2010 г. 02:14:37

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

Scott B Scott B
11 окт. 2010 г. 19:36:46
Все ответы на вопрос 2
7
15

Не хочу быть вестником плохих новостей, но WordPress жестко привязывает функциональность шаблонов страниц к типу записи "page", по крайней мере в версии 3.0 (это может измениться в будущих версиях, но пока нет конкретной инициативы, о которой я знаю, чтобы это изменить. Так что это один из тех редких случаев, когда я не могу найти обходное решение без изменения ядра.)

Решение, которое я придумал, заключается в копировании соответствующего кода из ядра WordPress и его модификации под наши нужды. Вот шаги (номера строк приведены для версии 3.0.1):

  1. Скопируйте функцию page_attributes_meta_box() со строки 535 файла /wp-admin/includes/meta-boxes.php и модифицируйте её.

  2. Напишите хук add_meta_boxes для добавления метабокса, созданного в шаге 1.

  3. Скопируйте функцию get_page_templates() со строки 166 файла /wp-admin/includes/theme.php и модифицируйте её.

  4. Скопируйте функцию page_template_dropdown() со строки 2550 файла /wp-admin/includes/template.php и модифицируйте её.

  5. Добавьте шаблон записи в вашу тему.

  6. Напишите хук save_post для сохранения имени файла шаблона записи при сохранении.

  7. Напишите хук 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 вы увидите метабокс "Шаблоны записей" на странице редактирования записи:

Как выглядит метабокс Шаблоны записей в WordPress 3.0
(источник: 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'. На мой взгляд, работа с пользовательскими типами записей потребовала бы дифференциации между разными типами записей, и хотя это не слишком сложно, я не стал этого делать здесь.

11 окт. 2010 г. 02:11:33
Комментарии

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

sorich87 sorich87
11 окт. 2010 г. 10:57:00

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

MikeSchinkel MikeSchinkel
11 окт. 2010 г. 12:08:41

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

Scott B Scott B
11 окт. 2010 г. 19:33:53

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

Scott B Scott B
11 окт. 2010 г. 19:34:22

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

sorich87 sorich87
11 окт. 2010 г. 19:47:04

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

MikeSchinkel MikeSchinkel
11 окт. 2010 г. 23:12:22

Кто-нибудь знает, есть ли сейчас в 2016 году лучшее решение?

JasonDavis JasonDavis
7 июл. 2016 г. 08:59:47
Показать остальные 2 комментариев
0

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/

14 сент. 2013 г. 17:27:02