Как сделать черновик записи доступным для всех пользователей?
У меня есть несколько неопубликованных записей на моем сайте WordPress, и я пытаюсь сделать их доступными для обычных пользователей (не авторизованных) через стандартные ЧПУ-ссылки (site.com/post-here
). Я понимаю, что это не лучшая практика, но для моих особых целей это необходимо.
Я пробовал добавить следующий код в файл functions.php
:
// Включаем возможность просмотра черновиков
function enable_view_drafts() {
$role = get_role( 'subscriber' );
$role->add_cap( 'read_private_posts' );
$role->add_cap( 'edit_posts' );
}
add_action( 'after_setup_theme', 'enable_view_drafts');
Также пробовал использовать хук init
вместо after_setup_theme
. Безрезультатно.
Насколько я понимаю, изменения ролей сохраняются в базе данных и должны выполняться только один раз. Поэтому я использую хук after_setup_theme
для вызова функции.
Но когда я пытаюсь открыть страницу как обычный пользователь, вместо содержимого записи отображается ошибка 404. Я также пробовал использовать URL превью (site.com/?p=212&preview=true
), но это тоже не сработало.
Мои предположения:
- обычный пользователь не имеет достаточных прав (
caps
) для чтения черновиков - просмотр черновиков на фронтэнде невозможен для любых пользователей (включая администраторов)
Какие изменения мне нужно внести, чтобы достичь желаемого? Если это невозможно, какие альтернативные решения вы можете предложить?
Примечание: Меня интересуют решения без использования плагинов.

Нельзя назначать права неизвестным пользователям. Если вы хотите сделать запись видимой для всех, создайте отдельный URL для таких записей и добавьте элемент управления в редактор записи, чтобы включить предпросмотр только для выбранных записей.
При обращении к такому URL проверяйте, разрешен ли предпросмотр для записи и не была ли запись уже опубликована. Также убедитесь, что поисковые системы игнорируют этот URL.
Для URL я бы использовал эндпоинт:
add_rewrite_endpoint( 'post-preview', EP_ROOT );
Теперь можно создавать URL вида …
http://example.com/post-preview/123
… где 123
— это ID записи.
Затем используйте обработчик обратного вызова для проверки ID записи, его валидности и переопределения основного запроса. Это, вероятно, единственный допустимый случай использования query_posts()
. :)
Допустим, эндпоинт — это класс T5_Endpoint
(модель), а обработчик вывода — класс T5_Render_Endpoint
(представление), которому ранее передавалась модель. Тогда, вероятно, есть метод render()
, вызываемый на template_redirect
:
public function render()
{
$post_id = $this->endpoint->get_value();
if ( ! $post_id )
return;
if ( 1 !== $this->meta->get_value( $post_id )
or 'publish' === get_post_status( $post_id )
)
{
wp_redirect( get_permalink( $post_id ) );
exit;
}
$query = array (
'suppress_filters' => TRUE,
'p' => $post_id,
'post_type' => 'any'
);
query_posts( $query );
add_action( 'wp_head', 'wp_no_robots' );
}
$this->meta
— это еще одна модель (класс T5_Post_Meta
) для мета-значения записи, которое контролирует разрешение предпросмотра. Элемент управления размещается в блоке Publish (действие post_submitbox_misc_actions
), отображаемом другим представлением, которое получает тот же мета-класс.
Таким образом, T5_Post_Meta
знает, где и когда сохранять мета-значение, а представления делают с ним что-то.
Также подключите хук transition_post_status
, чтобы удалить мета-поле записи при публикации. Мы же не хотим тратить ресурсы впустую, верно?
Это лишь общий план. Есть множество деталей, которые нужно учесть… Я написал небольшой плагин, демонстрирующий реализацию: T5 Public Preview.

