Drupal пользуюсь с 2012 года больше как админ, меньше как программист. И вот только в 2022 (через 10 лет) столкнулся с интересной проблемой в D7 (на D9 пока не проверял).
Суть проблемы - невозможно подгрузить в форму файл в любое второе файловое поле в режиме AJAX для не авторизованного пользователя. Причина этой проблемы зарыта глубоко. Я уже сталкивался с этой причиной, но совершенно по другому поводу. Это если кратко.
Полностью описание проблемы выглядит вот так.
1. Создаем любую форму (я создавал через Entityform, но с нодой такая же фигня будет) в которой больше, чем одно поле типа file. Например, это будет два поля (а хоть и 10 - без разницы). Пусть оба поля будут множественными, то есть позволят загружать не 1 файл в поле, а несколько. Можно сделать их и для 1 значения - тоже без разницы, но с множественным полем один эффект интересный проявляется.
2. Делаем форму доступной для заполнения анонимным посетителем.

3. Сначала проверяем без AJAX. Загружаем в первое попавшееся поле (в любое из двух, но выбранного в первую очередь для загрузки файла) файл. Как обычно кликаем на кнопку "Обзор"(в FF или "Выбрать файлы" в Chrome) выбираем файл. Точно так же делаем на втором файловом поле. Нажимаем кнопку "Отправить" внизу формы. И вуаля!!! Файлы нормально отправились в оба поля и загрузились на сайт в соответствующую папку. Все прекрасно работает. Если установлен модуль Multiupload, то точно также в каждое поле выбираем по нескольку файлов одновременно и то же самое - все прекрасно работает
4. Теперь проверим, а как там AJAX? (Утром мажу бутерброд — Сразу мысль: а как народ?). Делаем точно так же как и в п.3 (выше) для любого из полей кликаем по кнопке "Обзор", выбираем файл. НЕ НАЖИМАЕМ кнопку "Отправить" (sic!), а кликаем по кнопке "Закачать". Отлично - файл закачался (это как раз AJAX отработал)! На этом же самом поле опять нажимаем "Обзор", выбираем другой файл и опять по кнопке "Закачать". И опять отлично все закачалось в ЭТО же поле! А вот со вторым полем такой финт ушами уже проделать не получится!!! При попытке закачать во второе поле файл таким же образом - "Обзор"+"Закачать" поле исчезнет и вместо него выскочит огненная надпись не имеющая никакого отношения к возникшей ситуации: "Неустранимые ошибки. Размер загружаемого файла вероятно, превысила максимально допустимый размер файла (100 МБ), который поддерживает данный сервер." (у меня на серваке ограничение в 100М) (in english: "An unrecoverable error occurred. The uploaded file likely exceeded the maximum file size (@SiZE) that this server supports.")
Резюмируем:
После того, как в любое первое поле загружен файл(файлы), во второе любое поле уже загрузить ничего не возможно анониму через AJAX
Причина понятна и на самом деле давно известна, хоть в данном случае и не сразу очевидна. Пока не проверял в D9, но в D7 давно существует один досадный недостаток. D7 ничего не хочет хранить о пользователе-анониме. Как будто это не один из самых навороченных CMS/CMF, а какой-то статичный HTML. С этой ерундой сталкивался когда лет 8 назад пытался реализовать переключение основной темы на тему для слабовидящих. Без костылей такое переключение для анонимов у меня не работало. То есть, тема переключалась, но после нажатия на любую ссылку и переходе на другую страницу возвращалась основная тема. D7 просто не запоминал какая тема включена у анонима. Естественно, что для любого авторизованного все работало "из коробки".
Продолжаем разбор файловых полей в форме. То сообщение об ошибке, которое я упомянул в п.4. Выдает (что логично) модуль ядра D7 file. А именно функция file_ajax_upload() (что тоже логично). Вот только смысл сообщения вообще не разу не логичен, если посмотреть в каком случае он выскакивает.
Привожу кусок кода этой функции, чтобы читателям не надо было лезть в исходники. Как раз там все в самом начале.
<?php
function file_ajax_upload() {
$form_parents = func_get_args();
$form_build_id = (string) array_pop($form_parents);
// Sanitize form parents before using them.
$form_parents = array_filter($form_parents, 'element_child');
if (empty($_POST['form_build_id']) || $form_build_id != $_POST['form_build_id']) {
// Invalid request.
drupal_set_message(t('An unrecoverable error occurred. The uploaded file likely exceeded the maximum file size (@SiZE) that this server supports.', arr
$commands = array();
$commands[] = ajax_command_replace(NULL, theme('status_messages'));
return array('#type' => 'ajax', '#commands' => $commands);
}
?>Кому лень изучать, объясняю.
В переменную $form_build_id загружается уникальный id существующей формы. Выглядит он примерно вот так: "form-GkKEX24dWY24r3M3cILOPUOvRZUDWlB7I7HAOAgccMY"
И в переменной $_POST['form_build_id'] тоже хранится (передался в POST-запросе) уникальный идентификатор той же самой формы. А теперь сюрприз, сюрприз!!! Для анонима $form_build_id будет заполнен идентификатором, который автоматически сгенерировался при загрузке файла в первое поле, а вот для второго поля он еще не был сгенерирован, а D7-то не помнит нихера (Дед старый! Не помнит нихера! (с) Брат-2) и для второго поля генерирует его заново и естественно совершенно другой - не совпадающий с первым. Далее обе переменные сравниваются и раз они не совпадают - выдается сообщение об ошибке.
Только я не понял, а причем тут размер файла?!?!?!
Теперь вопрос знатокам.
1. Кто, что порекомендует в данной ситуации?
2. Как правильно для этой функции file_ajax_upload() нарисовать ХУК, чтобы перед ее выполнением сделать соответствующие проверки и исправления значений переменных.
P.S. Так, как у меня очень сильно горит по срокам нормальная работа этой функции, то пока буду влезать в ядро и патчить эту конкретную функцию. Код позже выложу сюда в комментариях для обсуждения и возможной корректировки.

Комментарии
Пока решил сделать так. Проверяю пользователя. Если это аноним, то просто закидываю значение из $form_build_id в $_POST['form_build_id'] перед условием проверки на совпадение . Во так:
<?php$form_parents = array_filter($form_parents, 'element_child');
global
$user;$user_id = $user->uid;
if ($user_id == 0 && !empty($_POST['form_build_id']) && !empty($form_build_id))
$_POST['form_build_id'] = $form_build_id;
if (empty(
$_POST['form_build_id']) || $form_build_id != $_POST['form_build_id']) { //... ?>Вроде бы работает. Посмотрю еще в эксплуатации