Добавление настроек к пользовательскому типу записей

5 авг. 2016 г., 12:10:18
Просмотры: 15.3K
Голосов: 3

У меня есть пользовательский тип записей Portfolio. Он связан с тремя пользовательскими таксономиями. Всё работает нормально.

Однако для архивной страницы мне нужно добавить несколько пользовательских настроек. Из-за ограничений проекта я не могу создать плагин — все изменения должны быть сделаны в теме.

Мне удалось добавить страницу настроек в подменю (это было несложно), следуя этому руководству, и настройки отображаются на странице без проблем. Проблема в том, что они не сохраняются.

Пока я добавил только одну настройку (header_text), пока не решу проблему с сохранением.

Думаю, что проблема, вероятно, в $option_group.

Если сделать var_dump($_POST), я получаю:

array (size=6)
  'option_page' => string 'edit.php?post_type=rushhour_projects&page=projects_archive' (length=58)
  'action' => string 'update' (length=6)
  '_wpnonce' => string '23c70a3029' (length=10)
  '_wp_http_referer' => string '/wp-admin/edit.php?post_type=rushhour_projects&page=projects_archive' (length=68)
  'rushhour_projects_archive' => 
    array (size=1)
      'header_text' => string 'asdf' (length=4)
  'submit' => string 'Save Changes' (length=12)

Вот регистрация пользовательского типа записей:

if ( ! function_exists('rushhour_post_type_projects') ) :

// Добавляем Portfolio Projects в WordPress
add_action( 'init', 'rushhour_post_type_projects', 0 );

// Регистрируем пользовательский тип записей Portfolio Projects
function rushhour_post_type_projects()
{
    $labels = array(
        'name'                  => _x( 'Портфолио', 'Общее название типа записи', 'rushhour' ),
        'singular_name'         => _x( 'Проект', 'Название одного элемента этого типа', 'rushhour' ),
        'menu_name'             => __( 'Проекты портфолио', 'rushhour' ),
        'name_admin_bar'        => __( 'Проект портфолио', 'rushhour' ),
        'archives'              => __( 'Архивы портфолио', 'rushhour' ),
        'parent_item_colon'     => __( 'Родительский проект:', 'rushhour' ),
        'all_items'             => __( 'Все проекты', 'rushhour' ),
        'add_new_item'          => __( 'Добавить новый проект', 'rushhour' ),
        'add_new'               => __( 'Добавить новый', 'rushhour' ),
        'new_item'              => __( 'Новый проект', 'rushhour' ),
        'edit_item'             => __( 'Редактировать проект', 'rushhour' ),
        'update_item'           => __( 'Обновить проект', 'rushhour' ),
        'view_item'             => __( 'Просмотреть проект', 'rushhour' ),
        'search_items'          => __( 'Искать проекты', 'rushhour' ),
        'not_found'             => __( 'Не найдено', 'rushhour' ),
        'not_found_in_trash'    => __( 'Не найдено в корзине', 'rushhour' ),
        'featured_image'        => __( 'Изображение записи', 'rushhour' ),
        'set_featured_image'    => __( 'Установить изображение записи', 'rushhour' ),
        'remove_featured_image' => __( 'Удалить изображение записи', 'rushhour' ),
        'use_featured_image'    => __( 'Использовать как изображение записи', 'rushhour' ),
        'insert_into_item'      => __( 'Вставить в проект', 'rushhour' ),
        'uploaded_to_this_item' => __( 'Загружено для этого проекта', 'rushhour' ),
        'items_list'            => __( 'Список проектов', 'rushhour' ),
        'items_list_navigation' => __( 'Навигация по списку проектов', 'rushhour' ),
        'filter_items_list'     => __( 'Фильтровать список проектов', 'rushhour' ),
        );
    $rewrite = array(
        'slug'                  => 'portfolio',
        'with_front'            => true,
        'pages'                 => true,
        'feeds'                 => true,
        );
    $args = array(
        'label'                 => __( 'Проект', 'rushhour' ),
        'description'           => __( 'Проекты портфолио для Global VDC.', 'rushhour' ),
        'labels'                => $labels,
        'supports'              => array( 'title', 'editor', 'excerpt', 'thumbnail', 'revisions', ),
        'taxonomies'            => array( 'rushhour_clients', 'rushhour_locations', 'rushhour_project_type' ),
        'hierarchical'          => false,
        'public'                => true,
        'show_ui'               => true,
        'show_in_menu'          => true,
        'menu_position'         => 5,
        'menu_icon'             => 'dashicons-portfolio',
        'show_in_admin_bar'     => true,
        'show_in_nav_menus'     => true,
        'can_export'            => true,
        'has_archive'           => 'portfolio',
        'exclude_from_search'   => false,
        'publicly_queryable'    => true,
        'rewrite'               => $rewrite,
        'capability_type'       => 'page',
        );
    register_post_type( 'rushhour_projects', $args );
}
endif;