Я решил эту проблему, как мне кажется, более простым способом, чем ответ @toscho выше.
Мой случай использования: я использую одну и ту же базу данных для внутренней интранет-стадии и публичного сайта. Рабочий процесс заключается в том, что авторы пишут черновики и делятся ими с другими пользователями, которые просматривают эти черновики на интранет-сайте, перед публикацией. Я специально не хотел требовать от рецензентов входа в систему для просмотра черновиков, поэтому я просто завишу от константы ENV_PRODUCTION
, которая устанавливается в файле wp-config на основе имени хоста в $_SERVER['SERVER_NAME']
. Именно для этого здесь нужны проверки на ENV_PRODUCTION
— просто отключаем все эти фильтры, если просматривается рабочий сайт.
Это немного странно, потому что приходится подключаться после того, как WP_Query удаляет все записи из массива $wp_query->posts, но мне это кажется стабильным и безопасным.
/*
* На стадии домашней страницы и архивов черновики должны быть видны.
*/
function show_drafts_in_staging_archives( $query ) {
if ( ENV_PRODUCTION )
return;
if ( is_admin() || is_feed() )
return;
$query->set( 'post_status', array( 'publish', 'draft' ) );
}
add_action( 'pre_get_posts', 'show_drafts_in_staging_archives' );
/*
* Сделать черновики видимыми на стадии при одиночном просмотре.
*
* (Потому что при одиночном просмотре WP_Query проверяет,
* может ли текущий пользователь редактировать запись, прежде чем показать черновик.)
*/
function show_single_drafts_on_staging( $posts, $wp_query ) {
if ( ENV_PRODUCTION )
return $posts;
// Убеждаемся, что это предпросмотр, чтобы не показывать опубликованные приватные записи
if ( ! is_preview() )
return $posts;
if ( count( $posts ) )
return $posts;
if ( !empty( $wp_query->query['p'] ) ) {
return array ( get_post( $wp_query->query['p'] ) );
}
}
add_filter( 'the_posts', 'show_single_drafts_on_staging', 10, 2 );
Здесь два отдельных фильтра:
- Фильтр на хуке "pre_get_posts" устанавливает post_status по умолчанию в 'publish,draft' для стадии. Это позволит показывать черновики в архивах.
- Отдельный фильтр нужен для одиночных просмотров, потому что в классе WP_Query есть сложная логика, удаляющая черновики из результатов запроса, если текущий пользователь не может их редактировать. Я обошел это, фильтруя 'the_posts' и добавляя нужную запись обратно в результаты.

Это невероятно, огромное спасибо за то, что поделились этим. Абсолютно идеально и именно то, что мне было нужно.

@Joelio Можете конкретнее описать проблему, которую решаете? В простом варианте я просто добавил этот код в свой functions.php и простое определение в wp-config.php, которое устанавливает константу ENV_PRODUCTION в true или false в зависимости от домена запроса.

@goldenapples Я добавил этот кусок кода в свой function.php, что мне нужно добавить в wp-config? Спасибо за помощь

@MatthiasGrahamSlick - Тебе просто нужно что-то, что будет устанавливать константу ENV_PRODUCTION
, если ты на продакшене. Я использовал domain.com для прода и staging.domain.com для стейджинга, так что моя строка была define( 'ENV_PRODUCTION', false === stripos( $_SERVER['HTTP_HOST'], 'staging' ) );
Помогает?

@goldenapples где в твоей функции show_single_drafts_on_staging
ты контролируешь показ только постов с post_status=draft
? Насколько я протестировал твой код, он будет отображать любые посты (даже удаленные) на страницах записей. Или я что-то делаю не так?

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

Я также озадачен причиной. Если вы хотите, чтобы все видели запись, почему бы просто не опубликовать её? Вы могли бы использовать произвольные поля для записи, чтобы отслеживать любое особое состояние, которое вам нужно определить.

Я считаю, что комментарий G.M. здесь лучший. Предполагаю, вы пытаетесь сделать следующее:
- Написать пост
- Сохранить как черновик
- Разрешить внешнему (не авторизованному) пользователю просмотреть черновик для согласования
- Опубликовать
Так ли это?
К сожалению, я не могу придумать простого способа сделать это. Вы можете опубликовать его как приватный пост, тогда для просмотра потребуется ввести пароль, но для этого нужно быть авторизованным. Вы также можете защитить его паролем, но тогда он всё равно будет отображаться в вашей ленте и списке последних постов и т.д. Не могли бы вы создать гостевую учётную запись и передать им логин/пароль, когда даёте им URL?
Подробнее читайте здесь: http://codex.wordpress.org/Content_Visibility
Альтернативно, есть плагин, который может подойти для ваших нужд: http://wordpress.org/extend/plugins/shareadraft/ Я бегло просмотрел код, и кажется, разработчик изменяет значение, возвращаемое get_post_status, так что вы можете поэкспериментировать с этим:
http://codex.wordpress.org/Function_Reference/get_post_status
Надеюсь, это поможет.

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