С чего все началось? С темы которая всех на этом форме мягко говоря удивила: "Прикрепить файлы к статье Joomla". Несмотря на название, само содержимое топика (опуская слово Joomla) было вполне адекватным! А именно поставлена следующая задача.
Задача: Прикрепить к статье файл.
Основные требования:
1. Файл должен загружаться с компьютера автора, в процессе ввода текста. *Хороший пример - этот форум. Тут можно прикрепить файл непосредственно при вводе сообщения.
2. Файл должен быть защищён от скачивания НЕ зарегистрированными пользователями. *не просто скрыта ссылка, а именно защита от скачивания и хотлинков.
Дополнительные (не обязательные, но крайне жалетельные) требования:
1. Если пользователь не загистрирован, то ссылка на файл не исчезает, он просто не может его скачать
2. Если не зарегистрированный пользователь пытается скачать файл - система его уведомляет о необходимости регистрации
3. Счётчик скачивания файла где-нибудь недалеко от ссылки "скачать"
4. Статика в админке, "кто, когда и что" скачал (это было бы вообе шикарно!)
Подопытным для всего этого как не странно стала не Joomla, а Drupal 7.
Сама задача для друпала не хитрая "Прикрепить к статье (материалу) файл" нам позволяет замечательная фишка семерки "Field API" т.е. по сути первый пункт основных требований выполнили. Идем дальше:
Файл должен быть защищён от скачивания НЕ зарегистрированными пользователями. *не просто скрыта ссылка, а именно защита от скачивания и хотлинков.
Это для меня был самый интересный вопрос, поэтому на нем я и заострю внимание.
Для начала нам надо настроить "файловую систему" первым делом надо создать приватную папку, где будут хранится все наши приватные файлы. Я сделал это следующим образом:
Для тех кто не в теме, расшифровываю. Сначала папке /sites/default задал права на запись, создал в ней папку, затем вернул права на "только доступ". поскольку папку /sites/default/files по умолчанию отдана апачу, я решил не создавать приватную папку в ней, чтоб еще не писать правила для запрета апача гулять туда (хотя подозреваю что оповестить его о запрете все равно придется).
После этой процедуры вернемся в админку друпала, а именно идем по адресу admin/config/media/file-system и в настройках "Приватный путь файловой системы" вписываем только что созданную нами папку sites/default/files-private после чего сохраняем настройки.
Далее идем по направлению Структура » Типы содержимого » наш тестовый тип » Управлять полями создаем поле типа "файл" (для изображений аналогично) и в настройках указываем Хранилище Личные файлы сохраняем и смотрим что получилось.
Я минут 5 втыкал что за фигня получилась, вроде как файлы приватные загружаются в одну структуру, отдаются из другой, но отдаются всем подряд. После чего я начал гуглить... и первым делом нагуглил следующее:
Setting the download method to "Private" doesn't have any effect by itself...
what the private download setting does do, is fire off an extra event, hook_download()
Тут кстати автор опЯчататься. На самом деле хук называется hook_file_download
А вообщем это все можно перевести как "Сама по себе приватная файловая система ничего не делает т.е. по сути это api и которая дает один (может и больше, не интересовался) хук с помощью которого мы можем управлять скачиванием этих файлов. После чего я набросал модуль из двух хуков, один создает новое право для ролей hook_permission, а второй тот самый hook_file_download в котором указано что если у пользователя нет права на просмотр приватных файлов слать его лесом! Получившийся модуль искать в прикрепленных файлах.
После активации модуля идем на страницу прав пользователей admin/people/permissions и выдаем новое правило "View private files" кому положено качать приватные файлы т.е. зарегистрированным пользователям. На этом собственно второй пункт выполнен, все анонимы идут лесом.
Теперь идем по дополнительным пунктам:
1. Если пользователь не загистрирован, то ссылка на файл не исчезает, он просто не может его скачать
Уже сделано.
2. Если не зарегистрированный пользователь пытается скачать файл - система его уведомляет о необходимости регистрации
В нашем модуле надо просто $uri прировать не к -1 а к странице регистрации.
3 и 4 пункты решает модуль Download Count вот только портировать под 7-ку еще надо его. А вот это Download Tools для семерки, но сам его не тестил.
Ну в общем-то тема раскрыта, комментируем.
Комментарии
Да что мне так не везет с заливкой файлов на форум...
Умничка, сразу бы так!
Я наверно это все дольше описывал чем делал. Кстати после всего этого у меня возник вопрос, а почему этот функционал в ядро не запихнули (я про код из модуля)? Я б даже сказал в идеале для каждого нового созданного поля с приватным хранилищем ядро должно было создаваться право доступа, чтоб можно было тонко разграничивать приватные файлы между ролями и группами (OG)
Приватными файлами почти не пользуются.
Я скоро обнародую баг в пятёрке и шестёрке, его правда править никто уже не будет
Я вот тоже не представляю где бы мне это пригодилось, но сама идея понравилась, поэтому делал из спортивного интереса
платный контент например, заплатил - получил возможность скачивать. статья интересная, спасибо
Это да, или даже к примеру на не малом корпаративном сайте приватные файлы не помешали бы, да или для той-же социалки и.т.д. и.т.п. Я просто имел введу что в ТЗ еще не видел пункта о приватных файлах.
спс пригодится...
Хе-хе) я попал на главную)
Кстати заметил баг, что при включенном кешировании анонимов не редиректит на страницу "доступ запрещен" в место этого рендрится какая-то страница с иероглифами.
Ну вот, а старуха Шапокляк говорила что хорошими делами прославиться нельзя, выходит врала корга старая.
«После чего я набросал модуль из двух хуков»
А где модуль можно посмотреть, тыкните носом, пожалуйста.
Первый комментарий) не?)
Да, простите. Был крайне невнимателен. Спасибо за модуль.
Можете привести код для примера?
<?php
if (!user_access('view private files')) {
$uri = -1;
}
?>
Что здесь надо поставить?
Решил этот вопрос созданием страницы с инструкцией и ссылкой на регистрацию и указанием ея, как страницу ошибки 403 в admin/config/system/site-information.
Ибо, если править код модуля, то файл становится доступным.
Угу, я в топике описывал эту часть чисто теоретически, когда сам попробовал, заметил тоже самое.
А чем всё решилось в деле счётчика, логирования загрузок?
Download Count до семёрки ещё не дорос.
Siniy ничем, передо мной выше указанные задачи не стояли, я чисто из "спортивного" интереса занялся этими вопросами. Счетчик портировать еще надо, а это не ко мне)
Спасибо тебе, друг.
А как можно использовать данный способ при загрузке файла через фтп вручную? Хочу получить на сайте аналогичную ссылку на файл. (ограничения хостинга)
Разобрался сам.
vitok
Спасибо за помощь, закрыл доступ к скачиванию анонимами, кликает по ссылке попадает на user/register но в строке браузера висит путь system/files/price_list.xls а вверху, над формой регистрации ошибка:
Notice: Undefined index: type в функции metatags_quick_field_access() (строка 119 в файле /home/sozida/public_html/sites/all/modules/metatags_quick/metatags_quick.module).
Может подскажет кто, как устранить? Я почти "0" в этом деле, сижу сам леплю из того что нахожу в сети.
Спасибо
Цитата:
2. Если не зарегистрированный пользователь пытается скачать файл - система его уведомляет о необходимости регистрации
Народ так я и не понял как эту проблему решить
Стесняюсь спросить, а как скачать модуль??
Да не сочтётся за некропостинг, ибо плодить темы-близнецы также неразумно.
Я уж было обрадовался, когда на этот топик набрёл, думал полезного чего найду, да не тут-то было. Аттача след простыл.
Прошу помощи, поскольку нуб.
Дело такое. Необходимо регулировать доступ к приватным файлам не по ролям, а индивидуально по пользователям. Файлы типа "Image", множественно добавляемые в материал.
Для такого дела добавил типу файла поле "field_access" с сылкой на сущность пользователя, со множественным значением. При редактировании материала могу отметить тех пользователей, кому будет доступно скачивание изображения. Администраторам и хозяину файла тоже можно. Остальных надо слать.
Копал интернеты и по своему скудоумию состряпал нечто. Возникла засада. Не умею обратиться и прочитать значение добавленного мною к изображению поля. Знающие люди, подскажите, пожалуйста.
Получившийся код привожу ниже.
<?php function user_file_access_reference_file_download($uri) {
global $user;
foreach ($user->roles as $role) {
if (($role == 'administrator') || ($role == 'editor')) {
return array('Content-Type' => file_get_mimetype($uri));
} elseif (isset( А вот тут засада ->field_access['und'])) {
$qnty_users = count( А вот тут засада ->field_access['und']);
for ( $i = 0; $i < $qnty_users; $i++ ) {
if ( А вот тут засада ->field_access['und'][$i]['target_id'] == $user->uid) {
return array('Content-Type' => file_get_mimetype($uri));
}
}
} else {
return -1;
}
}
} ?>
Модуль есть:
https://www.drupal.org/project/private_files_download_permission
Есть, но он не подходит. Во-первых нельзя назначить права на каждый отдельный файл в поле и модуль сыплет ошибками, если значений поля больше одного. Во-вторых, при использовании uLogin вместо имён он отображает идентификаторы, а в-третьих, в моём случае галочки на разрешение скачивания должен ставить автор материала/хозяин файла.
Дополню, чтоб нагляднее было, что-ли...
При редактировании файла:
В профиле пользователя живёт такая вкладка со списком изображений, доступных ему для скачивания:
По прямым ссылкам должен быть отлуп. Вот чего я хочу добиться.
Я почти добился своего.
<?php function user_file_access_reference_file_download($uri) {
$file = file_load_multiple(array(), array('uri' => $uri));
$field_values = field_get_items('image', $file, 'field_access');
$access_enabled = false;
global $user;
foreach ($user->roles as $role) {
if ($role == 'administrator') {
$access_enabled = true;
break;
}
}
// if ($field_values['uid'] == $user->uid) {
// $access_enabled = true;
// }
if ($access_enabled == false) {
$qnty_users = count($field_values['field_access']['und']);
for ( $i = 0; $i < $qnty_users; $i++ ) {
if ($field_values['field_access']['und'][$i]['target_id'] == $user->uid) {
$access_enabled = true;
break;
}
}
}
if ($access_enabled) {
// Access to the file is success.
return array('Content-Type' => file_get_mimetype($uri));
} else {
// Access to the file is denied.
return -1;
}
} ?>
Работает, но я не догоняю один момент. Стоит раскомментить строки
<?php
if ($field_values['uid'] == $user->uid) {
$access_enabled = true;
}
?>
и прямые ссылки начинают открывать изображения кому попало. При этом хозяин картинки один чёрт её не может в оригинале просмотреть. В чём косяк?
Продолжаю свою борьбу, успехи так себе.
Текущий тестовый вариант:
<?php /* Implements hook_file_download()
* administrator - roles have access
* field_access - entityreference field contains users that have access
*/ function check_accsess($uri) {
$access_enabled = false;
$files = file_load_multiple(array(), array('uri' => $uri));
// watchdog('MyModule', '<pre>'. print_r($files, TRUE) .'</pre>', array(), WATCHDOG_INFO, NULL);
reset($files);
$file = current($files);
unset($files);
// watchdog('MyModule', '<pre>'. print_r($file, TRUE) .'</pre>', array(), WATCHDOG_INFO, NULL);
if (isset($file->field_access)) {
$access_values = $file->field_access;
} else {
watchdog('MyModule', '<pre>'. print_r('Не файл!', TRUE) .'</pre>', array(), WATCHDOG_INFO, NULL);
$access_enabled = true;
return $access_enabled;
}
// watchdog('MyModule', '<pre>'. print_r($access_values, TRUE) .'</pre>', array(), WATCHDOG_INFO, NULL);
global $user;
foreach ($user->roles as $role) {
if ($role == 'administrator') {
watchdog('MyModule', '<pre>'. print_r('Права доступа как Администратору', TRUE) .'</pre>', array(), WATCHDOG_INFO, NULL);
$access_enabled = true;
return true;
break;
}
}
// watchdog('MyModule', '<pre>'. print_r('$file->uid', TRUE) .'</pre>', array(), WATCHDOG_INFO, NULL);
if ($access_enabled == false) {
if ($file->uid == $user->uid) {
watchdog('MyModule', '<pre>'. print_r('Права доступа как владельцу', TRUE) .'</pre>', array(), WATCHDOG_INFO, NULL);
$access_enabled = true;
return true;
}
}
if ($access_enabled == false) {
watchdog('MyModule', '<pre>'. print_r('Проверка прав доступа по списку пользователей', TRUE) .'</pre>', array(), WATCHDOG_INFO, NULL);
foreach($access_values['und'] as $key_arr => $val_arr){
// watchdog('MyModule', '<pre>'. print_r($val_arr['target_id'], TRUE) .'</pre>', array(), WATCHDOG_INFO, NULL);
if ($val_arr['target_id'] == $user->uid) {
watchdog('MyModule', '<pre>'. print_r('Права доступа предоставлены по списку пользователей', TRUE) .'</pre>', array(), WATCHDOG_INFO, NULL);
$access_enabled = true;
return true;
break;
}
}
}
return $access_enabled;
}
function user_file_access_reference_file_download($uri) {
if (check_accsess($uri)) {
// Access to the file is success.
return array('Content-Type' => file_get_mimetype($uri));
} else {
// Access to the file is denied.
watchdog('MyModule', '<pre>'. print_r('Доступ запрещён', TRUE) .'</pre>', array(), WATCHDOG_INFO, NULL);
return -1;
}
}
?>
Текущие проблемы:
несмотря на предварительную проверку. Да и проверку вставлял только из-за того, что массив $files периодически оказывается пустым.
Мозги вскипают. Может, подскажет кто, как быть?
Немного подпилил код:
<?php /* Implements hook_file_download()
* administrator - roles have access
* field_access - entityreference field contains users that have access
*/ function check_accsess($uri) {
$access_enabled = false;
watchdog('MyModule', '<pre>'. print_r($uri, TRUE) .'</pre>', array(), WATCHDOG_INFO, NULL);
$uri_check = explode('/', (string)$uri);
if (array_search('styles', $uri_check)) {
watchdog('MyModule', '<pre>'. print_r('Просмотр превьюшки разрешен', TRUE) .'</pre>', array(), WATCHDOG_INFO, NULL);
$access_enabled = true; // Просмотр превьюшки разрешен
return $access_enabled;
}
if ($access_enabled == false) {
global $user;
foreach ($user->roles as $role) {
if ($role == 'administrator') {
watchdog('MyModule', '<pre>'. print_r('Разрешить для роли администратора', TRUE) .'</pre>', array(), WATCHDOG_INFO, NULL);
$access_enabled = true; // Разрешить для роли администратора
return $access_enabled;
break;
}
}
}
if ($access_enabled == false) {
$files = file_load_multiple(array(), array('uri' => $uri));
reset($files);
$file = current($files);
unset($files);
}
if (isset($file->uid)) {
if ($file->uid == $user->uid) {
watchdog('MyModule', '<pre>'. print_r('Права доступа как владельцу', TRUE) .'</pre>', array(), WATCHDOG_INFO, NULL);
$access_enabled = true; // Права доступа предоставлены как владельцу
return $access_enabled;
}
}
if ($access_enabled == false) {
if (isset($file->field_access)) {
watchdog('MyModule', '<pre>'. print_r('Проверка прав доступа по списку пользователей', TRUE) .'</pre>', array(), WATCHDOG_INFO, NULL);
$access_values = $file->field_access;
foreach($access_values['und'] as $key_arr => $val_arr){
if ($val_arr['target_id'] == $user->uid) {
watchdog('MyModule', '<pre>'. print_r('Права доступа предоставлены по списку пользователей', TRUE) .'</pre>', array(), WATCHDOG_INFO, NULL);
$access_enabled = true; // Права доступа предоставлены по списку пользователей
return $access_enabled;
break;
}
}
}
}
return $access_enabled;
}
function user_file_access_reference_file_download($uri) {
if (check_accsess($uri)) {
// Access to the file is success.
return array('Content-Type' => file_get_mimetype($uri));
} else {
// Access to the file is denied.
watchdog('MyModule', '<pre>'. print_r('Доступ запрещён', TRUE) .'</pre>', array(), WATCHDOG_INFO, NULL);
return -1;
}
}
?>
Выяснилось, что при отображении картинки не в оригинале, а в каком-либо стиле, хук дёргается дважды. Посмотреть превьюшку я как бы разрешаю, но при этом на сам оригинальный файл мне приходится возвращать запрет. Ну пришёл он в uri. По каким признакам мне его проигнорировать? И этот запрет не даёт посмотреть и превьюшку.
Как решить такую завихруху? В отчётах журнала чётко видно, что пользователь тянет картинку из стилей, но на хук цепляется один раз uri превьюшки и один раз uri оригинальной картинки.
Помогите разобраться, пожалуйста.
Решение для отдачи превьюшек нашёл. Оказалось просто, сложным был путь к простому решению.