В WooCommerce часто появляется одна и та же задача: покупатель может прикрепить файл к заказу, но это нужно не всем и не всегда. Например, менеджер должен загружать макеты только после оплаты, а клиент — только в статусе processing или completed. Если не ограничить сценарий, в админке быстро появляются лишние файлы, а в заказах — путаница с версиями документов.
Ниже — рабочий вариант без выдуманных хуков и без привязки к сторонним плагинам. Подход простой: сначала проверяем роль пользователя и статус заказа, затем разрешаем или блокируем форму загрузки, а сам файл сохраняем только после валидации.
Когда проблема становится заметной
Обычно это всплывает в одном из трёх сценариев:
- клиент загружает файлы до оплаты, хотя по процессу это запрещено;
- менеджер прикрепляет документы не к тому заказу;
- все роли видят одинаковую форму загрузки, хотя права должны отличаться.
Если у вас уже есть кастомная форма или поле загрузки в карточке заказа, проверьте, не открыта ли она для всех авторизованных пользователей без дополнительной проверки. В WooCommerce сам факт входа в аккаунт ещё не означает, что пользователь должен иметь право на загрузку.
Диагностика: что проверить до правки кода
Перед внедрением посмотрите на три вещи. Это экономит время и помогает не искать ошибку в коде, если проблема в логике магазина.
- Где именно отображается загрузка: в личном кабинете, на странице заказа, в админке или в checkout.
- Какие роли должны иметь доступ:
customer,shop_manager,administratorили кастомные роли. - В каких статусах заказа загрузка допустима:
pending,on-hold,processing,completed.
Если форма уже есть, но файл всё равно проходит, значит проверка сделана только на уровне интерфейса. Это слабое место: скрыть кнопку недостаточно, нужно проверять права и на сервере.
Подходы: плагин, код или гибрид
Для такой задачи есть три реалистичных варианта. Выбор зависит от того, насколько часто меняются правила доступа.
| Подход | Когда подходит | Минус |
|---|---|---|
| Код в мини-плагине | Нужны точные правила по ролям и статусам | Требует поддержки |
| Плагин с правами доступа | Нужно быстро закрыть задачу без разработки | Не всегда учитывает статусы заказа |
| Гибрид | Интерфейс делает плагин, а серверную проверку — код | Чуть сложнее настройка |
Если задача завязана на безопасность и документы, я бы не ограничивался только настройкой интерфейса. Серверная проверка обязательна.
Пошаговое решение через проверку роли и статуса заказа
Ниже пример мини-плагина. Он показывает, как:
- разрешить загрузку только пользователям с нужной ролью;
- проверить статус заказа;
- не принимать файл, если условия не выполнены;
- сохранить файл в медиабиблиотеку и привязать к заказу.
<?php
/**
* Plugin Name: WC Order File Upload Restriction
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
function wpupload_user_has_allowed_role( $user_id ) {
$user = get_userdata( $user_id );
if ( ! $user || empty( $user->roles ) ) {
return false;
}
$allowed_roles = array( 'customer', 'shop_manager' );
return (bool) array_intersect( $allowed_roles, $user->roles );
}
function wpupload_order_status_allows_upload( $order_id ) {
$order = wc_get_order( $order_id );
if ( ! $order ) {
return false;
}
$allowed_statuses = array( 'processing', 'completed' );
return in_array( $order->get_status(), $allowed_statuses, true );
}
function wpupload_handle_order_file_upload() {
if ( empty( $_POST['order_id'] ) || empty( $_FILES['order_file'] ) ) {
wp_die( 'Неверные данные формы.' );
}
$order_id = absint( $_POST['order_id'] );
$user_id = get_current_user_id();
if ( ! $user_id || ! wpupload_user_has_allowed_role( $user_id ) ) {
wp_die( 'У вас нет прав на загрузку файла.' );
}
if ( ! wpupload_order_status_allows_upload( $order_id ) ) {
wp_die( 'Загрузка для этого статуса заказа недоступна.' );
}
require_once ABSPATH . 'wp-admin/includes/file.php';
require_once ABSPATH . 'wp-admin/includes/media.php';
require_once ABSPATH . 'wp-admin/includes/image.php';
$attachment_id = media_handle_upload( 'order_file', $order_id );
if ( is_wp_error( $attachment_id ) ) {
wp_die( $attachment_id->get_error_message() );
}
update_post_meta( $order_id, '_order_uploaded_file_id', $attachment_id );
wp_safe_redirect( add_query_arg( 'upload', 'success', wp_get_referer() ) );
exit;
}
add_action( 'admin_post_wpupload_order_file_upload', 'wpupload_handle_order_file_upload' );
add_action( 'admin_post_nopriv_wpupload_order_file_upload', 'wpupload_handle_order_file_upload' );В этом примере форма отправляется через admin-post.php. Это удобно, если вы хотите стандартную обработку WordPress без AJAX. Для AJAX логика проверки останется той же, изменится только транспорт.
Как вывести форму только тем, кому она нужна
Скрывать форму на фронтенде можно, но это не замена проверке на сервере. Тем не менее для удобства пользователей лучше не показывать лишние элементы тем, кто всё равно не сможет загрузить файл.
<?php
$user_id = get_current_user_id();
$order = wc_get_order( $order_id );
if ( $user_id && wpupload_user_has_allowed_role( $user_id ) && $order && in_array( $order->get_status(), array( 'processing', 'completed' ), true ) ) :
?>
<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" enctype="multipart/form-data">
<input type="hidden" name="action" value="wpupload_order_file_upload">
<input type="hidden" name="order_id" value="<?php echo esc_attr( $order_id ); ?>">
<input type="file" name="order_file" accept=".pdf,.jpg,.png" required>
<button type="submit">Загрузить файл</button>
</form>
<?php
endif;Проверка результата после внедрения
После установки кода не ограничивайтесь ручной проверкой одной учётной записи. Проверьте минимум четыре сценария:
- пользователь с ролью
customerи заказом в статусеprocessing— загрузка проходит; - тот же пользователь с заказом в статусе
pending— загрузка блокируется; - пользователь без нужной роли — форма не показывается и запрос отклоняется;
- администратор или менеджер — поведение соответствует вашей политике доступа.
Если хотите убедиться, что файл действительно привязан к заказу, проверьте мета-данные заказа через get_post_meta( $order_id, '_order_uploaded_file_id', true ) и откройте вложение в медиабиблиотеке.
Что смотреть в логах
Если загрузка не проходит, сначала смотрите не только PHP error log, но и ответы формы. Частая ситуация: файл отклонён не из-за размера или типа, а из-за статуса заказа. Это легко пропустить, если на фронтенде показывается общее сообщение вроде «Ошибка загрузки».
Частые ошибки и как их исправить
- Проверка есть только в шаблоне. Пользователь может отправить запрос вручную. Исправление: дублируйте проверку в обработчике формы.
- Сравнение статуса заказа сделано по неверному значению. В WooCommerce статус возвращается без префикса
wc-в методе$order->get_status(). Исправление: сравнивайте сprocessing,completedи т.д. - Не подключены файлы
media.phpиfile.php. Тогдаmedia_handle_upload()работает нестабильно или падает. Исправление: подключайте нужные файлы перед обработкой. - Не проверяется
current_user_can()или роль пользователя. Это открывает загрузку лишним аккаунтам. Исправление: ограничивайте доступ по ролям или возможностям, а не только по факту авторизации. - Файл сохраняется, но не привязывается к заказу. Исправление: сохраняйте ID вложения в мета-поле заказа и используйте его при выводе.
Безопасность и производительность: что не стоит упускать
Если через заказ загружают документы, не оставляйте проверку типа файла на уровне HTML-атрибута accept. Это только подсказка для браузера. На сервере нужно дополнительно ограничить типы через wp_check_filetype_and_ext() или через стандартную валидацию WordPress, если вы используете media_handle_upload().
Ещё один практический момент: если файлы чувствительные, не храните их в открытом каталоге без контроля доступа. Для таких сценариев лучше использовать отдельную схему выдачи файлов или закрывать доступ на уровне приложения. Если у вас уже есть система хранения в uploads, проверьте, не доступны ли документы по прямой ссылке.
Для магазинов с большим числом заказов полезно ограничить размер и список расширений заранее, чтобы не тащить на сервер лишние архивы и тяжёлые медиа. Это снижает нагрузку на обработку и уменьшает риск мусора в медиабиблиотеке.
Если правила доступа меняются часто
Когда менеджеры регулярно пересматривают, кто и на каком этапе может загружать файл, лучше вынести правила в настройки, а не хардкодить их в шаблоне. В этом случае код остаётся тем же, но массивы разрешённых ролей и статусов можно хранить в опциях сайта. Это удобнее для поддержки и меньше ломается после обновлений темы.
Если нужен быстрый слой управления видимостью блоков, можно дополнить решение плагином для скрытия лишних элементов интерфейса, но серверную проверку всё равно оставлять в коде. Иначе это будет только косметика без реальной защиты.