Затем у меня есть функция для настройки страницы подменю в админке:

if ( ! function_exists('rushhour_projects_admin_page') ) :

add_action( 'admin_menu' , 'rushhour_projects_admin_page' );

/**
 * Создаем страницу подменю для настроек
 *
 * @uses rushhour_projects_options_display()
 */
function rushhour_projects_admin_page()
{
    add_submenu_page(
        'edit.php?post_type=rushhour_projects',
        __('Настройки проектов портфолио', 'rushhour'),
        __('Настройки портфолио', 'rushhour'),
        'manage_options',
        'projects_archive',
        'rushhour_projects_options_display');
}
endif;

Эти две части работают без проблем.

Проблема, как я думаю, где-то в функциях ниже для регистрации и сохранения настроек:

if ( ! function_exists('rushhour_projects_options_display') ) :
/**
 * Отображаем форму на странице настроек Rush Hour Projects.
 *
 * Используется в 'rushhour_projects_admin_page'.
 */
function rushhour_projects_options_display()
{
    // Создаем заголовок в стандартном контейнере WordPress 'wrap'
    echo '<div class="wrap">';

    settings_errors();

    echo '<form method="post" action="">';

    var_dump( get_option('rushhour_projects_archive') );

    settings_fields( 'edit.php?post_type=rushhour_projects&page=projects_archive' );

    do_settings_sections( 'edit.php?post_type=rushhour_projects&page=projects_archive' );

    submit_button();

    echo '</form></div><!-- .wrap -->';
}
endif;

add_action( 'admin_init', 'rushhour_projects_settings' );

/**
 * Регистрируем настройки и добавляем секции и поля настроек на страницу админки.
 */
function rushhour_projects_settings()
{
    if ( false == get_option( 'rushhour_projects_archive' ) )
        add_option( 'rushhour_projects_archive' );

    add_settings_section(
        'projects_archive_header', // ID секции
        __('Настройки архивной страницы проектов портфолио', 'rushhour'),
        'rushhour_project_settings_section_title', // Функция обратного вызова
        'edit.php?post_type=rushhour_projects&page=projects_archive' // Ярлык страницы настроек
        );

    add_settings_field(
        'header_text',          // ID поля
        __('Текст заголовка', 'rushhour'),          // Название настройки
        'projects_archive_header_text_callback',
        'edit.php?post_type=rushhour_projects&page=projects_archive',   // Ярлык страницы настроек
        'projects_archive_header',          // ID секции
        array('Текст для отображения в заголовке архива.')
        );

    register_setting(
        'edit.php?post_type=rushhour_projects&page=projects_archive', // Группа опций
        'rushhour_projects_archive',  // Имя опции
        'rushhour_projects_archive_save_options'
        );
}

/**
 * Функция обратного вызова для секции настроек.
 *
 * Закомментировано, пока настройки не работают.
 * 
 * @param  array $args Получает $id, $title и $callback.
 */
function rushhour_project_settings_section_title( $args ) {
    // printf( '<h2>%s</h2>', apply_filters( 'the_title', $args['title'] ) );
}

/**
 * Функции обратного вызова для полей настроек.
 */
function projects_archive_header_text_callback($args)
{
    $options = get_option('rushhour_projects_archive');

    printf( '<input class="widefat" id="header_text" name="rushhour_projects_archive[header_text]" type="textarea" value="%1$s" />',
        $options );
}

/**
 * Сохраняем настройки.
 */
function rushhour_projects_archive_save_options()
{
    if ( isset( $_POST['rushhour_projects_archive[header_text]'] ) )
    {
        update_option( 'rushhour_projects_archive', $_POST['rushhour_projects_archive[header_text]'] );
    }
}
3
Комментарии

Просто хочу прокомментировать "Я не умею писать плагины" — это не проблема. Обычно всё, что вы могли бы поместить в плагин, можно добавить в файл functions.php темы, если это лучше подходит для вашей ситуации.

Andy Macaulay-Brook Andy Macaulay-Brook
5 авг. 2016 г. 12:12:50

Привет, Энди. Спасибо за ответ. Ты прав, это не проблема, но я подумал, что это стоит отметить как параметр для отвечающих.

dotZak dotZak
5 авг. 2016 г. 12:14:25

Да. Очень полный и детальный вопрос. Сам не могу ответить в данный момент, но надеюсь, что мой голос привлечёт к нему внимание кого-нибудь другого.

Andy Macaulay-Brook Andy Macaulay-Brook
5 авг. 2016 г. 12:22:23
Все ответы на вопрос 2
0

Хорошо, меня раздражало, что это не работает, и я решил просто переписать код. Я не уверен, в чём именно было решение, но подозреваю две вещи: неправильный endpoint для формы (должен быть options.php) и неверные значения $option_group и $option_name (скорее всего, они не совпадали).

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

  1. Теперь это отдельный файл, поэтому регистрация пользовательского типа записи здесь не видна.
  2. Я использовал объект для страницы, как показано в примере 2 из кодекса WordPress.
  3. Добавил вторую опцию для загрузчика медиафайлов, используя этот туториал, который я обновил, применив wp_localize_script() для инъекции JSON-объекта с данными из PHP.
    <?php
    class RushHourProjectArchivesAdminPage
    {
        /**
         * Содержит значения, используемые в колбэках полей
         */
        private $options;

        public function __construct()
        {
            add_action( 'admin_menu', array( $this, 'add_submenu_page_to_post_type' ) );
            add_action( 'admin_init', array( $this, 'sub_menu_page_init' ) );
            add_action( 'admin_init', array( $this, 'media_selector_scripts' ) );
        }

        /**
         * Добавляет страницу подменю к пользовательскому типу записи
         */
        public function add_submenu_page_to_post_type()
        {
            add_submenu_page(
                'edit.php?post_type=rushhour_projects',
                __('Настройки портфолио проектов', 'rushhour'),
                __('Настройки портфолио', 'rushhour'),
                'manage_options',
                'projects_archive',
                array($this, 'rushhour_projects_options_display'));
        }

        /**
         * Колбэк страницы настроек
         */
        public function rushhour_projects_options_display()
        {
            $this->options = get_option( 'rushhour_projects_archive' );

            wp_enqueue_media();

            echo '<div class="wrap">';

            printf( '<h1>%s</h1>', __('Настройки портфолио', 'rushhour' ) ); 

            echo '<form method="post" action="options.php">';

            settings_fields( 'projects_archive' );

            do_settings_sections( 'projects-archive-page' );

            submit_button();

            echo '</form></div>';
        }

        /**
         * Регистрирует и добавляет настройки
         */
        public function sub_menu_page_init()
        {
            register_setting(
                'projects_archive', // Группа опций
                'rushhour_projects_archive', // Имя опции
                array( $this, 'sanitize' ) // Функция очистки
                );

            add_settings_section(
                'header_settings_section', // ID
                __('Настройки заголовка', 'rushhour'), // Заголовок
                array( $this, 'print_section_info' ), // Колбэк
                'projects-archive-page' // Страница
                );

            add_settings_field(
                'archive_description', // ID
                __('Описание архива', 'rushhour'), // Заголовок
                array( $this, 'archive_description_callback' ), // Колбэк
                'projects-archive-page', // Страница
                'header_settings_section' // Секция
                );

            add_settings_field(
                'image_attachment_id',
                __('Фоновое изображение заголовка', 'rushhour'),
                array( $this, 'header_bg_image_callback' ),
                'projects-archive-page',
                'header_settings_section'
                );
        }

        /**
         * Очищает каждое поле настроек по мере необходимости
         *
         * @param array $input Содержит все поля настроек в виде ключей массива
         */
        public function sanitize( $input )
        {
            $new_input = array();

            if( isset( $input['archive_description'] )
                $new_input['archive_description'] = sanitize_text_field( $input['archive_description'] );

            if( isset( $input['image_attachment_id'] ) )
                $new_input['image_attachment_id'] = absint( $input['image_attachment_id'] );

            return $new_input;
        }

        /**
         * Выводит текст секции
         */
        public function print_section_info()
        {
            print 'Выберите настройки для заголовка страницы архива.';
        }

        /**
         * Получает массив опций настроек и выводит одно из его значений
         */
        public function archive_description_callback()
        {
            printf(
                '<input type="text" id="archive_description" name="rushhour_projects_archive[archive_description]" value="%s" />',
                isset( $this->options['archive_description'] ) ? esc_attr( $this->options['archive_description']) : ''
                );
        }

        /**
         * Получает массив опций настроек и выводит одно из его значений
         */
        public function header_bg_image_callback()
        {
            $attachment_id = $this->options['image_attachment_id'];

            // Превью изображения
            printf('<div class="image-preview-wrapper"><img id="image-preview" src="%s" ></div>', wp_get_attachment_url( $attachment_id ) );

            // Кнопка загрузки изображения
            printf( '<input id="upload_image_button" type="button" class="button" value="%s" />',
                __( 'Загрузить изображение', 'rushhour' ) );

            // Скрытое поле с ID вложения изображения
            printf( '<input type="hidden" name="rushhour_projects_archive[image_attachment_id]" id="image_attachment_id" value="%s">',
                $attachment_id );
        }

        public function media_selector_scripts()
        {
            $my_saved_attachment_post_id = get_option( 'media_selector_attachment_id', 0 );

            wp_register_script( 'sub_menu_media_selector_scripts', get_template_directory_uri() . '/admin/js/media-selector.js', array('jquery'), false, true );

            $selector_data = array(
                'attachment_id' => get_option( 'media_selector_attachment_id', 0 )
                );

            wp_localize_script( 'sub_menu_media_selector_scripts', 'selector_data', $selector_data );

            wp_enqueue_script( 'sub_menu_media_selector_scripts' );
        }
    }

Затем просто вызываем объект, если is_admin() возвращает true:

    if ( is_admin() )
         $my_settings_page = new RushHourProjectArchivesAdminPage();
8 авг. 2016 г. 12:04:47
2

Это должно сработать. Измените

function rushhour_projects_archive_save_options()
{
    if ( isset( $_POST['rushhour_projects_archive[header_text]'] ) )
    {
        update_option( 'rushhour_projects_archive', $_POST['rushhour_projects_archive[header_text]'] );
    }
}

на

function rushhour_projects_archive_save_options()
{
    if ( isset( $_POST['rushhour_projects_archive']['header_text'] ) )
    {
        update_option( 'rushhour_projects_archive', $_POST['rushhour_projects_archive']['header_text'] );
    }
}
5 авг. 2016 г. 13:23:10
Комментарии

О, конечно! Попробую и дам знать, как получилось. Спасибо за это.

dotZak dotZak
6 авг. 2016 г. 03:15:22

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

dotZak dotZak
8 авг. 2016 г. 04:18:02